TDataSeries<T>

This commit is contained in:
Michael Schimmel
2025-06-05 14:36:05 +02:00
parent 6bed68748d
commit f8c3ffceb8
14 changed files with 1051 additions and 517 deletions
+83 -83
View File
@@ -99,15 +99,15 @@ begin
LInitStateAsState := TState.Null;
LFuture :=
TMycGateFuncFuture<Integer>.Create(
FTaskFactory,
LInitStateAsState,
function: Integer
begin
Inc(Self.FProcExecutionCount);
Result := CExpectedResult;
end
);
TMycGateFuncFuture<Integer>.Create(
FTaskFactory,
LInitStateAsState,
function: Integer
begin
Inc(Self.FProcExecutionCount);
Result := CExpectedResult;
end
);
FTaskFactory.WaitFor(LFuture.Done); // Accesses IMycFuture.GetDone [cite: 110, 234, 352]
@@ -129,15 +129,15 @@ begin
LInitLatch := TLatch.Construct(1); // Create an init state that is not yet set [cite: 77, 212, 330]
LFuture :=
TMycGateFuncFuture<string>.Create(
FTaskFactory,
LInitLatch.State,
function: string
begin
Inc(Self.FProcExecutionCount);
Result := CExpectedResult;
end
);
TMycGateFuncFuture<string>.Create(
FTaskFactory,
LInitLatch.State,
function: string
begin
Inc(Self.FProcExecutionCount);
Result := CExpectedResult;
end
);
Assert.AreEqual(0, FProcExecutionCount, 'AProc should not have executed yet.');
Assert.IsFalse(LFuture.Done.IsSet, 'Future should not be done yet.');
@@ -168,15 +168,15 @@ begin
LInitStateAsState := TState.Null; // Immediate execution
LFuture :=
TMycGateFuncFuture<Integer>.Create(
LLocalTaskFactory,
LInitStateAsState,
function: Integer
begin
Inc(Self.FProcExecutionCount);
raise Exception.Create(CExceptionMessage);
end
);
TMycGateFuncFuture<Integer>.Create(
LLocalTaskFactory,
LInitStateAsState,
function: Integer
begin
Inc(Self.FProcExecutionCount);
raise Exception.Create(CExceptionMessage);
end
);
try
LLocalTaskFactory.WaitFor(LFuture.Done);
@@ -225,15 +225,15 @@ begin
LInitLatch := TLatch.Construct(1);
LFuture :=
TMycGateFuncFuture<Integer>.Create(
FTaskFactory,
LInitLatch.State,
function: Integer
begin
Inc(Self.FProcExecutionCount);
Result := 123;
end
);
TMycGateFuncFuture<Integer>.Create(
FTaskFactory,
LInitLatch.State,
function: Integer
begin
Inc(Self.FProcExecutionCount);
Result := 123;
end
);
Assert.IsFalse(LFuture.Done.IsSet, 'Future should not be marked as done initially.');
@@ -264,44 +264,44 @@ begin
// Create MainFuture, AInitState is the GateLatch's state.
LMainFuture :=
TMycGateFuncFuture<string>.Create(
FTaskFactory,
LGateLatch.State,
function: string
begin
Inc(Self.FProcExecutionCount, 10); // Indicate MainFuture's proc ran
Result := CMainFutureResult;
end
);
TMycGateFuncFuture<string>.Create(
FTaskFactory,
LGateLatch.State,
function: string
begin
Inc(Self.FProcExecutionCount, 10); // Indicate MainFuture's proc ran
Result := CMainFutureResult;
end
);
// Setup Prerequisite Futures
// PrerequisiteFuture1
LInitStateP1 := TLatch.Construct(1); // Controllable init state for PF1
LPrerequisiteFuture1 :=
TMycGateFuncFuture<Integer>.Create(
FTaskFactory,
LInitStateP1.State,
function: Integer
begin
Inc(Self.FProcExecutionCount, 1); // PF1 ran
Result := 1;
end
);
TMycGateFuncFuture<Integer>.Create(
FTaskFactory,
LInitStateP1.State,
function: Integer
begin
Inc(Self.FProcExecutionCount, 1); // PF1 ran
Result := 1;
end
);
LSub1 := TLatchNotifierSubscriber.Create(LGateLatch);
Subscriptions[1] := LPrerequisiteFuture1.Done.Subscribe(LSub1); // Subscribe to PF1's completion [cite: 56, 188, 306]
// PrerequisiteFuture2
LInitStateP2 := TLatch.Construct(1); // Controllable init state for PF2
LPrerequisiteFuture2 :=
TMycGateFuncFuture<Integer>.Create(
FTaskFactory,
LInitStateP2.State,
function: Integer
begin
Inc(Self.FProcExecutionCount, 1); // PF2 ran
Result := 2;
end
);
TMycGateFuncFuture<Integer>.Create(
FTaskFactory,
LInitStateP2.State,
function: Integer
begin
Inc(Self.FProcExecutionCount, 1); // PF2 ran
Result := 2;
end
);
LSub2 := TLatchNotifierSubscriber.Create(LGateLatch);
Subscriptions[2] := LPrerequisiteFuture2.Done.Subscribe(LSub2); // Subscribe to PF2's completion
@@ -347,29 +347,29 @@ begin
// Future A
LFutureA :=
TMycGateFuncFuture<Integer>.Create(
FTaskFactory,
LTriggerLatch.State,
function: Integer
begin
LFlagFutureARan := True;
System.SyncObjs.TInterlocked.Increment(Self.FSharedCounter); // Thread-safe increment
Result := 100;
end
);
TMycGateFuncFuture<Integer>.Create(
FTaskFactory,
LTriggerLatch.State,
function: Integer
begin
LFlagFutureARan := True;
System.SyncObjs.TInterlocked.Increment(Self.FSharedCounter); // Thread-safe increment
Result := 100;
end
);
// Future B
LFutureB :=
TMycGateFuncFuture<Integer>.Create(
FTaskFactory,
LTriggerLatch.State,
function: Integer
begin
LFlagFutureBRan := True;
System.SyncObjs.TInterlocked.Increment(Self.FSharedCounter); // Thread-safe increment
Result := 200;
end
);
TMycGateFuncFuture<Integer>.Create(
FTaskFactory,
LTriggerLatch.State,
function: Integer
begin
LFlagFutureBRan := True;
System.SyncObjs.TInterlocked.Increment(Self.FSharedCounter); // Thread-safe increment
Result := 200;
end
);
Assert.IsFalse(LFutureA.Done.IsSet, 'FutureA should not be done yet.');
Assert.IsFalse(LFutureB.Done.IsSet, 'FutureB should not be done yet.');
+158 -158
View File
@@ -75,13 +75,13 @@ var
begin
// Test construction with a simple function that returns an Integer
fut :=
TFuture<Integer>.Construct( // [cite: 146]
function: Integer
begin
TThread.Sleep(20); // Simulate some background work
Result := 42;
end
);
TFuture<Integer>.Construct( // [cite: 146]
function: Integer
begin
TThread.Sleep(20); // Simulate some background work
Result := 42;
end
);
Assert.IsNotNull(fut.Done, 'Future.Done property should not be nil after construction.'); // Static string [cite: 148]
fut.WaitFor(); // Wait for the future to complete its execution [cite: 148]
@@ -99,14 +99,14 @@ var
begin
// Test construction with a nil gate, which should execute the task immediately
fut :=
TFuture<Integer>.Construct(
nil, // Explicitly providing a nil gate [cite: 147]
function: Integer
begin
TThread.Sleep(20); // Simulate work
Result := 43;
end
);
TFuture<Integer>.Construct(
nil, // Explicitly providing a nil gate [cite: 147]
function: Integer
begin
TThread.Sleep(20); // Simulate work
Result := 43;
end
);
Assert.IsNotNull(fut.Done, 'Future.Done should not be nil when constructed with a nil gate.'); // Static string [cite: 148]
fut.WaitFor(); // [cite: 148]
@@ -128,13 +128,13 @@ begin
// Construct a future with a gate that is already set
fut :=
TFuture<Integer>.Construct(
presetGate, // [cite: 147]
function: Integer
begin
Result := 44; // This should execute quickly
end
);
TFuture<Integer>.Construct(
presetGate, // [cite: 147]
function: Integer
begin
Result := 44; // This should execute quickly
end
);
Assert.IsNotNull(fut.Done, 'Future.Done should not be nil for preset gate construct.'); // Static string [cite: 148]
fut.WaitFor(); // [cite: 148]
@@ -157,10 +157,10 @@ begin
// Construct a future with a gate that is not yet set
fut :=
TFuture<Integer>.Construct(
delayedGate.State, // Get the IMycState interface from the latch [cite: 147, 59]
function: Integer begin Result := 45; end
);
TFuture<Integer>.Construct(
delayedGate.State, // Get the IMycState interface from the latch [cite: 147, 59]
function: Integer begin Result := 45; end
);
Assert.IsNotNull(fut.Done, 'Future.Done should not be nil for delayed gate construct.'); // Static string [cite: 148]
@@ -189,23 +189,23 @@ var
begin
// Create an initial future
fut1 :=
TFuture<Integer>.Construct( // [cite: 146]
function: Integer
begin
TThread.Sleep(20); // Simulate work
Result := 100;
end
);
TFuture<Integer>.Construct( // [cite: 146]
function: Integer
begin
TThread.Sleep(20); // Simulate work
Result := 100;
end
);
// Chain a second future that depends on the result of the first
fut2 :=
fut1.Chain<string>( // [cite: 147]
function(Input: Integer): string // This function receives the result of fut1
begin
TThread.Sleep(20); // Simulate further work
Result := 'Value: ' + Input.ToString; // Use ToString for converting Integer to String
end
);
fut1.Chain<string>( // [cite: 147]
function(Input: Integer): string // This function receives the result of fut1
begin
TThread.Sleep(20); // Simulate further work
Result := 'Value: ' + Input.ToString; // Use ToString for converting Integer to String
end
);
Assert.IsNotNull(fut2.Done, 'Chained Future.Done should not be nil.'); // Static string [cite: 148]
fut2.WaitFor(); // Wait for the chained future to complete [cite: 148]
@@ -229,16 +229,16 @@ begin
// First future depends on the delayedGate
fut1 :=
TFuture<Integer>.Construct(
delayedGate.State, // [cite: 147, 59]
function: Integer begin Result := 200; end
);
TFuture<Integer>.Construct(
delayedGate.State, // [cite: 147, 59]
function: Integer begin Result := 200; end
);
// Second future is chained to the first
fut2 :=
fut1.Chain<string>( // [cite: 147]
function(Input: Integer): string begin Result := 'ChainVal: ' + Input.ToString; end
);
fut1.Chain<string>( // [cite: 147]
function(Input: Integer): string begin Result := 'ChainVal: ' + Input.ToString; end
);
Assert.IsNotNull(fut2.Done, 'Chained (with gate) Future.Done should not be nil.'); // Static string [cite: 148]
@@ -268,33 +268,33 @@ var
begin
// Initial future A
futA :=
TFuture<Integer>.Construct( // [cite: 146]
function: Integer
begin
TThread.Sleep(10);
Result := 10;
end
);
TFuture<Integer>.Construct( // [cite: 146]
function: Integer
begin
TThread.Sleep(10);
Result := 10;
end
);
// Future B, chained from A
futB :=
futA.Chain<Real>( // [cite: 147]
function(InputA: Integer): Real
begin
TThread.Sleep(10);
Result := InputA * 2.5; // Calculation: 10 * 2.5 = 25.0
end
);
futA.Chain<Real>( // [cite: 147]
function(InputA: Integer): Real
begin
TThread.Sleep(10);
Result := InputA * 2.5; // Calculation: 10 * 2.5 = 25.0
end
);
// Future C, chained from B
futC :=
futB.Chain<string>( // [cite: 147]
function(InputB: Real): string
begin
TThread.Sleep(10);
Result := 'Final: ' + FloatToStr(InputB); // Convert Real to String
end
);
futB.Chain<string>( // [cite: 147]
function(InputB: Real): string
begin
TThread.Sleep(10);
Result := 'Final: ' + FloatToStr(InputB); // Convert Real to String
end
);
Assert.IsNotNull(futC.Done, 'Multi-chained Future.Done should not be nil.'); // Static string [cite: 148]
futC.WaitFor(); // Wait for the last future in the chain [cite: 148]
@@ -318,13 +318,13 @@ var
begin
// Parametric test for Future<string>
fut :=
TFuture<string>.Construct( // [cite: 146]
function: string
begin
TThread.Sleep(10);
Result := ParamValue; // Use the parameter in the future's function
end
);
TFuture<string>.Construct( // [cite: 146]
function: string
begin
TThread.Sleep(10);
Result := ParamValue; // Use the parameter in the future's function
end
);
Assert.IsNotNull(fut.Done, 'Parametric Future.Done should not be nil.'); // Static string [cite: 148]
fut.WaitFor(); // [cite: 148]
@@ -354,22 +354,22 @@ begin
begin
// Capture loop variable for use in anonymous method
Futures[i] :=
TFuture<Integer>.Construct( // [cite: 146]
(
function(captureIndex: Integer): TFunc<Integer>
begin
Result :=
function: Integer
var
delay: Integer;
begin
delay := 10 + Random(40); // Random delay between 10ms and 49ms
TThread.Sleep(delay);
Result := captureIndex; // Return the captured index
end;
end
)(i)
);
TFuture<Integer>.Construct( // [cite: 146]
(
function(captureIndex: Integer): TFunc<Integer>
begin
Result :=
function: Integer
var
delay: Integer;
begin
delay := 10 + Random(40); // Random delay between 10ms and 49ms
TThread.Sleep(delay);
Result := captureIndex; // Return the captured index
end;
end
)(i)
);
doneStates[i] := Futures[i].Done; // [cite: 148]
end;
@@ -378,13 +378,13 @@ begin
// Create a master future that waits on the combined State.All state
masterFuture :=
TFuture<Boolean>.Construct(
combinedStateAll, // [cite: 147]
function: Boolean
begin
Result := True; // This function executes when combinedStateAll is set
end
);
TFuture<Boolean>.Construct(
combinedStateAll, // [cite: 147]
function: Boolean
begin
Result := True; // This function executes when combinedStateAll is set
end
);
masterFuture.WaitFor(); // Wait for all futures to complete via the master future [cite: 148]
Assert.IsTrue(combinedStateAll.IsSet, 'Combined State.All should be set after masterFuture.WaitFor().'); // Static string [cite: 55]
@@ -424,25 +424,25 @@ begin
begin
// Capture loop variable
Futures[i] :=
TFuture<Integer>.Construct( // [cite: 146]
(
function(captureIndex: Integer): TFunc<Integer>
begin
Result :=
function: Integer
var
delay: Integer;
begin
if captureIndex = QuickFutureIndex then
delay := 5 // Short delay for the 'quick' future
else
delay := 50 + Random(100); // Longer random delay (50-149ms) for others
TThread.Sleep(delay);
Result := captureIndex;
end;
end
)(i)
);
TFuture<Integer>.Construct( // [cite: 146]
(
function(captureIndex: Integer): TFunc<Integer>
begin
Result :=
function: Integer
var
delay: Integer;
begin
if captureIndex = QuickFutureIndex then
delay := 5 // Short delay for the 'quick' future
else
delay := 50 + Random(100); // Longer random delay (50-149ms) for others
TThread.Sleep(delay);
Result := captureIndex;
end;
end
)(i)
);
doneStates[i] := Futures[i].Done; // [cite: 148]
end;
@@ -451,13 +451,13 @@ begin
// Create a master future that waits on the combined State.Any state
masterFuture :=
TFuture<Boolean>.Construct(
combinedStateAny, // [cite: 147]
function: Boolean
begin
Result := True; // This function executes when combinedStateAny is set
end
);
TFuture<Boolean>.Construct(
combinedStateAny, // [cite: 147]
function: Boolean
begin
Result := True; // This function executes when combinedStateAny is set
end
);
masterFuture.WaitFor(); // Wait for at least one future to complete [cite: 148]
Assert.IsTrue(combinedStateAny.IsSet, 'Combined State.Any should be set after masterFuture.WaitFor().'); // Static string [cite: 55]
@@ -492,20 +492,20 @@ var
begin
// Create an outer future that, when resolved, produces another (inner) future.
outerFuture :=
TFuture<TFuture<Integer>>.Construct( // [cite: 146]
function: TFuture<Integer> // This lambda returns a Future<Integer>
begin
TThread.Sleep(10); // Simulate work for the outer future to produce the inner one
Result :=
TFuture<Integer>.Construct( // [cite: 146]
function: Integer
begin
TThread.Sleep(10); // Simulate work for the inner future
Result := 123; // The final value
end
);
end
TFuture<TFuture<Integer>>.Construct( // [cite: 146]
function: TFuture<Integer> // This lambda returns a Future<Integer>
begin
TThread.Sleep(10); // Simulate work for the outer future to produce the inner one
Result :=
TFuture<Integer>.Construct( // [cite: 146]
function: Integer
begin
TThread.Sleep(10); // Simulate work for the inner future
Result := 123; // The final value
end
);
end
);
Assert.IsNotNull(outerFuture.Done, 'Outer future''s Done state should not be nil.'); // Static string [cite: 148]
outerFuture.WaitFor(); // Wait for the outer future to complete and yield the inner future [cite: 148]
@@ -531,31 +531,31 @@ var
begin
// Create an initial future
initialFuture :=
TFuture<Integer>.Construct( // [cite: 146]
function: Integer
begin
TThread.Sleep(10);
Result := 77;
end
);
TFuture<Integer>.Construct( // [cite: 146]
function: Integer
begin
TThread.Sleep(10);
Result := 77;
end
);
// Chain it with a function that itself returns a new Future<string>
outerChainedFuture :=
initialFuture.Chain<TFuture<string>>( // [cite: 147]
function(Input: Integer): TFuture<string> // This lambda returns a Future<string>
begin
TThread.Sleep(10); // Simulate work in the chain function
// Input is the result of initialFuture (77)
Result :=
TFuture<string>.Construct( // [cite: 146]
function: string
begin
TThread.Sleep(10); // Simulate work for the inner-most future
Result := 'Value: ' + Input.ToString; // Input is captured (77)
end
);
end
initialFuture.Chain<TFuture<string>>( // [cite: 147]
function(Input: Integer): TFuture<string> // This lambda returns a Future<string>
begin
TThread.Sleep(10); // Simulate work in the chain function
// Input is the result of initialFuture (77)
Result :=
TFuture<string>.Construct( // [cite: 146]
function: string
begin
TThread.Sleep(10); // Simulate work for the inner-most future
Result := 'Value: ' + Input.ToString; // Input is captured (77)
end
);
end
);
Assert.IsNotNull(outerChainedFuture.Done, 'Outer chained future''s Done state should not be nil.'); // Static string [cite: 148]
outerChainedFuture.WaitFor(); // Wait for initialFuture to complete AND the chain function to execute [cite: 148]
+17 -17
View File
@@ -106,11 +106,11 @@ var
begin
predicateCallCount := 0;
dummyPredicate :=
function(Item: IMyTestInterface): Boolean
begin
Inc(predicateCallCount);
Result := True;
end;
function(Item: IMyTestInterface): Boolean
begin
Inc(predicateCallCount);
Result := True;
end;
FNotifier.Lock;
Assert.IsTrue(FNotifier.IsLocked, 'Notifier should be locked.');
@@ -209,12 +209,12 @@ begin
itemsProcessedCount := 0;
FNotifier.Notify(
function(Item: IMyTestInterface): Boolean
begin
Inc(itemsProcessedCount);
TMyTestReceiver(Item).Foo;
Result := Item.GetValue <> 20; // Remove item with value 20
end
function(Item: IMyTestInterface): Boolean
begin
Inc(itemsProcessedCount);
TMyTestReceiver(Item).Foo;
Result := Item.GetValue <> 20; // Remove item with value 20
end
);
Assert.AreEqual(3, itemsProcessedCount, 'Notify predicate called for all 3 items.');
@@ -229,12 +229,12 @@ begin
itemsProcessedCount := 0;
FNotifier.Notify(
function(Item: IMyTestInterface): Boolean
begin
Inc(itemsProcessedCount);
TMyTestReceiver(Item).Foo;
Result := True; // Keep remaining
end
function(Item: IMyTestInterface): Boolean
begin
Inc(itemsProcessedCount);
TMyTestReceiver(Item).Foo;
Result := True; // Keep remaining
end
);
Assert.AreEqual(2, itemsProcessedCount, 'Notify predicate called for 2 remaining items.');
Assert.AreEqual(1, rcv1.CallCount, 'Receiver1 called again.');
+27 -32
View File
@@ -213,11 +213,11 @@ begin
// if not FOwnerFixture.FNotifier.IsFinalized then // Don't notify if finalized
begin
FOwnerFixture.FNotifier.Notify(
function(Item: IMyStressTestInterface): Boolean
begin
Item.Foo(FThreadID); // Pass ThreadID as notification type for context
Result := True; // Keep item
end
function(Item: IMyStressTestInterface): Boolean
begin
Item.Foo(FThreadID); // Pass ThreadID as notification type for context
Result := True; // Keep item
end
);
end;
finally
@@ -359,11 +359,11 @@ begin
// if not FNotifier.IsFinalized then
begin
FNotifier.Notify(
function(Item: IMyStressTestInterface): Boolean
begin
actualLiveReceiversInNotifier.Add(Item);
Result := True;
end
function(Item: IMyStressTestInterface): Boolean
begin
actualLiveReceiversInNotifier.Add(Item);
Result := True;
end
);
end;
finally
@@ -373,12 +373,12 @@ begin
// 2. Compare overall counts
Assert.AreEqual(
totalExpectedLiveByThreadsAtEnd,
actualLiveInNotifierAtEnd,
Format(
'Mismatch in live item count at end. Threads expected %d, Notifier has %d.',
[totalExpectedLiveByThreadsAtEnd, actualLiveInNotifierAtEnd]
)
totalExpectedLiveByThreadsAtEnd,
actualLiveInNotifierAtEnd,
Format(
'Mismatch in live item count at end. Threads expected %d, Notifier has %d.',
[totalExpectedLiveByThreadsAtEnd, actualLiveInNotifierAtEnd]
)
);
// 3. Detailed check: Iterate all receivers ever created.
@@ -400,27 +400,22 @@ begin
end;
Assert.AreEqual(
receiver.ExpectedToBeAdvised,
found,
Format(
'Receiver ID %d: Thread expected it to be advised=%d, but its presence in Notifier is %d. NotificationCount=%d',
[
receiver.GetInstanceID,
Integer(receiver.ExpectedToBeAdvised),
Integer(found),
receiver.GetNotificationCount
]
)
receiver.ExpectedToBeAdvised,
found,
Format(
'Receiver ID %d: Thread expected it to be advised=%d, but its presence in Notifier is %d. NotificationCount=%d',
[receiver.GetInstanceID, Integer(receiver.ExpectedToBeAdvised), Integer(found), receiver.GetNotificationCount]
)
);
// Further checks on NotificationCount could be added if specific notification patterns were expected.
// For this chaos test, ensuring count is non-negative and consistent with advised state is a good start.
Assert.IsTrue(
receiver.GetNotificationCount >= 0,
Format(
'Receiver ID %d has non-positive notification count: %d',
[receiver.GetInstanceID, receiver.GetNotificationCount]
)
receiver.GetNotificationCount >= 0,
Format(
'Receiver ID %d has non-positive notification count: %d',
[receiver.GetInstanceID, receiver.GetNotificationCount]
)
);
if found and (receiver.GetNotificationCount = 0) then
begin
+20 -20
View File
@@ -256,23 +256,23 @@ begin
FNotifier.Lock;
try
FNotifier.Notify(
function(Item: IMyTestInterface): Boolean
begin
Inc(currentNotifyCount);
Result := True; // Keep item in the Notifier
end
function(Item: IMyTestInterface): Boolean
begin
Inc(currentNotifyCount);
Result := True; // Keep item in the Notifier
end
);
finally
FNotifier.Release;
end;
Assert.AreEqual(
totalAdvisedExpected,
currentNotifyCount,
'The number of items in the Notifier after all Advise operations ('
+ currentNotifyCount.ToString
+ ') does not match the expected number ('
+ totalAdvisedExpected.ToString
+ ').'
totalAdvisedExpected,
currentNotifyCount,
'The number of items in the Notifier after all Advise operations ('
+ currentNotifyCount.ToString
+ ') does not match the expected number ('
+ totalAdvisedExpected.ToString
+ ').'
);
// 2. Execute UnadviseAll
@@ -288,19 +288,19 @@ begin
FNotifier.Lock;
try
FNotifier.Notify(
function(Item: IMyTestInterface): Boolean
begin
Inc(currentNotifyCount);
Result := True;
end
function(Item: IMyTestInterface): Boolean
begin
Inc(currentNotifyCount);
Result := True;
end
);
finally
FNotifier.Release;
end;
Assert.AreEqual(
0,
currentNotifyCount,
'The Notifier should be empty after UnadviseAll, but still contains ' + currentNotifyCount.ToString + ' items.'
0,
currentNotifyCount,
'The Notifier should be empty after UnadviseAll, but still contains ' + currentNotifyCount.ToString + ' items.'
);
end;
+11 -14
View File
@@ -129,9 +129,9 @@ begin
end;
procedure TTestMycLatch.TestNotify_ReturnValueAndState_AfterOneNotify(
InitialCount: Integer;
ExpectedReturn: Boolean;
ExpectedIsSet: Boolean
InitialCount: Integer;
ExpectedReturn: Boolean;
ExpectedIsSet: Boolean
);
var
latch: IMycLatch;
@@ -141,14 +141,14 @@ begin
returnedValueFromNotify := latch.Notify; // This is IMycLatch (as IMycSubscriber).Notify
Assert.AreEqual(
ExpectedReturn,
returnedValueFromNotify,
'Unexpected return value from Latch.Notify call for initial count ' + IntToStr(InitialCount) + '.'
ExpectedReturn,
returnedValueFromNotify,
'Unexpected return value from Latch.Notify call for initial count ' + IntToStr(InitialCount) + '.'
);
Assert.AreEqual(
ExpectedIsSet,
latch.State.IsSet,
'Unexpected IsSet state after Latch.Notify call for initial count ' + IntToStr(InitialCount) + '.'
ExpectedIsSet,
latch.State.IsSet,
'Unexpected IsSet state after Latch.Notify call for initial count ' + IntToStr(InitialCount) + '.'
);
end;
@@ -504,11 +504,8 @@ begin
Assert.AreEqual(1, mockSubInitial.NotifyCount, 'Initial subscriber (unadvised) not notified again.');
// mockSubNew should also not be notified again, as it was only notified immediately
// and not persistently added to FSubscribers for subsequent Latch.Notify calls.
Assert.AreEqual(
1,
mockSubNew.NotifyCount,
'New subscriber (notified once on subscribe) not notified by subsequent Latch.Notify calls.'
);
Assert
.AreEqual(1, mockSubNew.NotifyCount, 'New subscriber (notified once on subscribe) not notified by subsequent Latch.Notify calls.');
end;
initialization
+46 -46
View File
@@ -92,13 +92,13 @@ begin
jobCompletedLatch := TLatch.Construct(1); // Changed from Signals.CreateLatch
FFactory
.Run(
procedure
begin
jobExecuted := True;
jobCompletedLatch.Notify;
end)
.Notify;
.Run(
procedure
begin
jobExecuted := True;
jobCompletedLatch.Notify;
end)
.Notify;
FFactory.WaitFor(jobCompletedLatch.State);
Assert.IsTrue(jobExecuted, 'Immediate job should have executed');
@@ -114,13 +114,13 @@ begin
jobCompletedLatch := TLatch.Construct(1); // Changed from Signals.CreateLatch
subscriber :=
FFactory.Run(
procedure
begin
jobExecuted := True;
jobCompletedLatch.Notify;
end
);
FFactory.Run(
procedure
begin
jobExecuted := True;
jobCompletedLatch.Notify;
end
);
Assert.IsNotNull(subscriber, 'Run should return a subscriber for delayed job');
// Use TMycLatch.Null for comparison
@@ -162,12 +162,12 @@ begin
Assert.IsFalse(FFactory.InWorkerThread, 'Test method itself should not be in a factory worker thread');
FFactory.EnqueueJob(
procedure
begin
inMainThreadInJob := FFactory.InMainThread;
inWorkerThreadInJob := FFactory.InWorkerThread;
jobDoneLatch.Notify;
end
procedure
begin
inMainThreadInJob := FFactory.InMainThread;
inWorkerThreadInJob := FFactory.InWorkerThread;
jobDoneLatch.Notify;
end
);
FFactory.WaitFor(jobDoneLatch.State);
@@ -183,20 +183,20 @@ begin
jobDoneLatch := TLatch.Construct(1);
FFactory.EnqueueJob(
procedure
begin
try
raise TMycTaskFactory.ETaskException.Create('Test Exception From Job');
finally
jobDoneLatch.Notify;
end;
end
procedure
begin
try
raise TMycTaskFactory.ETaskException.Create('Test Exception From Job');
finally
jobDoneLatch.Notify;
end;
end
);
Assert.WillRaise(
procedure begin FFactory.WaitFor(jobDoneLatch.State); end,
TMycTaskFactory.ETaskException,
'WaitFor should re-raise the exception from the job'
procedure begin FFactory.WaitFor(jobDoneLatch.State); end,
TMycTaskFactory.ETaskException,
'WaitFor should re-raise the exception from the job'
);
var noExceptionRaised: Boolean := True;
@@ -213,9 +213,9 @@ begin
FFactory.Teardown;
Assert.WillRaise(
procedure begin FFactory.EnqueueJob(procedure begin end); end,
TMycTaskFactory.ETaskException,
'Running a job on a torn-down factory should raise ETaskException'
procedure begin FFactory.EnqueueJob(procedure begin end); end,
TMycTaskFactory.ETaskException,
'Running a job on a torn-down factory should raise ETaskException'
);
end;
@@ -230,18 +230,18 @@ begin
dummyStateToWaitFor := TLatch.Construct(1).State;
FFactory.EnqueueJob(
procedure
begin
try
FFactory.WaitFor(dummyStateToWaitFor);
except
on E: TMycTaskFactory.ETaskException do
begin
exceptionCaughtInJob := True;
end;
end;
jobDoneLatch.Notify;
end
procedure
begin
try
FFactory.WaitFor(dummyStateToWaitFor);
except
on E: TMycTaskFactory.ETaskException do
begin
exceptionCaughtInJob := True;
end;
end;
jobDoneLatch.Notify;
end
);
FFactory.WaitFor(jobDoneLatch.State);