254 lines
7.2 KiB
ObjectPascal
254 lines
7.2 KiB
ObjectPascal
unit TestTasks;
|
|
|
|
interface
|
|
|
|
uses
|
|
DUnitX.TestFramework,
|
|
System.SysUtils,
|
|
System.Classes,
|
|
System.SyncObjs,
|
|
Myc.Core.Tasks,
|
|
Myc.Signals,
|
|
Myc.Core.Signals,
|
|
Myc.Core.Atomic;
|
|
|
|
type
|
|
[TestFixture]
|
|
[IgnoreMemoryLeaks(true)]
|
|
TMycTaskFactoryTests = class(TObject)
|
|
private
|
|
FFactory: IMycTaskFactory;
|
|
public
|
|
[Setup]
|
|
procedure Setup;
|
|
[TearDown]
|
|
procedure TearDown;
|
|
|
|
[Test]
|
|
procedure TestFactoryCreationAndTeardown;
|
|
[Test]
|
|
procedure TestCreateThreadExecutesAndSignals;
|
|
[Test]
|
|
procedure TestRunImmediateJob;
|
|
[Test]
|
|
procedure TestRunDelayedJobExecutesAfterAllNotifies;
|
|
[Test]
|
|
procedure TestWaitForAlreadySetState;
|
|
[Test]
|
|
procedure TestThreadInfoMethods;
|
|
[Test]
|
|
procedure TestExceptionPropagationFromJob;
|
|
[Test]
|
|
procedure TestRunJobAfterFactoryTeardown;
|
|
[Test]
|
|
procedure TestWaitForInWorkerThreadRaisesException;
|
|
end;
|
|
|
|
implementation
|
|
|
|
{ TMycTaskFactoryTests }
|
|
|
|
procedure TMycTaskFactoryTests.Setup;
|
|
begin
|
|
FFactory := TMycTaskFactory.Create;
|
|
end;
|
|
|
|
procedure TMycTaskFactoryTests.TearDown;
|
|
begin
|
|
if Assigned(FFactory) then
|
|
begin
|
|
FFactory.Teardown;
|
|
FFactory := nil;
|
|
end;
|
|
end;
|
|
|
|
procedure TMycTaskFactoryTests.TestFactoryCreationAndTeardown;
|
|
begin
|
|
Assert.IsNotNull(FFactory, 'Factory should be created');
|
|
end;
|
|
|
|
procedure TMycTaskFactoryTests.TestCreateThreadExecutesAndSignals;
|
|
var
|
|
executed: Boolean;
|
|
threadState: IMycState;
|
|
procWrapper: TProc;
|
|
begin
|
|
executed := False;
|
|
procWrapper := procedure begin executed := True; end;
|
|
|
|
threadState := FFactory.CreateThread(procWrapper); // CreateThread in TaskFactory now uses TMycLatch.CreateLatch
|
|
Assert.IsNotNull(threadState, 'CreateThread should return a valid state object');
|
|
|
|
FFactory.WaitFor(threadState);
|
|
Assert.IsTrue(executed, 'Procedure in created thread should have executed');
|
|
end;
|
|
|
|
procedure TMycTaskFactoryTests.TestRunImmediateJob;
|
|
var
|
|
jobExecuted: Boolean;
|
|
jobCompletedLatch: IMycEvent;
|
|
begin
|
|
jobExecuted := False;
|
|
jobCompletedLatch := TEvent.CreateLatch(1); // Changed from Signals.CreateLatch
|
|
|
|
FFactory
|
|
.Run(
|
|
procedure
|
|
begin
|
|
jobExecuted := True;
|
|
jobCompletedLatch.Notify;
|
|
end)
|
|
.Notify;
|
|
|
|
FFactory.WaitFor(jobCompletedLatch.State);
|
|
Assert.IsTrue(jobExecuted, 'Immediate job should have executed');
|
|
end;
|
|
|
|
procedure TMycTaskFactoryTests.TestRunDelayedJobExecutesAfterAllNotifies;
|
|
var
|
|
jobExecuted: Boolean;
|
|
jobCompletedLatch: IMycEvent;
|
|
subscriber: IMycSubscriber;
|
|
begin
|
|
jobExecuted := False;
|
|
jobCompletedLatch := TEvent.CreateLatch(1); // Changed from Signals.CreateLatch
|
|
|
|
subscriber :=
|
|
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
|
|
Assert.AreNotEqual<IMycSubscriber>(TEvent.Null, subscriber, 'Subscriber should not be TMycLatch.Null for StartCount > 0');
|
|
|
|
subscriber.Notify;
|
|
|
|
FFactory.WaitFor(jobCompletedLatch.State);
|
|
Assert.IsTrue(jobExecuted, 'Delayed job should execute after all notifications');
|
|
end;
|
|
|
|
procedure TMycTaskFactoryTests.TestWaitForAlreadySetState;
|
|
var
|
|
alreadySetLatch: IMycEvent;
|
|
startTime, endTime: Cardinal;
|
|
begin
|
|
// TMycLatch.CreateLatch(0) will return TMycLatch.Null
|
|
alreadySetLatch := TEvent.CreateLatch(0); // Changed from Signals.CreateLatch
|
|
Assert.IsTrue(alreadySetLatch.State.IsSet, 'Latch should be initially set');
|
|
|
|
startTime := TThread.GetTickCount;
|
|
FFactory.WaitFor(alreadySetLatch.State);
|
|
endTime := TThread.GetTickCount;
|
|
|
|
Assert.IsTrue((endTime - startTime) < 50, 'WaitFor on an already set state should not block significantly');
|
|
end;
|
|
|
|
procedure TMycTaskFactoryTests.TestThreadInfoMethods;
|
|
var
|
|
inMainThreadInJob: Boolean;
|
|
inWorkerThreadInJob: Boolean;
|
|
jobDoneLatch: IMycEvent;
|
|
begin
|
|
inMainThreadInJob := True;
|
|
inWorkerThreadInJob := False;
|
|
jobDoneLatch := TEvent.CreateLatch(1); // Changed from Signals.CreateLatch
|
|
|
|
Assert.IsTrue(FFactory.InMainThread, 'Test method itself should be in main thread');
|
|
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
|
|
);
|
|
|
|
FFactory.WaitFor(jobDoneLatch.State);
|
|
|
|
Assert.IsFalse(inMainThreadInJob, 'Job executed by factory should not be in main thread');
|
|
Assert.IsTrue(inWorkerThreadInJob, 'Job executed by factory should be in a worker thread');
|
|
end;
|
|
|
|
procedure TMycTaskFactoryTests.TestExceptionPropagationFromJob;
|
|
var
|
|
jobDoneLatch: IMycEvent;
|
|
begin
|
|
jobDoneLatch := TEvent.CreateLatch(1);
|
|
|
|
FFactory.EnqueueJob(
|
|
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'
|
|
);
|
|
|
|
var noExceptionRaised: Boolean := True;
|
|
try
|
|
(FFactory as TMycTaskFactory).HandleException;
|
|
except
|
|
noExceptionRaised := False;
|
|
end;
|
|
Assert.IsTrue(noExceptionRaised, 'FException should be cleared after HandleException');
|
|
end;
|
|
|
|
procedure TMycTaskFactoryTests.TestRunJobAfterFactoryTeardown;
|
|
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'
|
|
);
|
|
end;
|
|
|
|
procedure TMycTaskFactoryTests.TestWaitForInWorkerThreadRaisesException;
|
|
var
|
|
exceptionCaughtInJob: Boolean;
|
|
jobDoneLatch: IMycEvent;
|
|
dummyStateToWaitFor: IMycState;
|
|
begin
|
|
exceptionCaughtInJob := False;
|
|
jobDoneLatch := TEvent.CreateLatch(1);
|
|
dummyStateToWaitFor := TEvent.CreateLatch(1).State;
|
|
|
|
FFactory.EnqueueJob(
|
|
procedure
|
|
begin
|
|
try
|
|
FFactory.WaitFor(dummyStateToWaitFor);
|
|
except
|
|
on E: TMycTaskFactory.ETaskException do
|
|
begin
|
|
exceptionCaughtInJob := True;
|
|
end;
|
|
end;
|
|
jobDoneLatch.Notify;
|
|
end
|
|
);
|
|
|
|
FFactory.WaitFor(jobDoneLatch.State);
|
|
Assert.IsTrue(exceptionCaughtInJob, 'WaitFor called within a worker thread job should raise ETaskException');
|
|
end;
|
|
|
|
initialization
|
|
TDUnitX.RegisterTestFixture(TMycTaskFactoryTests);
|
|
end.
|