TMutable
This commit is contained in:
+112
-5
@@ -8,14 +8,53 @@ uses
|
|||||||
Myc.Lazy;
|
Myc.Lazy;
|
||||||
|
|
||||||
type
|
type
|
||||||
TMycNullLazy<T> = class(TInterfacedObject, IMycLazy<T>)
|
TMycNullMutable<T> = class(TInterfacedObject, TMutable<T>.IMutable)
|
||||||
|
protected
|
||||||
|
function GetChanged: TSignal;
|
||||||
|
function GetValue: T;
|
||||||
|
end;
|
||||||
|
|
||||||
|
TMycMutableBase<T> = class(TInterfacedObject, TMutable<T>.IMutable)
|
||||||
|
strict private
|
||||||
|
FChanged: TEvent;
|
||||||
|
FChangeState: TSignal.TSubscription;
|
||||||
|
function GetChanged: TSignal;
|
||||||
|
protected
|
||||||
|
function GetValue: T; virtual; abstract;
|
||||||
|
public
|
||||||
|
constructor Create(const AChanged: TSignal);
|
||||||
|
destructor Destroy; override;
|
||||||
|
end;
|
||||||
|
|
||||||
|
TMycFuncMutable<T> = class(TMycMutableBase<T>)
|
||||||
|
private
|
||||||
|
FProc: TFunc<T>;
|
||||||
|
protected
|
||||||
|
function GetValue: T; override;
|
||||||
|
public
|
||||||
|
constructor Create(const AChanged: TSignal; const AProc: TFunc<T>);
|
||||||
|
end;
|
||||||
|
|
||||||
|
TMycWriteableMutable<T> = class(TInterfacedObject, TMutable<T>.IMutable, TMutable<T>.IWriteableMutable)
|
||||||
|
private
|
||||||
|
FValue: T;
|
||||||
|
FChanged: TEvent;
|
||||||
|
protected
|
||||||
|
function GetChanged: TSignal;
|
||||||
|
function GetValue: T;
|
||||||
|
public
|
||||||
|
constructor Create(const AValue: T);
|
||||||
|
procedure SetValue(const Value: T);
|
||||||
|
end;
|
||||||
|
|
||||||
|
TMycNullLazy<T> = class(TInterfacedObject, TLazy<T>.ILazy)
|
||||||
protected
|
protected
|
||||||
function GetChanged: TState;
|
function GetChanged: TState;
|
||||||
public
|
public
|
||||||
function Pop(out Res: T): Boolean;
|
function Pop(out Res: T): Boolean;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TMycLazyBase<T> = class(TInterfacedObject, IMycLazy<T>)
|
TMycLazyBase<T> = class(TInterfacedObject, TLazy<T>.ILazy)
|
||||||
strict private
|
strict private
|
||||||
FChanged: TFlag;
|
FChanged: TFlag;
|
||||||
FChangeState: TSignal.TSubscription;
|
FChangeState: TSignal.TSubscription;
|
||||||
@@ -39,15 +78,72 @@ type
|
|||||||
|
|
||||||
TMycChainedLazy<T> = class(TMycLazyBase<T>)
|
TMycChainedLazy<T> = class(TMycLazyBase<T>)
|
||||||
private
|
private
|
||||||
FValue: IMycLazy<T>;
|
FValue: TLazy<T>.ILazy;
|
||||||
protected
|
protected
|
||||||
function GetValue: T; override;
|
function GetValue: T; override;
|
||||||
public
|
public
|
||||||
constructor Create(const AValue: IMycLazy<T>);
|
constructor Create(const AValue: TLazy<T>.ILazy);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
|
{ TMycNullMutable<T> }
|
||||||
|
|
||||||
|
function TMycNullMutable<T>.GetChanged: TSignal;
|
||||||
|
begin
|
||||||
|
Result := TSignal.Null;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TMycNullMutable<T>.GetValue: T;
|
||||||
|
begin
|
||||||
|
Result := Default(T);
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TMycMutableBase<T> }
|
||||||
|
|
||||||
|
constructor TMycMutableBase<T>.Create(const AChanged: TSignal);
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
FChanged := TEvent.CreateEvent;
|
||||||
|
FChangeState := AChanged.Subscribe(FChanged);
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TMycMutableBase<T>.Destroy;
|
||||||
|
begin
|
||||||
|
FChangeState.Unsubscribe;
|
||||||
|
inherited;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TMycMutableBase<T>.GetChanged: TSignal;
|
||||||
|
begin
|
||||||
|
Result := FChanged.Signal;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TMycWriteableMutable<T> }
|
||||||
|
|
||||||
|
constructor TMycWriteableMutable<T>.Create(const AValue: T);
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
FValue := AValue;
|
||||||
|
FChanged := TEvent.CreateEvent;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TMycWriteableMutable<T>.GetChanged: TSignal;
|
||||||
|
begin
|
||||||
|
Result := FChanged.Signal;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TMycWriteableMutable<T>.GetValue: T;
|
||||||
|
begin
|
||||||
|
Result := FValue;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TMycWriteableMutable<T>.SetValue(const Value: T);
|
||||||
|
begin
|
||||||
|
FValue := Value;
|
||||||
|
FChanged.Notify;
|
||||||
|
end;
|
||||||
|
|
||||||
{ TMycNullLazy<T> }
|
{ TMycNullLazy<T> }
|
||||||
|
|
||||||
function TMycNullLazy<T>.GetChanged: TState;
|
function TMycNullLazy<T>.GetChanged: TState;
|
||||||
@@ -107,7 +203,7 @@ end;
|
|||||||
|
|
||||||
{ TMycChainedLazy<T> }
|
{ TMycChainedLazy<T> }
|
||||||
|
|
||||||
constructor TMycChainedLazy<T>.Create(const AValue: IMycLazy<T>);
|
constructor TMycChainedLazy<T>.Create(const AValue: TLazy<T>.ILazy);
|
||||||
begin
|
begin
|
||||||
inherited Create(AValue.Changed);
|
inherited Create(AValue.Changed);
|
||||||
FValue := AValue;
|
FValue := AValue;
|
||||||
@@ -119,4 +215,15 @@ begin
|
|||||||
raise Exception.Create('Lazy chain already popped');
|
raise Exception.Create('Lazy chain already popped');
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
constructor TMycFuncMutable<T>.Create(const AChanged: TSignal; const AProc: TFunc<T>);
|
||||||
|
begin
|
||||||
|
inherited Create(AChanged);
|
||||||
|
FProc := AProc;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TMycFuncMutable<T>.GetValue: T;
|
||||||
|
begin
|
||||||
|
Result := FProc();
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
+44
-31
@@ -12,8 +12,8 @@ type
|
|||||||
// This state is always considered "set".
|
// This state is always considered "set".
|
||||||
TMycNullSignal = class(TInterfacedObject, TSignal.ISignal)
|
TMycNullSignal = class(TInterfacedObject, TSignal.ISignal)
|
||||||
public
|
public
|
||||||
function Subscribe(Subscriber: IMycSubscriber): Pointer;
|
function Subscribe(Subscriber: TSignal.ISubscriber): Pointer;
|
||||||
procedure Unsubscribe(Tag: TMycNotifyList<IMycSubscriber>.TTag);
|
procedure Unsubscribe(Tag: TMycNotifyList<TSignal.ISubscriber>.TTag);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// TMycNullState implements a "null object" pattern for IMycState.
|
// TMycNullState implements a "null object" pattern for IMycState.
|
||||||
@@ -27,29 +27,31 @@ type
|
|||||||
// A state that acts as an event. It's state ist always set and it will always Trigger it's subscribers, if it gets notified by itself.
|
// A state that acts as an event. It's state ist always set and it will always Trigger it's subscribers, if it gets notified by itself.
|
||||||
TMycEvent = class(TInterfacedObject, TSignal.ISignal, TEvent.IEvent)
|
TMycEvent = class(TInterfacedObject, TSignal.ISignal, TEvent.IEvent)
|
||||||
strict private
|
strict private
|
||||||
FSubscribers: TMycNotifyList<IMycSubscriber>;
|
FSubscribers: TMycNotifyList<TSignal.ISubscriber>;
|
||||||
protected
|
protected
|
||||||
|
function GetSignal: TSignal;
|
||||||
function GetIsSet: Boolean;
|
function GetIsSet: Boolean;
|
||||||
public
|
public
|
||||||
constructor Create;
|
constructor Create;
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
function Subscribe(Subscriber: IMycSubscriber): Pointer;
|
function Subscribe(Subscriber: TSignal.ISubscriber): Pointer;
|
||||||
procedure Unsubscribe(Tag: TMycNotifyList<IMycSubscriber>.TTag);
|
procedure Unsubscribe(Tag: TMycNotifyList<TSignal.ISubscriber>.TTag);
|
||||||
procedure Trigger;
|
function Notify: Boolean;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TMycNullEvent = class(TMycNullSignal, TEvent.IEvent)
|
TMycNullEvent = class(TMycNullSignal, TEvent.IEvent)
|
||||||
private
|
private
|
||||||
procedure Trigger;
|
function GetSignal: TSignal;
|
||||||
|
function Notify: Boolean;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// TMycLatch implements a countdown latch.
|
// TMycLatch implements a countdown latch.
|
||||||
// It is initialized with a count. Calls to its Notify method (as an IMycSubscriber)
|
// It is initialized with a count. Calls to its Notify method (as an TSignal.ISubscriber)
|
||||||
// decrement this count. When the count reaches zero, the latch transitions to the "set"
|
// decrement this count. When the count reaches zero, the latch transitions to the "set"
|
||||||
// state (IsSet becomes true), notifies its current subscribers once, and then remains set.
|
// state (IsSet becomes true), notifies its current subscribers once, and then remains set.
|
||||||
TMycLatch = class(TInterfacedObject, TState.IState, TLatch.ILatch)
|
TMycLatch = class(TInterfacedObject, TState.IState, TLatch.ILatch)
|
||||||
strict private
|
strict private
|
||||||
FSubscribers: TMycNotifyList<IMycSubscriber>; // List of subscribers waiting for this latch to be set.
|
FSubscribers: TMycNotifyList<TSignal.ISubscriber>; // List of subscribers waiting for this latch to be set.
|
||||||
private
|
private
|
||||||
[volatile]
|
[volatile]
|
||||||
FCount: Integer; // The internal countdown value for the latch.
|
FCount: Integer; // The internal countdown value for the latch.
|
||||||
@@ -63,10 +65,10 @@ type
|
|||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
|
|
||||||
// ISignal implementation
|
// ISignal implementation
|
||||||
function Subscribe(Subscriber: IMycSubscriber): Pointer;
|
function Subscribe(Subscriber: TSignal.ISubscriber): Pointer;
|
||||||
procedure Unsubscribe(Tag: TMycNotifyList<IMycSubscriber>.TTag);
|
procedure Unsubscribe(Tag: TMycNotifyList<TSignal.ISubscriber>.TTag);
|
||||||
|
|
||||||
// IMycSubscriber implementation for TMycLatch itself.
|
// TSignal.ISubscriber implementation for TMycLatch itself.
|
||||||
// Decrements the internal count. If the count reaches zero,
|
// Decrements the internal count. If the count reaches zero,
|
||||||
// registered subscribers are notified, and the latch becomes permanently set.
|
// registered subscribers are notified, and the latch becomes permanently set.
|
||||||
function Notify: Boolean;
|
function Notify: Boolean;
|
||||||
@@ -83,7 +85,7 @@ type
|
|||||||
// Subscribers are notified of relevant state changes.
|
// Subscribers are notified of relevant state changes.
|
||||||
TMycFlag = class(TInterfacedObject, TState.IState, TFlag.IFlag)
|
TMycFlag = class(TInterfacedObject, TState.IState, TFlag.IFlag)
|
||||||
strict private
|
strict private
|
||||||
FSubscribers: TMycNotifyList<IMycSubscriber>; // List of subscribers interested in state changes of this dirty flag.
|
FSubscribers: TMycNotifyList<TSignal.ISubscriber>; // List of subscribers interested in state changes of this dirty flag.
|
||||||
private
|
private
|
||||||
[volatile]
|
[volatile]
|
||||||
FFlag: Boolean; // Internal state: true if dirty/set, false if clean/reset.
|
FFlag: Boolean; // Internal state: true if dirty/set, false if clean/reset.
|
||||||
@@ -98,10 +100,10 @@ type
|
|||||||
// Factory method to create a new TMycFlag instance.
|
// Factory method to create a new TMycFlag instance.
|
||||||
class function CreateDirty: TFlag.IFlag; static;
|
class function CreateDirty: TFlag.IFlag; static;
|
||||||
|
|
||||||
function Subscribe(Subscriber: IMycSubscriber): Pointer;
|
function Subscribe(Subscriber: TSignal.ISubscriber): Pointer;
|
||||||
procedure Unsubscribe(Tag: TMycNotifyList<IMycSubscriber>.TTag);
|
procedure Unsubscribe(Tag: TMycNotifyList<TSignal.ISubscriber>.TTag);
|
||||||
|
|
||||||
// IMycSubscriber implementation for TMycFlag.
|
// TSignal.ISubscriber implementation for TMycFlag.
|
||||||
// Sets this flag to dirty (true). Notifies subscribers if it transitioned from clean to dirty.
|
// Sets this flag to dirty (true). Notifies subscribers if it transitioned from clean to dirty.
|
||||||
function Notify: Boolean;
|
function Notify: Boolean;
|
||||||
|
|
||||||
@@ -145,17 +147,23 @@ begin
|
|||||||
Result := true;
|
Result := true;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMycEvent.Trigger;
|
function TMycEvent.GetSignal: TSignal;
|
||||||
|
begin
|
||||||
|
Result := Self;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TMycEvent.Notify: Boolean;
|
||||||
begin
|
begin
|
||||||
FSubscribers.Lock;
|
FSubscribers.Lock;
|
||||||
try
|
try
|
||||||
FSubscribers.Notify(function(Subscriber: IMycSubscriber): Boolean begin Result := Subscriber.Notify; end);
|
FSubscribers.Notify(function(Subscriber: TSignal.ISubscriber): Boolean begin Result := Subscriber.Notify; end);
|
||||||
finally
|
finally
|
||||||
FSubscribers.Release;
|
FSubscribers.Release;
|
||||||
end;
|
end;
|
||||||
|
Result := true;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TMycEvent.Subscribe(Subscriber: IMycSubscriber): Pointer;
|
function TMycEvent.Subscribe(Subscriber: TSignal.ISubscriber): Pointer;
|
||||||
begin
|
begin
|
||||||
Result := nil;
|
Result := nil;
|
||||||
if not Assigned(Subscriber) then
|
if not Assigned(Subscriber) then
|
||||||
@@ -170,7 +178,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMycEvent.Unsubscribe(Tag: TMycNotifyList<IMycSubscriber>.TTag);
|
procedure TMycEvent.Unsubscribe(Tag: TMycNotifyList<TSignal.ISubscriber>.TTag);
|
||||||
begin
|
begin
|
||||||
if Tag = nil then
|
if Tag = nil then
|
||||||
exit;
|
exit;
|
||||||
@@ -227,7 +235,7 @@ begin
|
|||||||
|
|
||||||
if shouldNotifySubscribers then
|
if shouldNotifySubscribers then
|
||||||
begin
|
begin
|
||||||
FSubscribers.Notify(function(Subscriber: IMycSubscriber): Boolean begin Result := Subscriber.Notify; end);
|
FSubscribers.Notify(function(Subscriber: TSignal.ISubscriber): Boolean begin Result := Subscriber.Notify; end);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// Returns true if the latch has not yet been set by this Notify call (count > 0).
|
// Returns true if the latch has not yet been set by this Notify call (count > 0).
|
||||||
@@ -254,7 +262,7 @@ begin
|
|||||||
Result := Self;
|
Result := Self;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TMycLatch.Subscribe(Subscriber: IMycSubscriber): Pointer;
|
function TMycLatch.Subscribe(Subscriber: TSignal.ISubscriber): Pointer;
|
||||||
var
|
var
|
||||||
alreadySet: Boolean;
|
alreadySet: Boolean;
|
||||||
begin
|
begin
|
||||||
@@ -281,7 +289,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMycLatch.Unsubscribe(Tag: TMycNotifyList<IMycSubscriber>.TTag);
|
procedure TMycLatch.Unsubscribe(Tag: TMycNotifyList<TSignal.ISubscriber>.TTag);
|
||||||
begin
|
begin
|
||||||
if Tag = nil then
|
if Tag = nil then
|
||||||
exit;
|
exit;
|
||||||
@@ -361,9 +369,9 @@ begin
|
|||||||
|
|
||||||
if wasPreviouslyClean then // Only notify subscribers if state changed from clean to dirty.
|
if wasPreviouslyClean then // Only notify subscribers if state changed from clean to dirty.
|
||||||
begin
|
begin
|
||||||
FSubscribers.Notify(function(Subscriber: IMycSubscriber): Boolean begin Result := Subscriber.Notify; end);
|
FSubscribers.Notify(function(Subscriber: TSignal.ISubscriber): Boolean begin Result := Subscriber.Notify; end);
|
||||||
end;
|
end;
|
||||||
// The return value of this Notify (as an IMycSubscriber) indicates if this
|
// The return value of this Notify (as an TSignal.ISubscriber) indicates if this
|
||||||
// TMycFlag instance itself would want more notifications if it were subscribed to something.
|
// TMycFlag instance itself would want more notifications if it were subscribed to something.
|
||||||
// Here, it reflects whether a state change to dirty occurred due to this call.
|
// Here, it reflects whether a state change to dirty occurred due to this call.
|
||||||
Result := wasPreviouslyClean;
|
Result := wasPreviouslyClean;
|
||||||
@@ -372,7 +380,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TMycFlag.Subscribe(Subscriber: IMycSubscriber): Pointer;
|
function TMycFlag.Subscribe(Subscriber: TSignal.ISubscriber): Pointer;
|
||||||
begin
|
begin
|
||||||
if not Assigned(Subscriber) then
|
if not Assigned(Subscriber) then
|
||||||
exit(nil);
|
exit(nil);
|
||||||
@@ -394,7 +402,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMycFlag.Unsubscribe(Tag: TMycNotifyList<IMycSubscriber>.TTag);
|
procedure TMycFlag.Unsubscribe(Tag: TMycNotifyList<TSignal.ISubscriber>.TTag);
|
||||||
begin
|
begin
|
||||||
if Tag = nil then
|
if Tag = nil then
|
||||||
exit;
|
exit;
|
||||||
@@ -407,12 +415,17 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMycNullEvent.Trigger;
|
function TMycNullEvent.GetSignal: TSignal;
|
||||||
begin
|
begin
|
||||||
// nop
|
Result := TSignal.Null;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TMycNullSignal.Subscribe(Subscriber: IMycSubscriber): Pointer;
|
function TMycNullEvent.Notify: Boolean;
|
||||||
|
begin
|
||||||
|
Result := false;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TMycNullSignal.Subscribe(Subscriber: TSignal.ISubscriber): Pointer;
|
||||||
begin
|
begin
|
||||||
// Since the state is always set, notify the subscriber immediately.
|
// Since the state is always set, notify the subscriber immediately.
|
||||||
if Assigned(Subscriber) then
|
if Assigned(Subscriber) then
|
||||||
@@ -423,7 +436,7 @@ begin
|
|||||||
Result := nil;
|
Result := nil;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMycNullSignal.Unsubscribe(Tag: TMycNotifyList<IMycSubscriber>.TTag);
|
procedure TMycNullSignal.Unsubscribe(Tag: TMycNotifyList<TSignal.ISubscriber>.TTag);
|
||||||
begin
|
begin
|
||||||
// Unsubscribing from a null state has no effect.
|
// Unsubscribing from a null state has no effect.
|
||||||
end;
|
end;
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ type
|
|||||||
// Exceptions during Job execution are caught; the first one is stored
|
// Exceptions during Job execution are caught; the first one is stored
|
||||||
// and can be re-raised in the main thread via WaitFor or Teardown.
|
// and can be re-raised in the main thread via WaitFor or Teardown.
|
||||||
// Raises ETaskException if the factory is already terminated.
|
// Raises ETaskException if the factory is already terminated.
|
||||||
function Run(Job: TProc): IMycSubscriber;
|
function Run(Job: TProc): TSignal.ISubscriber;
|
||||||
|
|
||||||
procedure EnqueueJob(const Job: TProc);
|
procedure EnqueueJob(const Job: TProc);
|
||||||
|
|
||||||
@@ -80,7 +80,7 @@ type
|
|||||||
function CreateThread(const Proc: TProc): TState.IState;
|
function CreateThread(const Proc: TProc): TState.IState;
|
||||||
procedure HandleException;
|
procedure HandleException;
|
||||||
procedure EnqueueJob(const Job: TProc);
|
procedure EnqueueJob(const Job: TProc);
|
||||||
function Run(Job: TProc): IMycSubscriber;
|
function Run(Job: TProc): TSignal.ISubscriber;
|
||||||
function CreateTask(const Gate: TState; const Proc: TProc): TSignal.TSubscription;
|
function CreateTask(const Gate: TState; const Proc: TProc): TSignal.TSubscription;
|
||||||
procedure WaitFor(State: TState.IState);
|
procedure WaitFor(State: TState.IState);
|
||||||
procedure Teardown;
|
procedure Teardown;
|
||||||
@@ -98,7 +98,7 @@ type
|
|||||||
implementation
|
implementation
|
||||||
|
|
||||||
type
|
type
|
||||||
TMycPendingJob = class(TInterfacedObject, IMycSubscriber)
|
TMycPendingJob = class(TInterfacedObject, TSignal.ISubscriber)
|
||||||
private
|
private
|
||||||
[volatile]
|
[volatile]
|
||||||
FJob: TProc; // The job to execute when count reaches zero
|
FJob: TProc; // The job to execute when count reaches zero
|
||||||
@@ -112,7 +112,7 @@ type
|
|||||||
property Owner: TMycTaskFactory read FOwner;
|
property Owner: TMycTaskFactory read FOwner;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TMycTaskWait = class sealed(TInterfacedObject, IMycSubscriber)
|
TMycTaskWait = class sealed(TInterfacedObject, TSignal.ISubscriber)
|
||||||
private
|
private
|
||||||
[volatile]
|
[volatile]
|
||||||
FSemaphore: TSemaphore; // System.SyncObjs.TSemaphore to be released on notification
|
FSemaphore: TSemaphore; // System.SyncObjs.TSemaphore to be released on notification
|
||||||
@@ -303,7 +303,7 @@ begin
|
|||||||
TaskManager := nil;
|
TaskManager := nil;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TMycTaskFactory.Run(Job: TProc): IMycSubscriber;
|
function TMycTaskFactory.Run(Job: TProc): TSignal.ISubscriber;
|
||||||
begin
|
begin
|
||||||
if FTerminated <> 0 then
|
if FTerminated <> 0 then
|
||||||
raise ETaskException.Create('Task factory terminated');
|
raise ETaskException.Create('Task factory terminated');
|
||||||
|
|||||||
@@ -81,7 +81,6 @@ end;
|
|||||||
function TFuture<T>.Chain<S>(const Proc: TFunc<T, S>): TFuture<S>;
|
function TFuture<T>.Chain<S>(const Proc: TFunc<T, S>): TFuture<S>;
|
||||||
begin
|
begin
|
||||||
var Cap := FFuture;
|
var Cap := FFuture;
|
||||||
|
|
||||||
Result := TFuture<S>.Construct(FFuture.Done, function: S begin Result := Proc(Cap.Result); end);
|
Result := TFuture<S>.Construct(FFuture.Done, function: S begin Result := Proc(Cap.Result); end);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|||||||
+118
-28
@@ -7,41 +7,80 @@ uses
|
|||||||
Myc.Signals;
|
Myc.Signals;
|
||||||
|
|
||||||
type
|
type
|
||||||
IMycMutable<T> = interface
|
TMutable<T> = record
|
||||||
function GetChanged: TState;
|
type
|
||||||
function GetValue: T;
|
IMutable = interface
|
||||||
property Changed: TState read GetChanged;
|
{$REGION 'property access'}
|
||||||
property Value: T read GetValue;
|
function GetChanged: TSignal;
|
||||||
end;
|
function GetValue: T;
|
||||||
|
{$ENDREGION}
|
||||||
|
property Changed: TSignal read GetChanged;
|
||||||
|
property Value: T read GetValue;
|
||||||
|
end;
|
||||||
|
|
||||||
IMycLazy<T> = interface
|
IWriteableMutable = interface(IMutable)
|
||||||
{$REGION 'property access'}
|
procedure SetValue(const Value: T);
|
||||||
function GetChanged: TState;
|
end;
|
||||||
{$ENDREGION}
|
|
||||||
function Pop(out Res: T): Boolean;
|
{$REGION 'private'}
|
||||||
property Changed: TState read GetChanged;
|
strict private
|
||||||
|
class var
|
||||||
|
FNull: IMutable;
|
||||||
|
class constructor CreateClass;
|
||||||
|
|
||||||
|
private
|
||||||
|
FMutable: IMutable;
|
||||||
|
function GetChanged: TSignal; inline;
|
||||||
|
{$ENDREGION}
|
||||||
|
public
|
||||||
|
constructor Create(const AMutable: IMutable);
|
||||||
|
class operator Initialize(out Dest: TMutable<T>);
|
||||||
|
class operator Implicit(const A: IMutable): TMutable<T>; overload;
|
||||||
|
class operator Implicit(const A: TMutable<T>): IMutable; overload;
|
||||||
|
|
||||||
|
class property Null: IMutable read FNull;
|
||||||
|
|
||||||
|
class function Construct(const Changing: TSignal; const Proc: TFunc<T>): TMutable<T>; overload; static;
|
||||||
|
|
||||||
|
class function CreateWriteable(const Init: T): IWriteableMutable; overload; static;
|
||||||
|
|
||||||
|
property Changed: TSignal read GetChanged;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TLazy<T> = record
|
TLazy<T> = record
|
||||||
private
|
type
|
||||||
FLazy: IMycLazy<T>;
|
ILazy = interface
|
||||||
function GetChanged: TState.IState; inline;
|
{$REGION 'property access'}
|
||||||
|
function GetChanged: TState;
|
||||||
|
{$ENDREGION}
|
||||||
|
function Pop(out Res: T): Boolean;
|
||||||
|
property Changed: TState read GetChanged;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{$REGION 'private'}
|
||||||
|
strict private
|
||||||
class var
|
class var
|
||||||
FNull: IMycLazy<T>;
|
FNull: ILazy;
|
||||||
class constructor CreateClass;
|
class constructor CreateClass;
|
||||||
|
|
||||||
|
private
|
||||||
|
FLazy: ILazy;
|
||||||
|
function GetChanged: TState; inline;
|
||||||
|
{$ENDREGION}
|
||||||
public
|
public
|
||||||
constructor Create(const ALazy: IMycLazy<T>);
|
constructor Create(const ALazy: ILazy);
|
||||||
|
class operator Initialize(out Dest: TLazy<T>);
|
||||||
|
class operator Implicit(const A: ILazy): TLazy<T>; overload;
|
||||||
|
class operator Implicit(const A: TLazy<T>): ILazy; overload;
|
||||||
|
|
||||||
class function Construct(const Changing: TState.IState; const Proc: TFunc<T>): IMycLazy<T>; overload; static;
|
class function Construct(const Changing: TState; const Proc: TFunc<T>): TLazy<T>; overload; static;
|
||||||
class function Construct(const Value: IMycLazy<T>): IMycLazy<T>; overload; static;
|
class function Construct(const Value: ILazy): TLazy<T>; overload; static;
|
||||||
|
|
||||||
class operator Implicit(const A: IMycLazy<T>): TLazy<T>; overload;
|
class property Null: ILazy read FNull;
|
||||||
class operator Implicit(const A: TLazy<T>): IMycLazy<T>; overload;
|
|
||||||
|
|
||||||
function Pop(out Res: T): Boolean; inline;
|
function Pop(out Res: T): Boolean; inline;
|
||||||
|
|
||||||
property Changed: TState.IState read GetChanged;
|
property Changed: TState read GetChanged;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
@@ -49,14 +88,60 @@ implementation
|
|||||||
uses
|
uses
|
||||||
Myc.Core.Lazy;
|
Myc.Core.Lazy;
|
||||||
|
|
||||||
constructor TLazy<T>.Create(const ALazy: IMycLazy<T>);
|
{ TMutable<T> }
|
||||||
|
|
||||||
|
constructor TMutable<T>.Create(const AMutable: IMutable);
|
||||||
|
begin
|
||||||
|
FMutable := AMutable;
|
||||||
|
if not Assigned(FMutable) then
|
||||||
|
FMutable := FNull;
|
||||||
|
end;
|
||||||
|
|
||||||
|
class constructor TMutable<T>.CreateClass;
|
||||||
|
begin
|
||||||
|
FNull := TMycNullMutable<T>.Create;
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TMutable<T>.Construct(const Changing: TSignal; const Proc: TFunc<T>): TMutable<T>;
|
||||||
|
begin
|
||||||
|
Result := TMycFuncMutable<T>.Create(Changing, Proc);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TMutable<T>.CreateWriteable(const Init: T): IWriteableMutable;
|
||||||
|
begin
|
||||||
|
Result := TMycWriteableMutable<T>.Create(Init);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TMutable<T>.GetChanged: TSignal;
|
||||||
|
begin
|
||||||
|
Result := FMutable.Changed;
|
||||||
|
end;
|
||||||
|
|
||||||
|
class operator TMutable<T>.Implicit(const A: TMutable<T>): IMutable;
|
||||||
|
begin
|
||||||
|
Result := A.FMutable;
|
||||||
|
end;
|
||||||
|
|
||||||
|
class operator TMutable<T>.Implicit(const A: IMutable): TMutable<T>;
|
||||||
|
begin
|
||||||
|
Result.Create(A);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class operator TMutable<T>.Initialize(out Dest: TMutable<T>);
|
||||||
|
begin
|
||||||
|
Dest.FMutable := FNull;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TLazy<T> }
|
||||||
|
|
||||||
|
constructor TLazy<T>.Create(const ALazy: ILazy);
|
||||||
begin
|
begin
|
||||||
FLazy := ALazy;
|
FLazy := ALazy;
|
||||||
if not Assigned(FLazy) then
|
if not Assigned(FLazy) then
|
||||||
FLazy := FNull;
|
FLazy := FNull;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
class function TLazy<T>.Construct(const Changing: TState.IState; const Proc: TFunc<T>): IMycLazy<T>;
|
class function TLazy<T>.Construct(const Changing: TState; const Proc: TFunc<T>): TLazy<T>;
|
||||||
begin
|
begin
|
||||||
Result := TMycFuncLazy<T>.Create(Changing, Proc);
|
Result := TMycFuncLazy<T>.Create(Changing, Proc);
|
||||||
end;
|
end;
|
||||||
@@ -66,12 +151,12 @@ begin
|
|||||||
FNull := TMycNullLazy<T>.Create;
|
FNull := TMycNullLazy<T>.Create;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
class function TLazy<T>.Construct(const Value: IMycLazy<T>): IMycLazy<T>;
|
class function TLazy<T>.Construct(const Value: ILazy): TLazy<T>;
|
||||||
begin
|
begin
|
||||||
Result := TMycChainedLazy<T>.Create(Value);
|
Result := TMycChainedLazy<T>.Create(Value);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TLazy<T>.GetChanged: TState.IState;
|
function TLazy<T>.GetChanged: TState;
|
||||||
begin
|
begin
|
||||||
Result := FLazy.Changed;
|
Result := FLazy.Changed;
|
||||||
end;
|
end;
|
||||||
@@ -81,14 +166,19 @@ begin
|
|||||||
Result := FLazy.Pop(Res);
|
Result := FLazy.Pop(Res);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
class operator TLazy<T>.Implicit(const A: IMycLazy<T>): TLazy<T>;
|
class operator TLazy<T>.Implicit(const A: ILazy): TLazy<T>;
|
||||||
begin
|
begin
|
||||||
Result.Create(A);
|
Result.Create(A);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
class operator TLazy<T>.Implicit(const A: TLazy<T>): IMycLazy<T>;
|
class operator TLazy<T>.Implicit(const A: TLazy<T>): ILazy;
|
||||||
begin
|
begin
|
||||||
Result := A.FLazy;
|
Result := A.FLazy;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
class operator TLazy<T>.Initialize(out Dest: TLazy<T>);
|
||||||
|
begin
|
||||||
|
Dest.FLazy := FNull;
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
+59
-48
@@ -6,16 +6,16 @@ uses
|
|||||||
System.SysUtils;
|
System.SysUtils;
|
||||||
|
|
||||||
type
|
type
|
||||||
IMycSubscriber = interface
|
|
||||||
function Notify: Boolean;
|
|
||||||
end;
|
|
||||||
|
|
||||||
TSignal = record
|
TSignal = record
|
||||||
type
|
type
|
||||||
|
ISubscriber = interface
|
||||||
|
function Notify: Boolean;
|
||||||
|
end;
|
||||||
|
|
||||||
TSubscriptionTag = Pointer;
|
TSubscriptionTag = Pointer;
|
||||||
|
|
||||||
ISignal = interface
|
ISignal = interface
|
||||||
function Subscribe(Subscriber: IMycSubscriber): TSubscriptionTag;
|
function Subscribe(Subscriber: ISubscriber): TSubscriptionTag;
|
||||||
procedure Unsubscribe(Tag: TSubscriptionTag);
|
procedure Unsubscribe(Tag: TSubscriptionTag);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -45,38 +45,11 @@ type
|
|||||||
class operator Implicit(const A: ISignal): TSignal; overload;
|
class operator Implicit(const A: ISignal): TSignal; overload;
|
||||||
class operator Implicit(const A: TSignal): ISignal; overload;
|
class operator Implicit(const A: TSignal): ISignal; overload;
|
||||||
|
|
||||||
function Subscribe(Subscriber: IMycSubscriber): TSubscription; inline;
|
function Subscribe(Subscriber: ISubscriber): TSubscription; inline;
|
||||||
|
|
||||||
class property Null: ISignal read FNull;
|
class property Null: ISignal read FNull;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TEvent = record
|
|
||||||
type
|
|
||||||
IEvent = interface(TSignal.ISignal)
|
|
||||||
procedure Trigger;
|
|
||||||
end;
|
|
||||||
|
|
||||||
{$REGION 'private'}
|
|
||||||
strict private
|
|
||||||
class var
|
|
||||||
FNull: IEvent;
|
|
||||||
class constructor ClassCreate;
|
|
||||||
|
|
||||||
private
|
|
||||||
FEvent: IEvent;
|
|
||||||
{$ENDREGION}
|
|
||||||
public
|
|
||||||
constructor Create(const AEvent: IEvent);
|
|
||||||
class operator Initialize(out Dest: TEvent);
|
|
||||||
class operator Implicit(const A: IEvent): TEvent; overload;
|
|
||||||
class operator Implicit(const A: TEvent): IEvent; overload;
|
|
||||||
|
|
||||||
class property Null: IEvent read FNull;
|
|
||||||
|
|
||||||
function Subscribe(Subscriber: IMycSubscriber): TSignal.TSubscription; inline;
|
|
||||||
procedure Unsubscribe(Tag: Pointer); inline;
|
|
||||||
end;
|
|
||||||
|
|
||||||
TState = record
|
TState = record
|
||||||
type
|
type
|
||||||
IState = interface(TSignal.ISignal)
|
IState = interface(TSignal.ISignal)
|
||||||
@@ -108,18 +81,54 @@ type
|
|||||||
|
|
||||||
class property Null: IState read FNull;
|
class property Null: IState read FNull;
|
||||||
|
|
||||||
function Subscribe(Subscriber: IMycSubscriber): TSignal.TSubscription; inline;
|
function Subscribe(Subscriber: TSignal.ISubscriber): TSignal.TSubscription; inline;
|
||||||
procedure Unsubscribe(Tag: Pointer); inline;
|
|
||||||
|
|
||||||
property IsSet: Boolean read GetIsSet;
|
property IsSet: Boolean read GetIsSet;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// An event is a stateless signal that forwards all notifications to the subscribers.
|
||||||
|
TEvent = record
|
||||||
|
type
|
||||||
|
IEvent = interface(TSignal.ISubscriber)
|
||||||
|
{$REGION 'property access'}
|
||||||
|
function GetSignal: TSignal;
|
||||||
|
{$ENDREGION}
|
||||||
|
property Signal: TSignal read GetSignal;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{$REGION 'private'}
|
||||||
|
strict private
|
||||||
|
class var
|
||||||
|
FNull: IEvent;
|
||||||
|
class constructor ClassCreate;
|
||||||
|
|
||||||
|
private
|
||||||
|
FEvent: IEvent;
|
||||||
|
function GetSignal: TSignal; inline;
|
||||||
|
{$ENDREGION}
|
||||||
|
public
|
||||||
|
constructor Create(const AEvent: IEvent);
|
||||||
|
class operator Initialize(out Dest: TEvent);
|
||||||
|
class operator Implicit(const A: IEvent): TEvent; overload;
|
||||||
|
class operator Implicit(const A: TEvent): IEvent; overload;
|
||||||
|
|
||||||
|
class function CreateEvent: TEvent; static;
|
||||||
|
|
||||||
|
class property Null: IEvent read FNull;
|
||||||
|
|
||||||
|
function Notify: Boolean; inline;
|
||||||
|
|
||||||
|
property Signal: TSignal read GetSignal;
|
||||||
|
end;
|
||||||
|
|
||||||
// A latch is a specific type of event that, once set, remains set (non-resettable).
|
// A latch is a specific type of event that, once set, remains set (non-resettable).
|
||||||
// It becomes set when the internal countdown reaches zero.
|
// It becomes set when the internal countdown reaches zero.
|
||||||
TLatch = record
|
TLatch = record
|
||||||
type
|
type
|
||||||
ILatch = interface(IMycSubscriber)
|
ILatch = interface(TSignal.ISubscriber)
|
||||||
|
{$REGION 'property access'}
|
||||||
function GetState: TState;
|
function GetState: TState;
|
||||||
|
{$ENDREGION}
|
||||||
property State: TState read GetState;
|
property State: TState read GetState;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -153,8 +162,10 @@ type
|
|||||||
// TFlag represents a resettable flag, typically indicating if a State is "dirty" (requiring attention) or "clean".
|
// TFlag represents a resettable flag, typically indicating if a State is "dirty" (requiring attention) or "clean".
|
||||||
TFlag = record
|
TFlag = record
|
||||||
type
|
type
|
||||||
IFlag = interface(IMycSubscriber)
|
IFlag = interface(TSignal.ISubscriber)
|
||||||
|
{$REGION 'property access'}
|
||||||
function GetState: TState;
|
function GetState: TState;
|
||||||
|
{$ENDREGION}
|
||||||
function Reset: Boolean;
|
function Reset: Boolean;
|
||||||
property State: TState read GetState;
|
property State: TState read GetState;
|
||||||
end;
|
end;
|
||||||
@@ -260,16 +271,11 @@ begin
|
|||||||
Result := FState.IsSet;
|
Result := FState.IsSet;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TState.Subscribe(Subscriber: IMycSubscriber): TSignal.TSubscription;
|
function TState.Subscribe(Subscriber: TSignal.ISubscriber): TSignal.TSubscription;
|
||||||
begin
|
begin
|
||||||
Result := TSignal.TSubscription.Create(FState, FState.Subscribe(Subscriber));
|
Result := TSignal.TSubscription.Create(FState, FState.Subscribe(Subscriber));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TState.Unsubscribe(Tag: Pointer);
|
|
||||||
begin
|
|
||||||
FState.Unsubscribe(Tag);
|
|
||||||
end;
|
|
||||||
|
|
||||||
class operator TState.Implicit(const A: TState): IState;
|
class operator TState.Implicit(const A: TState): IState;
|
||||||
begin
|
begin
|
||||||
Result := A.FState;
|
Result := A.FState;
|
||||||
@@ -299,14 +305,19 @@ begin
|
|||||||
FNull := TMycNullEvent.Create();
|
FNull := TMycNullEvent.Create();
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TEvent.Subscribe(Subscriber: IMycSubscriber): TSignal.TSubscription;
|
class function TEvent.CreateEvent: TEvent;
|
||||||
begin
|
begin
|
||||||
Result := TSignal.TSubscription.Create(FEvent, FEvent.Subscribe(Subscriber));
|
Result := TMycEvent.Create;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TEvent.Unsubscribe(Tag: Pointer);
|
function TEvent.GetSignal: TSignal;
|
||||||
begin
|
begin
|
||||||
FEvent.Unsubscribe(Tag);
|
Result := FEvent.Signal;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TEvent.Notify: Boolean;
|
||||||
|
begin
|
||||||
|
Result := FEvent.Notify;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
class operator TEvent.Implicit(const A: TEvent): IEvent;
|
class operator TEvent.Implicit(const A: TEvent): IEvent;
|
||||||
@@ -443,7 +454,7 @@ begin
|
|||||||
FSignal := FNull;
|
FSignal := FNull;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TSignal.Subscribe(Subscriber: IMycSubscriber): TSubscription;
|
function TSignal.Subscribe(Subscriber: ISubscriber): TSubscription;
|
||||||
begin
|
begin
|
||||||
Result := TSubscription.Create(FSignal, FSignal.Subscribe(Subscriber));
|
Result := TSubscription.Create(FSignal, FSignal.Subscribe(Subscriber));
|
||||||
end;
|
end;
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ uses
|
|||||||
System.Generics.Collections;
|
System.Generics.Collections;
|
||||||
|
|
||||||
type
|
type
|
||||||
TMycExecMock = class(TInterfacedObject, IMycSubscriber)
|
TMycExecMock = class(TInterfacedObject, TSignal.ISubscriber)
|
||||||
private
|
private
|
||||||
FProc: TProc;
|
FProc: TProc;
|
||||||
public
|
public
|
||||||
|
|||||||
+21
-21
@@ -13,8 +13,8 @@ type
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
TTestMycCoreLazy = class(TObject)
|
TTestMycCoreLazy = class(TObject)
|
||||||
private
|
private
|
||||||
procedure Helper_ConsumeInitialPop(const ALazy: IMycLazy<Integer>; InitialExpectedValue: Integer); overload;
|
procedure Helper_ConsumeInitialPop(const ALazy: TLazy<Integer>.ILazy; InitialExpectedValue: Integer); overload;
|
||||||
procedure Helper_ConsumeInitialPop(const ALazy: IMycLazy<string>; const InitialExpectedValue: string); overload;
|
procedure Helper_ConsumeInitialPop(const ALazy: TLazy<String>.ILazy; const InitialExpectedValue: string); overload;
|
||||||
public
|
public
|
||||||
[Setup]
|
[Setup]
|
||||||
procedure Setup;
|
procedure Setup;
|
||||||
@@ -65,7 +65,7 @@ uses
|
|||||||
|
|
||||||
{ TTestMycCoreLazy Helper Methods }
|
{ TTestMycCoreLazy Helper Methods }
|
||||||
|
|
||||||
procedure TTestMycCoreLazy.Helper_ConsumeInitialPop(const ALazy: IMycLazy<Integer>; InitialExpectedValue: Integer);
|
procedure TTestMycCoreLazy.Helper_ConsumeInitialPop(const ALazy: TLazy<Integer>.ILazy; InitialExpectedValue: Integer);
|
||||||
var
|
var
|
||||||
tempValue: Integer;
|
tempValue: Integer;
|
||||||
popResult: Boolean;
|
popResult: Boolean;
|
||||||
@@ -77,7 +77,7 @@ begin
|
|||||||
Assert.IsFalse(ALazy.GetChanged.IsSet, 'Changed.IsSet should be false after consuming initial pop');
|
Assert.IsFalse(ALazy.GetChanged.IsSet, 'Changed.IsSet should be false after consuming initial pop');
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TTestMycCoreLazy.Helper_ConsumeInitialPop(const ALazy: IMycLazy<string>; const InitialExpectedValue: string);
|
procedure TTestMycCoreLazy.Helper_ConsumeInitialPop(const ALazy: TLazy<String>.ILazy; const InitialExpectedValue: string);
|
||||||
var
|
var
|
||||||
tempValue: string;
|
tempValue: string;
|
||||||
popResult: Boolean;
|
popResult: Boolean;
|
||||||
@@ -102,10 +102,10 @@ end;
|
|||||||
// == Tests for TMycNullLazy<T> ==
|
// == Tests for TMycNullLazy<T> ==
|
||||||
procedure TTestMycCoreLazy.TestNullLazy_GetChanged_IsAlwaysNullState;
|
procedure TTestMycCoreLazy.TestNullLazy_GetChanged_IsAlwaysNullState;
|
||||||
var
|
var
|
||||||
nullLazy: IMycLazy<Integer>;
|
nullLazy: TLazy<Integer>.ILazy;
|
||||||
changedState: TState.IState;
|
changedState: TState.IState;
|
||||||
begin
|
begin
|
||||||
nullLazy := TMycNullLazy<Integer>.Create as IMycLazy<Integer>;
|
nullLazy := TMycNullLazy<Integer>.Create as TLazy<Integer>.ILazy;
|
||||||
Assert.IsNotNull(nullLazy, 'TMycNullLazy<Integer> instance should not be nil');
|
Assert.IsNotNull(nullLazy, 'TMycNullLazy<Integer> instance should not be nil');
|
||||||
changedState := nullLazy.GetChanged;
|
changedState := nullLazy.GetChanged;
|
||||||
Assert.AreSame(TState.Null, changedState, 'TMycNullLazy.GetChanged should return TState.Null');
|
Assert.AreSame(TState.Null, changedState, 'TMycNullLazy.GetChanged should return TState.Null');
|
||||||
@@ -114,11 +114,11 @@ end;
|
|||||||
|
|
||||||
procedure TTestMycCoreLazy.TestNullLazy_Pop_ReturnsDefaultIntegerAndTrue;
|
procedure TTestMycCoreLazy.TestNullLazy_Pop_ReturnsDefaultIntegerAndTrue;
|
||||||
var
|
var
|
||||||
nullLazy: IMycLazy<Integer>;
|
nullLazy: TLazy<Integer>.ILazy;
|
||||||
value: Integer;
|
value: Integer;
|
||||||
result: Boolean;
|
result: Boolean;
|
||||||
begin
|
begin
|
||||||
nullLazy := TMycNullLazy<Integer>.Create as IMycLazy<Integer>;
|
nullLazy := TMycNullLazy<Integer>.Create as TLazy<Integer>.ILazy;
|
||||||
Assert.IsNotNull(nullLazy, 'TMycNullLazy<Integer> instance should not be nil');
|
Assert.IsNotNull(nullLazy, 'TMycNullLazy<Integer> instance should not be nil');
|
||||||
value := 123;
|
value := 123;
|
||||||
result := nullLazy.Pop(value);
|
result := nullLazy.Pop(value);
|
||||||
@@ -128,11 +128,11 @@ end;
|
|||||||
|
|
||||||
procedure TTestMycCoreLazy.TestNullLazy_Pop_ReturnsDefaultStringAndTrue;
|
procedure TTestMycCoreLazy.TestNullLazy_Pop_ReturnsDefaultStringAndTrue;
|
||||||
var
|
var
|
||||||
nullLazy: IMycLazy<string>;
|
nullLazy: TLazy<String>.ILazy;
|
||||||
value: string;
|
value: string;
|
||||||
result: Boolean;
|
result: Boolean;
|
||||||
begin
|
begin
|
||||||
nullLazy := TMycNullLazy<string>.Create as IMycLazy<string>;
|
nullLazy := TMycNullLazy<string>.Create as TLazy<String>.ILazy;
|
||||||
Assert.IsNotNull(nullLazy, 'TMycNullLazy<string> instance should not be nil');
|
Assert.IsNotNull(nullLazy, 'TMycNullLazy<string> instance should not be nil');
|
||||||
value := 'test';
|
value := 'test';
|
||||||
result := nullLazy.Pop(value);
|
result := nullLazy.Pop(value);
|
||||||
@@ -142,11 +142,11 @@ end;
|
|||||||
|
|
||||||
procedure TTestMycCoreLazy.TestNullLazy_Pop_ReturnsDefaultInterfaceAndTrue;
|
procedure TTestMycCoreLazy.TestNullLazy_Pop_ReturnsDefaultInterfaceAndTrue;
|
||||||
var
|
var
|
||||||
nullLazy: IMycLazy<TState.IState>;
|
nullLazy: TLazy<TState.IState>.ILazy;
|
||||||
value: TState.IState;
|
value: TState.IState;
|
||||||
result: Boolean;
|
result: Boolean;
|
||||||
begin
|
begin
|
||||||
nullLazy := TMycNullLazy<TState.IState>.Create as IMycLazy<TState.IState>;
|
nullLazy := TMycNullLazy<TState.IState>.Create as TLazy<TState.IState>.ILazy;
|
||||||
Assert.IsNotNull(nullLazy, 'TMycNullLazy<TState.IState> instance should not be nil');
|
Assert.IsNotNull(nullLazy, 'TMycNullLazy<TState.IState> instance should not be nil');
|
||||||
value := TState.Null;
|
value := TState.Null;
|
||||||
result := nullLazy.Pop(value);
|
result := nullLazy.Pop(value);
|
||||||
@@ -157,7 +157,7 @@ end;
|
|||||||
// == Tests for TMycFuncLazy<T> - Initial State by Design ==
|
// == Tests for TMycFuncLazy<T> - Initial State by Design ==
|
||||||
procedure TTestMycCoreLazy.TestFuncLazy_GetChanged_IsAlwaysTrueAfterCreation;
|
procedure TTestMycCoreLazy.TestFuncLazy_GetChanged_IsAlwaysTrueAfterCreation;
|
||||||
var
|
var
|
||||||
funcLazy: IMycLazy<Integer>;
|
funcLazy: TLazy<Integer>.ILazy;
|
||||||
changedState: TState.IState;
|
changedState: TState.IState;
|
||||||
sourceDirty: TFlag.IFlag;
|
sourceDirty: TFlag.IFlag;
|
||||||
begin
|
begin
|
||||||
@@ -172,7 +172,7 @@ end;
|
|||||||
|
|
||||||
procedure TTestMycCoreLazy.TestFuncLazy_FirstPop_AlwaysEvaluatesAndResetsChanged;
|
procedure TTestMycCoreLazy.TestFuncLazy_FirstPop_AlwaysEvaluatesAndResetsChanged;
|
||||||
var
|
var
|
||||||
funcLazy: IMycLazy<Integer>;
|
funcLazy: TLazy<Integer>.ILazy;
|
||||||
value: Integer;
|
value: Integer;
|
||||||
result: Boolean;
|
result: Boolean;
|
||||||
sourceDirty: TFlag.IFlag;
|
sourceDirty: TFlag.IFlag;
|
||||||
@@ -206,7 +206,7 @@ end;
|
|||||||
|
|
||||||
procedure TTestMycCoreLazy.TestFuncLazy_AfterFirstPop_IfNoSourceChange_GetChangedIsFalse;
|
procedure TTestMycCoreLazy.TestFuncLazy_AfterFirstPop_IfNoSourceChange_GetChangedIsFalse;
|
||||||
var
|
var
|
||||||
funcLazy: IMycLazy<Integer>;
|
funcLazy: TLazy<Integer>.ILazy;
|
||||||
sourceDirty: TFlag.IFlag;
|
sourceDirty: TFlag.IFlag;
|
||||||
initialProcValue: Integer;
|
initialProcValue: Integer;
|
||||||
begin
|
begin
|
||||||
@@ -220,7 +220,7 @@ end;
|
|||||||
|
|
||||||
procedure TTestMycCoreLazy.TestFuncLazy_AfterFirstPop_IfNoSourceChange_PopReturnsFalse_ValueUndefined;
|
procedure TTestMycCoreLazy.TestFuncLazy_AfterFirstPop_IfNoSourceChange_PopReturnsFalse_ValueUndefined;
|
||||||
var
|
var
|
||||||
funcLazy: IMycLazy<Integer>;
|
funcLazy: TLazy<Integer>.ILazy;
|
||||||
value: Integer;
|
value: Integer;
|
||||||
result: Boolean;
|
result: Boolean;
|
||||||
sourceDirty: TFlag.IFlag;
|
sourceDirty: TFlag.IFlag;
|
||||||
@@ -249,7 +249,7 @@ end;
|
|||||||
|
|
||||||
procedure TTestMycCoreLazy.TestFuncLazy_AfterSourceChange_GetChanged_IsSet;
|
procedure TTestMycCoreLazy.TestFuncLazy_AfterSourceChange_GetChanged_IsSet;
|
||||||
var
|
var
|
||||||
funcLazy: IMycLazy<Integer>;
|
funcLazy: TLazy<Integer>.ILazy;
|
||||||
changedState: TState.IState;
|
changedState: TState.IState;
|
||||||
sourceDirty: TFlag.IFlag;
|
sourceDirty: TFlag.IFlag;
|
||||||
initialProcValue: Integer;
|
initialProcValue: Integer;
|
||||||
@@ -266,7 +266,7 @@ end;
|
|||||||
|
|
||||||
procedure TTestMycCoreLazy.TestFuncLazy_AfterSourceChange_Pop_ReturnsTrueAndProcResult_ResetsChanged;
|
procedure TTestMycCoreLazy.TestFuncLazy_AfterSourceChange_Pop_ReturnsTrueAndProcResult_ResetsChanged;
|
||||||
var
|
var
|
||||||
funcLazy: IMycLazy<Integer>;
|
funcLazy: TLazy<Integer>.ILazy;
|
||||||
value: Integer;
|
value: Integer;
|
||||||
result: Boolean;
|
result: Boolean;
|
||||||
sourceDirty: TFlag.IFlag;
|
sourceDirty: TFlag.IFlag;
|
||||||
@@ -304,7 +304,7 @@ end;
|
|||||||
|
|
||||||
procedure TTestMycCoreLazy.TestFuncLazy_Pop_Twice_SourceUnchanged_SecondPopReturnsFalse_ValueUndefined;
|
procedure TTestMycCoreLazy.TestFuncLazy_Pop_Twice_SourceUnchanged_SecondPopReturnsFalse_ValueUndefined;
|
||||||
var
|
var
|
||||||
funcLazy: IMycLazy<Integer>;
|
funcLazy: TLazy<Integer>.ILazy;
|
||||||
value: Integer;
|
value: Integer;
|
||||||
resultPop1, resultPop2: Boolean;
|
resultPop1, resultPop2: Boolean;
|
||||||
sourceDirty: TFlag.IFlag;
|
sourceDirty: TFlag.IFlag;
|
||||||
@@ -336,7 +336,7 @@ end;
|
|||||||
// to TestFuncLazy_SourceInteraction_NotifyOnSetSourceDoesNotRetriggerLazy
|
// to TestFuncLazy_SourceInteraction_NotifyOnSetSourceDoesNotRetriggerLazy
|
||||||
procedure TTestMycCoreLazy.TestFuncLazy_SourceInteraction_NotifyOnSetSourceDoesNotRetriggerLazy;
|
procedure TTestMycCoreLazy.TestFuncLazy_SourceInteraction_NotifyOnSetSourceDoesNotRetriggerLazy;
|
||||||
var
|
var
|
||||||
funcLazy: IMycLazy<Integer>;
|
funcLazy: TLazy<Integer>.ILazy;
|
||||||
value: Integer;
|
value: Integer;
|
||||||
result: Boolean;
|
result: Boolean;
|
||||||
sourceDirty: TFlag.IFlag;
|
sourceDirty: TFlag.IFlag;
|
||||||
@@ -417,7 +417,7 @@ end;
|
|||||||
|
|
||||||
procedure TTestMycCoreLazy.TestFuncLazy_SourceIsInitiallySet_PopBehavesCorrectly;
|
procedure TTestMycCoreLazy.TestFuncLazy_SourceIsInitiallySet_PopBehavesCorrectly;
|
||||||
var
|
var
|
||||||
funcLazy: IMycLazy<Integer>;
|
funcLazy: TLazy<Integer>.ILazy;
|
||||||
value: Integer;
|
value: Integer;
|
||||||
result: Boolean;
|
result: Boolean;
|
||||||
sourceDirty: TFlag.IFlag;
|
sourceDirty: TFlag.IFlag;
|
||||||
|
|||||||
+16
-16
@@ -137,7 +137,7 @@ end;
|
|||||||
|
|
||||||
procedure TTestMyLazy.TestConstruct_InitialChanged_IsAlwaysTrue;
|
procedure TTestMyLazy.TestConstruct_InitialChanged_IsAlwaysTrue;
|
||||||
var
|
var
|
||||||
lazyIntf: IMycLazy<Integer>;
|
lazyIntf: TLazy<Integer>.ILazy;
|
||||||
lazyRec: TLazy<Integer>;
|
lazyRec: TLazy<Integer>;
|
||||||
procExecuted: Boolean;
|
procExecuted: Boolean;
|
||||||
begin
|
begin
|
||||||
@@ -161,7 +161,7 @@ end;
|
|||||||
|
|
||||||
procedure TTestMyLazy.TestConstruct_FirstPop_SucceedsAndResetsChanged;
|
procedure TTestMyLazy.TestConstruct_FirstPop_SucceedsAndResetsChanged;
|
||||||
var
|
var
|
||||||
lazyIntf: IMycLazy<Integer>;
|
lazyIntf: TLazy<Integer>.ILazy;
|
||||||
lazyRec: TLazy<Integer>;
|
lazyRec: TLazy<Integer>;
|
||||||
procExecuted: Boolean;
|
procExecuted: Boolean;
|
||||||
expectedValue: Integer;
|
expectedValue: Integer;
|
||||||
@@ -185,7 +185,7 @@ end;
|
|||||||
|
|
||||||
procedure TTestMyLazy.TestConstruct_StateInteraction_SignalTriggersChanged;
|
procedure TTestMyLazy.TestConstruct_StateInteraction_SignalTriggersChanged;
|
||||||
var
|
var
|
||||||
lazyIntf: IMycLazy<Integer>;
|
lazyIntf: TLazy<Integer>.ILazy;
|
||||||
lazyRec: TLazy<Integer>;
|
lazyRec: TLazy<Integer>;
|
||||||
expectedValue: Integer;
|
expectedValue: Integer;
|
||||||
begin
|
begin
|
||||||
@@ -203,7 +203,7 @@ end;
|
|||||||
|
|
||||||
procedure TTestMyLazy.TestConstruct_StateInteraction_PopResetsChangedAfterSignal;
|
procedure TTestMyLazy.TestConstruct_StateInteraction_PopResetsChangedAfterSignal;
|
||||||
var
|
var
|
||||||
lazyIntf: IMycLazy<Integer>;
|
lazyIntf: TLazy<Integer>.ILazy;
|
||||||
lazyRec: TLazy<Integer>;
|
lazyRec: TLazy<Integer>;
|
||||||
val: Integer;
|
val: Integer;
|
||||||
popResult: Boolean;
|
popResult: Boolean;
|
||||||
@@ -234,7 +234,7 @@ end;
|
|||||||
|
|
||||||
procedure TTestMyLazy.TestConstruct_StateInteraction_NotifyOnAlreadySetSource_DoesNotRetrigger;
|
procedure TTestMyLazy.TestConstruct_StateInteraction_NotifyOnAlreadySetSource_DoesNotRetrigger;
|
||||||
var
|
var
|
||||||
lazyIntf: IMycLazy<Integer>;
|
lazyIntf: TLazy<Integer>.ILazy;
|
||||||
lazyRec: TLazy<Integer>;
|
lazyRec: TLazy<Integer>;
|
||||||
val: Integer;
|
val: Integer;
|
||||||
popResult: Boolean;
|
popResult: Boolean;
|
||||||
@@ -280,7 +280,7 @@ end;
|
|||||||
|
|
||||||
procedure TTestMyLazy.TestConstruct_Destruction_UnsubscribesAndNoCrashOnSourceNotify;
|
procedure TTestMyLazy.TestConstruct_Destruction_UnsubscribesAndNoCrashOnSourceNotify;
|
||||||
var
|
var
|
||||||
lazyIntf: IMycLazy<Integer>;
|
lazyIntf: TLazy<Integer>.ILazy;
|
||||||
localChangingSignal: TFlag.IFlag; // Use a local signal for this test to control its lifetime
|
localChangingSignal: TFlag.IFlag; // Use a local signal for this test to control its lifetime
|
||||||
begin
|
begin
|
||||||
localChangingSignal := TFlag.CreateFlag;
|
localChangingSignal := TFlag.CreateFlag;
|
||||||
@@ -293,7 +293,7 @@ begin
|
|||||||
var lazyRec: TLazy<Integer> := lazyIntf; // Wrap for initial pop
|
var lazyRec: TLazy<Integer> := lazyIntf; // Wrap for initial pop
|
||||||
ConsumeInitialPop(lazyRec, 1, 'TestConstruct_Destruction (Initial)');
|
ConsumeInitialPop(lazyRec, 1, 'TestConstruct_Destruction (Initial)');
|
||||||
|
|
||||||
lazyIntf := nil; // Release the IMycLazy interface. ARC should destroy the TMycFuncLazy object.
|
lazyIntf := nil; // Release the ILazy interface. ARC should destroy the TMycFuncLazy object.
|
||||||
// This should trigger its destructor, which should unsubscribe from localChangingSignal.
|
// This should trigger its destructor, which should unsubscribe from localChangingSignal.
|
||||||
|
|
||||||
Assert.WillNotRaise(
|
Assert.WillNotRaise(
|
||||||
@@ -308,11 +308,11 @@ begin
|
|||||||
localChangingSignal := nil; // Clean up the local signal itself.
|
localChangingSignal := nil; // Clean up the local signal itself.
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// == Tests for TLazy<T>.Create with a pre-existing (non-nil) IMycLazy ==
|
// == Tests for TLazy<T>.Create with a pre-existing (non-nil) ILazy ==
|
||||||
|
|
||||||
procedure TTestMyLazy.TestCreateWithExistingLazy_DelegatesChangedCorrectly;
|
procedure TTestMyLazy.TestCreateWithExistingLazy_DelegatesChangedCorrectly;
|
||||||
var
|
var
|
||||||
originalLazyIntf: IMycLazy<Integer>;
|
originalLazyIntf: TLazy<Integer>.ILazy;
|
||||||
wrappedLazyRec: TLazy<Integer>;
|
wrappedLazyRec: TLazy<Integer>;
|
||||||
expectedValue: Integer;
|
expectedValue: Integer;
|
||||||
begin
|
begin
|
||||||
@@ -335,7 +335,7 @@ end;
|
|||||||
|
|
||||||
procedure TTestMyLazy.TestCreateWithExistingLazy_DelegatesPopCorrectly;
|
procedure TTestMyLazy.TestCreateWithExistingLazy_DelegatesPopCorrectly;
|
||||||
var
|
var
|
||||||
originalLazyIntf: IMycLazy<Integer>;
|
originalLazyIntf: TLazy<Integer>.ILazy;
|
||||||
wrappedLazyRec: TLazy<Integer>;
|
wrappedLazyRec: TLazy<Integer>;
|
||||||
val: Integer;
|
val: Integer;
|
||||||
popResult: Boolean;
|
popResult: Boolean;
|
||||||
@@ -376,7 +376,7 @@ end;
|
|||||||
|
|
||||||
procedure TTestMyLazy.TestImplicitOperator_FromInterfaceToRecord;
|
procedure TTestMyLazy.TestImplicitOperator_FromInterfaceToRecord;
|
||||||
var
|
var
|
||||||
lazyIntf: IMycLazy<Integer>;
|
lazyIntf: TLazy<Integer>.ILazy;
|
||||||
lazyRec: TLazy<Integer>;
|
lazyRec: TLazy<Integer>;
|
||||||
expectedValue: Integer;
|
expectedValue: Integer;
|
||||||
begin
|
begin
|
||||||
@@ -384,7 +384,7 @@ begin
|
|||||||
lazyIntf := TLazy<Integer>.Construct(FChangingSignal.State, function: Integer begin Result := expectedValue; end);
|
lazyIntf := TLazy<Integer>.Construct(FChangingSignal.State, function: Integer begin Result := expectedValue; end);
|
||||||
Assert.IsNotNull(lazyIntf, 'Interface should be assigned');
|
Assert.IsNotNull(lazyIntf, 'Interface should be assigned');
|
||||||
|
|
||||||
lazyRec := lazyIntf; // Implicit conversion: IMycLazy<T> to TLazy<T>
|
lazyRec := lazyIntf; // Implicit conversion: ILazy<T> to TLazy<T>
|
||||||
|
|
||||||
// Verify by using the record
|
// Verify by using the record
|
||||||
Assert.IsTrue(lazyRec.Changed.IsSet, 'Record (from intf): Initial Changed.IsSet should be true');
|
Assert.IsTrue(lazyRec.Changed.IsSet, 'Record (from intf): Initial Changed.IsSet should be true');
|
||||||
@@ -393,9 +393,9 @@ end;
|
|||||||
|
|
||||||
procedure TTestMyLazy.TestImplicitOperator_FromRecordToInterface;
|
procedure TTestMyLazy.TestImplicitOperator_FromRecordToInterface;
|
||||||
var
|
var
|
||||||
lazyIntfFromConstruct: IMycLazy<Integer>;
|
lazyIntfFromConstruct: TLazy<Integer>.ILazy;
|
||||||
lazyRec: TLazy<Integer>;
|
lazyRec: TLazy<Integer>;
|
||||||
lazyIntfFromRecord: IMycLazy<Integer>;
|
lazyIntfFromRecord: TLazy<Integer>.ILazy;
|
||||||
val: Integer;
|
val: Integer;
|
||||||
expectedValue: Integer;
|
expectedValue: Integer;
|
||||||
begin
|
begin
|
||||||
@@ -403,7 +403,7 @@ begin
|
|||||||
lazyIntfFromConstruct := TLazy<Integer>.Construct(FChangingSignal.State, function: Integer begin Result := expectedValue; end);
|
lazyIntfFromConstruct := TLazy<Integer>.Construct(FChangingSignal.State, function: Integer begin Result := expectedValue; end);
|
||||||
lazyRec.Create(lazyIntfFromConstruct); // Explicitly create record
|
lazyRec.Create(lazyIntfFromConstruct); // Explicitly create record
|
||||||
|
|
||||||
lazyIntfFromRecord := lazyRec; // Implicit conversion: TLazy<T> to IMycLazy<T>
|
lazyIntfFromRecord := lazyRec; // Implicit conversion: TLazy<T> to ILazy<T>
|
||||||
|
|
||||||
// Verify by using the converted interface
|
// Verify by using the converted interface
|
||||||
Assert.AreSame(lazyIntfFromConstruct, lazyIntfFromRecord, 'Converted interface should be the same as the original wrapped one');
|
Assert.AreSame(lazyIntfFromConstruct, lazyIntfFromRecord, 'Converted interface should be the same as the original wrapped one');
|
||||||
@@ -417,7 +417,7 @@ end;
|
|||||||
// == Tests for TLazy<T>.Pop specific behaviors (Res undefined) ==
|
// == Tests for TLazy<T>.Pop specific behaviors (Res undefined) ==
|
||||||
procedure TTestMyLazy.TestPop_AfterInitialAndNoSignal_ReturnsFalseAndResUndefined;
|
procedure TTestMyLazy.TestPop_AfterInitialAndNoSignal_ReturnsFalseAndResUndefined;
|
||||||
var
|
var
|
||||||
lazyIntf: IMycLazy<Integer>;
|
lazyIntf: TLazy<Integer>.ILazy;
|
||||||
lazyRec: TLazy<Integer>;
|
lazyRec: TLazy<Integer>;
|
||||||
val: Integer; // Value will not be checked as Pop returns false
|
val: Integer; // Value will not be checked as Pop returns false
|
||||||
popResult: Boolean;
|
popResult: Boolean;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ uses
|
|||||||
|
|
||||||
type
|
type
|
||||||
// Helper class to mock a subscriber (reuse or redefine if not in common unit)
|
// Helper class to mock a subscriber (reuse or redefine if not in common unit)
|
||||||
TMockSubscriber = class(TInterfacedObject, IMycSubscriber)
|
TMockSubscriber = class(TInterfacedObject, TSignal.ISubscriber)
|
||||||
public
|
public
|
||||||
NotifyCount: Integer;
|
NotifyCount: Integer;
|
||||||
NotifyShouldReturn: Boolean;
|
NotifyShouldReturn: Boolean;
|
||||||
@@ -17,7 +17,7 @@ type
|
|||||||
LastReturnedValueFromSource: Boolean; // To store what the source's Notify returned
|
LastReturnedValueFromSource: Boolean; // To store what the source's Notify returned
|
||||||
|
|
||||||
constructor Create(AShouldReturn: Boolean = True);
|
constructor Create(AShouldReturn: Boolean = True);
|
||||||
function Notify: Boolean; // IMycSubscriber implementation.
|
function Notify: Boolean; // TSignal.ISubscriber implementation.
|
||||||
end;
|
end;
|
||||||
|
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
@@ -164,7 +164,7 @@ var
|
|||||||
begin
|
begin
|
||||||
dirtyFlag := CreateAndPrepareDirtyFlag(False); // Start clean
|
dirtyFlag := CreateAndPrepareDirtyFlag(False); // Start clean
|
||||||
|
|
||||||
notifyResult := dirtyFlag.Notify; // Call IMycSubscriber.Notify to make it dirty
|
notifyResult := dirtyFlag.Notify; // Call TSignal.ISubscriber.Notify to make it dirty
|
||||||
|
|
||||||
Assert.IsTrue(dirtyFlag.State.IsSet, 'Flag should be dirty (IsSet) after Notify on clean flag.');
|
Assert.IsTrue(dirtyFlag.State.IsSet, 'Flag should be dirty (IsSet) after Notify on clean flag.');
|
||||||
Assert.IsTrue(notifyResult, 'Notify() should return True indicating a state change from clean to dirty.');
|
Assert.IsTrue(notifyResult, 'Notify() should return True indicating a state change from clean to dirty.');
|
||||||
@@ -177,7 +177,7 @@ var
|
|||||||
begin
|
begin
|
||||||
dirtyFlag := CreateAndPrepareDirtyFlag(True); // Start dirty
|
dirtyFlag := CreateAndPrepareDirtyFlag(True); // Start dirty
|
||||||
|
|
||||||
notifyResult := dirtyFlag.Notify; // Call IMycSubscriber.Notify again
|
notifyResult := dirtyFlag.Notify; // Call TSignal.ISubscriber.Notify again
|
||||||
|
|
||||||
Assert.IsTrue(dirtyFlag.State.IsSet, 'Flag should remain dirty (IsSet).');
|
Assert.IsTrue(dirtyFlag.State.IsSet, 'Flag should remain dirty (IsSet).');
|
||||||
Assert.IsFalse(notifyResult, 'Notify() should return False as flag was already dirty (no state change to dirty).');
|
Assert.IsFalse(notifyResult, 'Notify() should return False as flag was already dirty (no state change to dirty).');
|
||||||
@@ -303,7 +303,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
// Category 3: Chaining Dirty Flags
|
// Category 3: Chaining Dirty Flags
|
||||||
// In these tests, FlagX.Notify means calling the IMycSubscriber.Notify method on FlagX.
|
// In these tests, FlagX.Notify means calling the TSignal.ISubscriber.Notify method on FlagX.
|
||||||
// This makes FlagX dirty and potentially notifies its subscribers (like FlagY).
|
// This makes FlagX dirty and potentially notifies its subscribers (like FlagY).
|
||||||
|
|
||||||
procedure TTestMycDirtyFlag.TestChain_A_Notifies_B_SimplePropagation;
|
procedure TTestMycDirtyFlag.TestChain_A_Notifies_B_SimplePropagation;
|
||||||
|
|||||||
@@ -9,14 +9,14 @@ uses
|
|||||||
|
|
||||||
type
|
type
|
||||||
// Helper class to mock a subscriber.
|
// Helper class to mock a subscriber.
|
||||||
TMockSubscriber = class(TInterfacedObject, IMycSubscriber)
|
TMockSubscriber = class(TInterfacedObject, TSignal.ISubscriber)
|
||||||
public
|
public
|
||||||
NotifyCount: Integer;
|
NotifyCount: Integer;
|
||||||
NotifyShouldReturn: Boolean; // Determines what this mock's Notify method returns
|
NotifyShouldReturn: Boolean; // Determines what this mock's Notify method returns
|
||||||
WasCalled: Boolean;
|
WasCalled: Boolean;
|
||||||
|
|
||||||
constructor Create(AShouldReturn: Boolean = True); // Parameter name AShouldReturn for clarity
|
constructor Create(AShouldReturn: Boolean = True); // Parameter name AShouldReturn for clarity
|
||||||
function Notify: Boolean; // IMycSubscriber implementation.
|
function Notify: Boolean; // TSignal.ISubscriber implementation.
|
||||||
end;
|
end;
|
||||||
|
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
@@ -137,7 +137,7 @@ var
|
|||||||
returnedValueFromNotify: Boolean;
|
returnedValueFromNotify: Boolean;
|
||||||
begin
|
begin
|
||||||
latch := TLatch.CreateLatch(InitialCount);
|
latch := TLatch.CreateLatch(InitialCount);
|
||||||
returnedValueFromNotify := latch.Notify; // This is TLatch.ILatch (as IMycSubscriber).Notify
|
returnedValueFromNotify := latch.Notify; // This is TLatch.ILatch (as TSignal.ISubscriber).Notify
|
||||||
|
|
||||||
Assert.AreEqual(
|
Assert.AreEqual(
|
||||||
ExpectedReturn,
|
ExpectedReturn,
|
||||||
@@ -282,18 +282,18 @@ var
|
|||||||
subHandle_B_listens_A, subHandle_Mock_listens_B: TSignal.TSubscription;
|
subHandle_B_listens_A, subHandle_Mock_listens_B: TSignal.TSubscription;
|
||||||
begin
|
begin
|
||||||
latchA := TLatch.CreateLatch(1);
|
latchA := TLatch.CreateLatch(1);
|
||||||
latchB := TLatch.CreateLatch(1); // latchB is an IMycSubscriber
|
latchB := TLatch.CreateLatch(1); // latchB is an TSignal.ISubscriber
|
||||||
mockSubForB := TMockSubscriber.Create;
|
mockSubForB := TMockSubscriber.Create;
|
||||||
|
|
||||||
subHandle_Mock_listens_B := latchB.State.Subscribe(mockSubForB);
|
subHandle_Mock_listens_B := latchB.State.Subscribe(mockSubForB);
|
||||||
// LatchA's state is an IMycSignal. LatchB (as IMycSubscriber) subscribes to it.
|
// LatchA's state is an IMycSignal. LatchB (as TSignal.ISubscriber) subscribes to it.
|
||||||
subHandle_B_listens_A := latchA.State.Subscribe(latchB);
|
subHandle_B_listens_A := latchA.State.Subscribe(latchB);
|
||||||
|
|
||||||
Assert.IsFalse(latchA.State.IsSet, 'LatchA initially not set.');
|
Assert.IsFalse(latchA.State.IsSet, 'LatchA initially not set.');
|
||||||
Assert.IsFalse(latchB.State.IsSet, 'LatchB initially not set.');
|
Assert.IsFalse(latchB.State.IsSet, 'LatchB initially not set.');
|
||||||
Assert.AreEqual(0, mockSubForB.NotifyCount, 'MockSubForB initially not notified.');
|
Assert.AreEqual(0, mockSubForB.NotifyCount, 'MockSubForB initially not notified.');
|
||||||
|
|
||||||
latchA.Notify; // Call Notify on LatchA (as IMycSubscriber)
|
latchA.Notify; // Call Notify on LatchA (as TSignal.ISubscriber)
|
||||||
|
|
||||||
Assert.IsTrue(latchA.State.IsSet, 'LatchA should be set after notify.');
|
Assert.IsTrue(latchA.State.IsSet, 'LatchA should be set after notify.');
|
||||||
Assert.IsTrue(latchB.State.IsSet, 'LatchB should be set after being notified by LatchA.');
|
Assert.IsTrue(latchB.State.IsSet, 'LatchB should be set after being notified by LatchA.');
|
||||||
|
|||||||
@@ -14,13 +14,13 @@ uses
|
|||||||
|
|
||||||
type
|
type
|
||||||
// Helper class to notify a latch when its Notify method is called.
|
// Helper class to notify a latch when its Notify method is called.
|
||||||
TLatchNotifierSubscriber = class(TInterfacedObject, IMycSubscriber)
|
TLatchNotifierSubscriber = class(TInterfacedObject, TSignal.ISubscriber)
|
||||||
private
|
private
|
||||||
FGateLatch: TLatch.ILatch;
|
FGateLatch: TLatch.ILatch;
|
||||||
public
|
public
|
||||||
constructor Create(AGateLatch: TLatch.ILatch);
|
constructor Create(AGateLatch: TLatch.ILatch);
|
||||||
// IMycSubscriber
|
// TSignal.ISubscriber
|
||||||
function Notify: Boolean; // Implements IMycSubscriber.Notify [cite: 46, 181, 299]
|
function Notify: Boolean; // Implements TSignal.ISubscriber.Notify [cite: 46, 181, 299]
|
||||||
end;
|
end;
|
||||||
|
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
@@ -251,7 +251,7 @@ var
|
|||||||
LPrerequisiteFuture1, LPrerequisiteFuture2: TFuture<Integer>.IFuture;
|
LPrerequisiteFuture1, LPrerequisiteFuture2: TFuture<Integer>.IFuture;
|
||||||
LMainFuture: TFuture<string>.IFuture;
|
LMainFuture: TFuture<string>.IFuture;
|
||||||
LGateLatch: TLatch.ILatch;
|
LGateLatch: TLatch.ILatch;
|
||||||
LSub1, LSub2: IMycSubscriber; // To hold the TLatchNotifierSubscriber instances
|
LSub1, LSub2: TSignal.ISubscriber; // To hold the TLatchNotifierSubscriber instances
|
||||||
LInitStateP1, LInitStateP2: TLatch.ILatch;
|
LInitStateP1, LInitStateP2: TLatch.ILatch;
|
||||||
Subscriptions: array[1..2] of TSignal.TSubscription; // To manage subscriptions
|
Subscriptions: array[1..2] of TSignal.TSubscription; // To manage subscriptions
|
||||||
const
|
const
|
||||||
|
|||||||
+2
-2
@@ -108,7 +108,7 @@ procedure TMycTaskFactoryTests.TestRunDelayedJobExecutesAfterAllNotifies;
|
|||||||
var
|
var
|
||||||
jobExecuted: Boolean;
|
jobExecuted: Boolean;
|
||||||
jobCompletedLatch: TLatch.ILatch;
|
jobCompletedLatch: TLatch.ILatch;
|
||||||
subscriber: IMycSubscriber;
|
subscriber: TSignal.ISubscriber;
|
||||||
begin
|
begin
|
||||||
jobExecuted := False;
|
jobExecuted := False;
|
||||||
jobCompletedLatch := TLatch.CreateLatch(1); // Changed from Signals.CreateLatch
|
jobCompletedLatch := TLatch.CreateLatch(1); // Changed from Signals.CreateLatch
|
||||||
@@ -124,7 +124,7 @@ begin
|
|||||||
|
|
||||||
Assert.IsNotNull(subscriber, 'Run should return a subscriber for delayed job');
|
Assert.IsNotNull(subscriber, 'Run should return a subscriber for delayed job');
|
||||||
// Use TMycLatch.Null for comparison
|
// Use TMycLatch.Null for comparison
|
||||||
Assert.AreNotEqual<IMycSubscriber>(TLatch.Null, subscriber, 'Subscriber should not be TMycLatch.Null for StartCount > 0');
|
Assert.AreNotEqual<TSignal.ISubscriber>(TLatch.Null, subscriber, 'Subscriber should not be TMycLatch.Null for StartCount > 0');
|
||||||
|
|
||||||
subscriber.Notify;
|
subscriber.Notify;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user