XPObserverTests.pas
上传用户:yjb1804
上传日期:2021-01-30
资源大小:3105k
文件大小:37k
源码类别:

Email服务器

开发平台:

Delphi

  1. unit XPObserverTests;
  2. interface
  3. uses
  4.   XPObserver,
  5.   TestFrameWork;
  6. type
  7.   IXPCrackedObserver = interface(IXPObserver)
  8.     ['{2523055E-E109-44E8-8A27-1663E0747493}']
  9.     function RefCount: integer;
  10.     procedure SetSubject(const Subject: IXPSubject);
  11.     function GetSubject: IXPSubject;
  12.     property Subject: IXPSubject
  13.       read GetSubject write SetSubject;
  14.   end;
  15.   IXPCrackedSubject = interface(IXPSubject)
  16.     ['{C469C949-3B53-4E5D-836F-5BE5A7F81718}']
  17.     function RefCount: integer;
  18.   end;
  19.   IXPCrackedSubjects = interface(IXPSubjects)
  20.     ['{26D4DFF5-2326-4AD0-9C9F-6D8251B1316D}']
  21.     function RefCount: integer;
  22.   end;
  23.   IXPCrackedParent = interface(IXPFamily)
  24.     ['{04FE35A5-8C4A-4230-9D01-3F480EB89454}']
  25.     function RefCount: integer;
  26.   end;
  27.   TXPSubjectsTests = class(TTestCase)
  28.   private
  29.     FSubject: IXPCrackedSubject;
  30.     FSubject2: IXPCrackedSubject;
  31.     FSubject3: IXPCrackedSubject;
  32.     FSubject4: IXPCrackedSubject;
  33.   protected
  34.     procedure SetUp; override;
  35.     procedure TearDown; override;
  36.   published
  37.     // Test methods
  38.     procedure TestAddSubject;
  39.     procedure TestDeleteSubject;
  40.     procedure TestClear;
  41.     procedure TestCreate;
  42.   end;
  43. type
  44.   TXPSubjectTests = class(TTestCase)
  45.   private
  46.     FObserver: IXPCrackedObserver;
  47.     FObserver2: IXPCrackedObserver;
  48.     FObserver3: IXPCrackedObserver;
  49.     FObserver4: IXPCrackedObserver;
  50.   protected
  51.     procedure SetUp; override;
  52.     procedure TearDown; override;
  53.   published
  54.     // Test methods
  55.     procedure TestObserverCount;
  56.     procedure TestAddObserver;
  57.     procedure TestInsertObserver;
  58.     procedure TestDeleteObserver;
  59.     procedure TestDeleteObservers;
  60.     procedure TestCreate;
  61.     procedure TestGetObserver;
  62.   end;
  63. type
  64.   TXPParentTests = class(TTestCase)
  65.   private
  66.     FParent: IXPCrackedParent;
  67.   protected
  68.     procedure SetUp; override;
  69.     procedure TearDown; override;
  70.   published
  71.     // Test methods
  72.     procedure TestReleaseSubject;
  73.     procedure TestAccessParent;
  74.     procedure TestCreate;
  75.   end;
  76. implementation
  77. uses
  78.   SysUtils;
  79. type
  80.   TCrackedObserver = class (TInterfacedObject, IXPObserver, IXPCrackedObserver)
  81.   private
  82.     FSubject: IXPSubject;
  83.   protected
  84.     function RefCount: integer;
  85.     procedure SetSubject(const Subject: IXPSubject);
  86.     function GetSubject: IXPSubject;
  87.     procedure ReleaseSubject(const Subject: IXPSubject;
  88.       const Context: pointer);
  89.   public
  90.      destructor Destroy; override;
  91.   end;
  92.   TCrackedSubject = class (TXPSubject, IXPCrackedSubject)
  93.   protected
  94.     function RefCount: integer;
  95.   end;
  96.   TCrackedSubjects = class (TXPSubjects, IXPCrackedSubjects)
  97.   protected
  98.     function RefCount: integer;
  99.   public
  100.     destructor Destroy; override;
  101.   end;
  102.   TCrackedParent = class (TXPFamily, IXPCrackedParent)
  103.   protected
  104.     function RefCount: integer;
  105.   end;
  106. { TXPSubjectsTests }
  107. procedure TXPSubjectsTests.SetUp;
  108. begin
  109.   inherited;
  110.   FSubject := TCrackedSubject.Create;
  111.   FSubject2 := TCrackedSubject.Create;
  112.   FSubject3 := TCrackedSubject.Create;
  113.   FSubject4 := TCrackedSubject.Create;
  114. end;
  115. procedure TXPSubjectsTests.TearDown;
  116. begin
  117.   FSubject := nil;
  118.   FSubject2 := nil;
  119.   FSubject3 := nil;
  120.   FSubject4 := nil;
  121.   inherited;
  122. end;
  123. procedure TXPSubjectsTests.TestAddSubject;
  124. var
  125.   Subjects: IXPCrackedSubjects;
  126. begin
  127.   Subjects := TCrackedSubjects.Create;
  128.   CheckEquals(1, Subjects.RefCount,
  129.     'subjects rc after clear after construction');
  130.   Check(not Subjects.AddSubject(nil), 'addsubject with nil argument');
  131.   CheckEquals(1, FSubject.RefCount, 'subject 1 rc before addition');
  132.   CheckEquals(1, FSubject2.RefCount, 'subject 2 rc before addition');
  133.   CheckEquals(1, FSubject3.RefCount, 'subject 3 rc before addition');
  134.   CheckEquals(1, FSubject4.RefCount, 'subject 4 rc before addition');
  135.   Check(Subjects.AddSubject(@FSubject), 'subject 1 addsubject');
  136.   CheckEquals(1, FSubject.RefCount, 'subject 1 rc after addition');
  137.   CheckEquals(2, Subjects.RefCount, 'subjects rc after subject 1 addition');
  138.   Check(Subjects.AddSubject(@FSubject2), 'subject 2 addsubject');
  139.   CheckEquals(1, FSubject2.RefCount, 'subject 2 rc after addition');
  140.   CheckEquals(3, Subjects.RefCount, 'subjects rc after subject 2 addition');
  141.   Check(Subjects.AddSubject(@FSubject3), 'subject 3 addsubject');
  142.   CheckEquals(1, FSubject3.RefCount, 'subject 3 rc after addition');
  143.   CheckEquals(4, Subjects.RefCount, 'subjects rc after subject 3 addition');
  144.   Check(Subjects.AddSubject(@FSubject4), 'subject 4 addsubject');
  145.   CheckEquals(1, FSubject4.RefCount, 'subject 4 rc after addition');
  146.   CheckEquals(5, Subjects.RefCount, 'subjects rc after subject 4 addition');
  147.   Subjects.Clear;
  148.   CheckEquals(1, Subjects.RefCount, 'subjects rc after clear on 4 subjects');
  149.   Check(FSubject = nil, 'subject 1 nil''d after clearing');
  150.   Check(FSubject2 = nil, 'subject 2 nil''d after clearing');
  151.   Check(FSubject3 = nil, 'subject 3 nil''d after clearing');
  152.   Check(FSubject4 = nil, 'subject 4 nil''d after clearing');
  153. end;
  154. procedure TXPSubjectsTests.TestClear;
  155. var
  156.   Subjects: IXPCrackedSubjects;
  157.   ACopy: IXPCrackedSubject;
  158. begin
  159.   Subjects := TCrackedSubjects.Create;
  160.   Subjects.Clear;
  161.   CheckEquals(1, Subjects.RefCount,
  162.     'subjects rc after clear after construction');
  163.   CheckEquals(1, FSubject.RefCount, 'subject 1 rc before addition');
  164.   Check(Subjects.AddSubject(@FSubject), 'first addsubject');
  165.   CheckEquals(1, FSubject.RefCount, 'subject 1 rc after addition');
  166.   CheckEquals(2, Subjects.RefCount, 'subjects rc after first addition');
  167.   ACopy := FSubject;
  168.   CheckEquals(2, FSubject.RefCount, 'subject 1 rc after copy');
  169.   CheckEquals(2, ACopy.RefCount, 'acopy rc after copy');
  170.   Subjects.Clear;
  171.   CheckEquals(1, Subjects.RefCount, 'subjects rc after clear on one subject');
  172.   Check(FSubject = nil, 'subject 1 nil''d after clearing');
  173.   CheckEquals(1, ACopy.RefCount, 'acopy rc after clearing');
  174. end;
  175. procedure TXPSubjectsTests.TestCreate;
  176. var
  177.   Subjects: IXPCrackedSubjects;
  178. begin
  179.   Subjects := TCrackedSubjects.Create;
  180.   CheckEquals(1, Subjects.RefCount, 'subjects rc after construction');
  181. end;
  182. procedure TXPSubjectsTests.TestDeleteSubject;
  183. var
  184.   Subjects: IXPCrackedSubjects;
  185.   ACopy: PInterface;
  186. begin
  187.   Subjects := TCrackedSubjects.Create;
  188.   CheckEquals(1, Subjects.RefCount,
  189.     'subjects rc after clear after construction');
  190.   Check(not Subjects.DeleteSubject(nil),
  191.     'deletesubject on empty subjects with nil argument');
  192.   Check(not Subjects.DeleteSubject(@FSubject2),
  193.     'deletesubject on empty subjects with non-nil but invalid argument');
  194.   CheckEquals(1, FSubject.RefCount, 'subject 1 rc before addition');
  195.   CheckEquals(1, FSubject2.RefCount, 'subject 2 rc before addition');
  196.   CheckEquals(1, FSubject3.RefCount, 'subject 3 rc before addition');
  197.   CheckEquals(1, FSubject4.RefCount, 'subject 4 rc before addition');
  198.   Check(Subjects.AddSubject(@FSubject), 'subject 1 addsubject');
  199.   CheckEquals(1, FSubject.RefCount, 'subject 1 rc after addition');
  200.   CheckEquals(2, Subjects.RefCount, 'subjects rc after subject 1 addition');
  201.   Check(not Subjects.DeleteSubject(nil),
  202.     'deletesubject on non-empty subjects with nil argument');
  203.   Check(not Subjects.DeleteSubject(@FSubject2),
  204.     'deletesubject on non-empty subjects with non-nil but invalid argument');
  205.   ACopy := @FSubject;
  206.   Check(Subjects.DeleteSubject(@FSubject),
  207.     'deletesubject 1 on non-empty subjects with valid argument');
  208.   CheckEquals(1, Subjects.RefCount, 'subjects rc after subject 1 deletion');
  209.   Check(not Subjects.DeleteSubject(ACopy),
  210.     'deletesubject 1 (again) on now empty subjects with now invalid argument');
  211.   CheckEquals(1, Subjects.RefCount,
  212.     'subjects rc after attempted subject 1 re-deletion');
  213.   Check(Subjects.AddSubject(@FSubject2), 'subject 2 addsubject');
  214.   CheckEquals(1, FSubject2.RefCount, 'subject 2 rc after addition');
  215.   CheckEquals(2, Subjects.RefCount, 'subjects rc after subject 2 addition');
  216.   Check(Subjects.AddSubject(@FSubject3), 'subject 3 addsubject');
  217.   CheckEquals(1, FSubject3.RefCount, 'subject 3 rc after addition');
  218.   CheckEquals(3, Subjects.RefCount, 'subjects rc after subject 3 addition');
  219.   Check(Subjects.AddSubject(@FSubject4), 'subject 4 addsubject');
  220.   CheckEquals(1, FSubject4.RefCount, 'subject 4 rc after addition');
  221.   CheckEquals(4, Subjects.RefCount, 'subjects rc after subject 4 addition');
  222.   Check(Subjects.DeleteSubject(@FSubject3),
  223.     'deletesubject 3 (middle element)');
  224.   Check(FSubject3 = nil, 'subject 3 nil''d after clearing');
  225.   CheckEquals(3, Subjects.RefCount, 'subjects rc after subject 3 deleted');
  226.   Check(Subjects.DeleteSubject(@FSubject4), 'deletesubject 4 (end element)');
  227.   Check(FSubject4 = nil, 'subject 4 nil''d after clearing');
  228.   CheckEquals(2, Subjects.RefCount, 'subjects rc after subject 4 deleted');
  229.   Check(Subjects.DeleteSubject(@FSubject2), 'deletesubject 2 (end element)');
  230.   Check(FSubject2 = nil, 'subject 2 nil''d after clearing');
  231.   CheckEquals(1, Subjects.RefCount, 'subjects rc after subject 2 deleted');
  232. end;
  233. { TXPSubjectTests }
  234. procedure TXPSubjectTests.TestAddObserver;
  235. var
  236.   ASubject: IXPCrackedSubject;
  237. begin
  238.   ASubject := TCrackedSubject.Create;
  239.   CheckEquals(0, ASubject.ObserverCount,
  240.     'empty observer count on construction');
  241.   CheckEquals(0, ASubject.Count, 'empty count on construction');
  242.   FObserver.Subject := ASubject;
  243.   Check(ASubject.AddObserver(FObserver, ASubject), 'adding observer');
  244.   CheckEquals(2, ASubject.RefCount, 'subject rc after first observer');
  245.   CheckEquals(2, FObserver.RefCount, 'observer rc after acquiring subject');
  246.   CheckEquals(1, ASubject.ObserverCount, 'observer count after first observer');
  247.   ASubject := nil;
  248.   CheckEquals(1, FObserver.RefCount, 'observer rc after subject is destroyed');
  249.   // go round again - try to add observer a second time
  250.   ASubject := TCrackedSubject.Create;
  251.   CheckEquals(0, ASubject.ObserverCount,
  252.     '2: empty observer count on construction');
  253.   FObserver.Subject := ASubject;
  254.   Check(ASubject.AddObserver(FObserver, ASubject), '2: adding observer');
  255.   CheckEquals(2, ASubject.RefCount, '2: subject rc after first observer');
  256.   CheckEquals(2, FObserver.RefCount, '2: observer rc after acquiring subject');
  257.   CheckEquals(1, ASubject.ObserverCount,
  258.     '2: observer count after first observer');
  259.   Check(not ASubject.AddObserver(FObserver, ASubject),
  260.     '2: adding observer again');
  261.   CheckEquals(2, ASubject.RefCount,
  262.     '2: subject rc after first observer added again');
  263.   CheckEquals(2, FObserver.RefCount,
  264.     '2: observer rc after attempting to add itself again');
  265.   CheckEquals(1, ASubject.ObserverCount,
  266.     '2: observer count after first observer added again');
  267.   ASubject := nil;
  268.   CheckEquals(1, FObserver.RefCount, 'observer rc after subject is destroyed');
  269. end;
  270. procedure TXPSubjectTests.TestCreate;
  271. var
  272.   ASubject: IXPCrackedSubject;
  273. begin
  274.   ASubject := TCrackedSubject.Create;
  275.   CheckEquals(1, ASubject.RefCount, 'sole reference on construction');
  276. end;
  277. procedure TXPSubjectTests.TestDeleteObserver;
  278. var
  279.   ASubject: IXPCrackedSubject;
  280. begin
  281.   ASubject := TCrackedSubject.Create;
  282.   CheckEquals(1, ASubject.RefCount, 'sole reference on construction');
  283.   CheckEquals(0, ASubject.Count, 'empty observer count on construction');
  284.   Check(not ASubject.DeleteObserver(nil), 'delete nil observer on empty');
  285.   FObserver.Subject := ASubject;
  286.   CheckEquals(2, ASubject.RefCount, 'subject rc after assignment');
  287.   Check(ASubject.InsertObserver(0, FObserver, ASubject),
  288.     'insert observer into subject');
  289.   CheckEquals(2, FObserver.RefCount, 'observer rc after registration');
  290.   Check(not ASubject.DeleteObserver(nil), 'delete nil observer on non-empty');
  291.   CheckEquals(2, FObserver.RefCount, 'observer rc after delete nil observer');
  292.   Check(not ASubject.DeleteObserver(FObserver, pointer(1)),
  293.     'delete observer with wrong context');
  294.   CheckEquals(2, FObserver.RefCount,
  295.     'observer rc after delete observer with wrong context');
  296.   Check(ASubject.DeleteObserver(FObserver),
  297.     'delete observer with right context');
  298.   CheckEquals(1, FObserver.RefCount,
  299.     'observer rc after delete observer with right context');
  300.   CheckEquals(1, ASubject.RefCount,
  301.     'subject rc after delete observer callback');
  302.   Check(not ASubject.DeleteObserver(FObserver),
  303.     'delete deleted observer with right context');
  304.   Check(not ASubject.DeleteObserver(nil),
  305.     'delete nil observer on empty after filling');
  306. end;
  307. procedure TXPSubjectTests.TestDeleteObservers;
  308. var
  309.   ASubject: IXPCrackedSubject;
  310. begin
  311.   ASubject := TCrackedSubject.Create;
  312.   CheckEquals(1, ASubject.RefCount, 'sole reference on construction');
  313.   CheckEquals(0, ASubject.Count, 'empty observer count on construction');
  314.   ASubject.DeleteObservers;
  315.   CheckEquals(1, ASubject.RefCount,
  316.     'rc after delete observers without observers');
  317.   CheckEquals(0, ASubject.Count,
  318.     'observer count after delete observers without observers');
  319.   FObserver.Subject := ASubject;
  320.   Check(ASubject.InsertObserver(0, FObserver, ASubject),
  321.     'insert observer into subject');
  322.   CheckEquals(2, FObserver.RefCount,
  323.     'observer rc after registration');
  324.   FObserver2.Subject := ASubject;
  325.   Check(ASubject.InsertObserver(0, FObserver2, ASubject),
  326.     'insert observer 2 into subject');
  327.   CheckEquals(2, FObserver2.RefCount, 'observer 2 rc after registration');
  328.   FObserver3.Subject := ASubject;
  329.   Check(ASubject.InsertObserver(0, FObserver3, ASubject),
  330.     'insert observer 3 into subject');
  331.   CheckEquals(2, FObserver3.RefCount, 'observer 3 rc after registration');
  332.   CheckEquals(3, ASubject.Count, 'observer count after 3 registrations');
  333.   CheckEquals(4, ASubject.RefCount, 'subject rc after 3 registrations');
  334.   ASubject.DeleteObservers;
  335.   CheckEquals(0, ASubject.Count, 'observer count after delete observers');
  336.   CheckEquals(1, ASubject.RefCount, 'subject rc after delete observers');
  337.   CheckEquals(1, FObserver.RefCount, 'observer rc after delete observers');
  338.   CheckEquals(1, FObserver2.RefCount, 'observer 2 rc after delete observers');
  339.   CheckEquals(1, FObserver3.RefCount, 'observer 3 rc after delete observers');
  340. end;
  341. procedure TXPSubjectTests.TestInsertObserver;
  342. var
  343.   ASubject: IXPCrackedSubject;
  344. begin
  345.   ASubject := TCrackedSubject.Create;
  346.   CheckEquals(1, ASubject.RefCount, 'sole reference on construction');
  347.   CheckEquals(0, ASubject.Count, 'empty observer count on construction');
  348.   CheckEquals(1, FObserver.RefCount, 'rc observer on construction');
  349.   FObserver.Subject := ASubject;
  350.   CheckEquals(2, ASubject.RefCount, 'subject rc after assignment');
  351.   CheckEquals(1, FObserver.RefCount, 'observer rc after assignment');
  352.   Check(not ASubject.InsertObserver(-1, FObserver, ASubject),
  353.     'insert lower bound error');
  354.   Check(not ASubject.InsertObserver(1, FObserver, ASubject),
  355.     'insert upper bound error');
  356.   Check(ASubject.InsertObserver(0, FObserver, ASubject),
  357.     'insert into empty subject');
  358.   CheckEquals(1, ASubject.Count, 'observer count after registration');
  359.   CheckEquals(2, FObserver.RefCount, 'observer rc after registration');
  360.   FObserver2.Subject := ASubject;
  361.   CheckEquals(3, ASubject.RefCount, 'subject rc after second assignment');
  362.   CheckEquals(1, FObserver2.RefCount, 'observer2 rc after assignment');
  363.   Check(not ASubject.InsertObserver(-1, FObserver2, ASubject),
  364.     'insert 2 lower bound error');
  365.   Check(not ASubject.InsertObserver(2, FObserver2, ASubject),
  366.     'insert 2 upper bound error');
  367.   Check(ASubject.InsertObserver(0, FObserver2, ASubject),
  368.     'insert into start of non-empty subject');
  369.   CheckEquals(2, ASubject.Count, 'observer count after second registration');
  370.   CheckEquals(2, FObserver2.RefCount, 'observer2 rc after registration');
  371.   FObserver3.Subject := ASubject;
  372.   CheckEquals(4, ASubject.RefCount, 'subject rc after third assignment');
  373.   CheckEquals(1, FObserver3.RefCount, 'observer3 rc after assignment');
  374.   Check(not ASubject.InsertObserver(-1, FObserver3, ASubject),
  375.     'insert 3 lower bound error');
  376.   Check(not ASubject.InsertObserver(3, FObserver3, ASubject),
  377.     'insert 3 upper bound error');
  378.   Check(ASubject.InsertObserver(2, FObserver3, ASubject),
  379.     'insert into end of non-empty subject');
  380.   CheckEquals(3, ASubject.Count,
  381.     'observer count after third registration');
  382.   CheckEquals(2, FObserver2.RefCount, 'observer3 rc after registration');
  383.   FObserver4.Subject := ASubject;
  384.   CheckEquals(5, ASubject.RefCount, 'subject rc after fourth assignment');
  385.   CheckEquals(1, FObserver4.RefCount, 'observer4 rc after assignment');
  386.   Check(not ASubject.InsertObserver(-1, FObserver4, ASubject),
  387.     'insert 4 lower bound error');
  388.   Check(not ASubject.InsertObserver(4, FObserver4, ASubject),
  389.     'insert 4 upper bound error');
  390.   Check(ASubject.InsertObserver(2, FObserver4, ASubject),
  391.     'insert into middle of non-empty subject');
  392.   CheckEquals(4, ASubject.Count,
  393.     'observer count after fourth registration');
  394.   CheckEquals(2, FObserver4.RefCount, 'observer4 rc after registration');
  395.   Check(not ASubject.InsertObserver(-1, FObserver3, ASubject),
  396.     'insert 5 lower bound error with current member');
  397.   Check(not ASubject.InsertObserver(5, FObserver3, ASubject),
  398.     'insert 5 upper bound error with current member');
  399.   Check(not ASubject.InsertObserver(0, FObserver3, ASubject),
  400.     'insert 5 at 0 with found current member');
  401.   Check(not ASubject.InsertObserver(1, FObserver3, ASubject),
  402.     'insert 5 at 1 with found current member');
  403.   Check(not ASubject.InsertObserver(2, FObserver3, ASubject),
  404.     'insert 5 at 2 with found current member');
  405.   Check(not ASubject.InsertObserver(3, FObserver3, ASubject),
  406.     'insert 5 at 3 with found current member');
  407.   Check(not ASubject.InsertObserver(4, FObserver3, ASubject),
  408.     'insert 5 at 4 with found current member');
  409.   ASubject := nil;
  410.   CheckEquals(1, FObserver.RefCount, 'observer 1 rc after subject nil''d');
  411.   CheckEquals(1, FObserver2.RefCount, 'observer 2 rc after subject nil''d');
  412.   CheckEquals(1, FObserver3.RefCount, 'observer 3 rc after subject nil''d');
  413.   CheckEquals(1, FObserver4.RefCount, 'observer 4 rc after subject nil''d');
  414. end;
  415. procedure TXPSubjectTests.TestObserverCount;
  416. var
  417.   ASubject: IXPCrackedSubject;
  418. begin
  419.   ASubject := TCrackedSubject.Create;
  420.   CheckEquals(0, ASubject.ObserverCount,
  421.     'empty observer count on construction');
  422.   CheckEquals(1, FObserver.RefCount, 'observer rc before acquiring subject');
  423.   FObserver.Subject := ASubject;
  424.   Check(ASubject.AddObserver(FObserver, ASubject),
  425.     '1: adding first observer to subject') ;
  426.   CheckEquals(2, ASubject.RefCount, '1: subject rc after first observer');
  427.   CheckEquals(2, FObserver.RefCount, '1: observer rc after acquiring subject');
  428.   CheckEquals(1, ASubject.ObserverCount,
  429.     '1: observer count after first observer');
  430.   CheckEquals(1, FObserver2.RefCount,
  431.     '1: observer rc before acquiring subject');
  432.   FObserver2.Subject := ASubject;
  433.   Check(ASubject.AddObserver(FObserver2, ASubject),
  434.     '1: adding second observer to subject');
  435.   CheckEquals(3, ASubject.RefCount, '1: subject rc after second observer');
  436.   CheckEquals(2, FObserver2.RefCount,
  437.     '1: second observer rc after acquiring subject');
  438.   CheckEquals(2, ASubject.ObserverCount,
  439.     '1: observer count after second observer');
  440.   Check(ASubject.DeleteObserver(FObserver2),
  441.     '1: deleting second observer from subject');
  442.   CheckEquals(2, ASubject.RefCount,
  443.     '1: subject rc after deleting second observer');
  444.   CheckEquals(1, FObserver2.RefCount,
  445.     '1: second observer rc after detaching from subject');
  446.   CheckEquals(1, ASubject.ObserverCount,
  447.     '1: observer count after deleting second observer');
  448.   CheckEquals(2, FObserver.RefCount,
  449.     '1: observer rc after detaching first observer from subject');
  450.   Check(ASubject.DeleteObserver(FObserver),
  451.     '1: deleting second observer from subject');
  452.   CheckEquals(1, ASubject.RefCount,
  453.     '1: subject rc after deleting first observer');
  454.   CheckEquals(1, FObserver.RefCount,
  455.     '1: first observer rc after detaching from subject');
  456.   CheckEquals(0, ASubject.ObserverCount,
  457.     '1: observer count after deleting first observer');
  458.   CheckEquals(1, FObserver2.RefCount,
  459.     '1: second observer rc after first observer detached from subject');
  460.   // Repeat but reverse removal order
  461.   FObserver.Subject := ASubject;
  462.   Check(ASubject.AddObserver(FObserver, ASubject),
  463.     '2: adding first observer to subject');
  464.   CheckEquals(2, ASubject.RefCount, '2: subject rc after first observer');
  465.   CheckEquals(2, FObserver.RefCount, '2: observer rc after acquiring subject');
  466.   CheckEquals(1, ASubject.ObserverCount,
  467.     '2: observer count after first observer');
  468.   CheckEquals(1, FObserver2.RefCount,
  469.     '2: observer rc before acquiring subject');
  470.   FObserver2.Subject := ASubject;
  471.   Check(ASubject.AddObserver(FObserver2, ASubject),
  472.     '2: adding second observer to subject');
  473.   CheckEquals(3, ASubject.RefCount, '2: subject rc after second observer');
  474.   CheckEquals(2, FObserver2.RefCount,
  475.     '2: second observer rc after acquiring subject');
  476.   CheckEquals(2, ASubject.ObserverCount,
  477.     '2: observer count after second observer');
  478.   Check(ASubject.DeleteObserver(FObserver),
  479.     '2: deleting first observer from subject');
  480.   CheckEquals(2, ASubject.RefCount,
  481.     '2: subject rc after deleting first observer');
  482.   CheckEquals(1, FObserver.RefCount,
  483.     '2: observer rc after detaching from subject');
  484.   CheckEquals(1, ASubject.ObserverCount,
  485.     '2: observer count after deleting first observer');
  486.   CheckEquals(2, FObserver2.RefCount,
  487.     '2: second observer rc after first observer detached from subject');
  488.   Check(ASubject.DeleteObserver(FObserver2),
  489.     '2: deleting second observer from subject');
  490.   CheckEquals(1, ASubject.RefCount,
  491.     '2: subject rc after deleting second observer');
  492.   CheckEquals(1, FObserver2.RefCount,
  493.     '2: second observer rc after detaching from subject');
  494.   CheckEquals(0, ASubject.ObserverCount,
  495.     '2: observer count after deleting second observer');
  496.   CheckEquals(1, FObserver.RefCount,
  497.     '2: first observer rc after detaching second observer from subject');
  498.   // Try repeated deletion
  499.   Check(not ASubject.DeleteObserver(FObserver2),
  500.     '2: deleting second observer (again) from subject');
  501.   CheckEquals(1, ASubject.RefCount,
  502.     '2: subject rc after second attempt to delete second observer');
  503.   CheckEquals(1, FObserver2.RefCount,
  504.     '2: second observer rc after second attempt to detach from subject');
  505.   CheckEquals(0, ASubject.ObserverCount,
  506.     '2: observer count after attempting  second deletion of second observer');
  507.   CheckEquals(1, FObserver.RefCount,
  508.     '2: observer rc after attempting to detach second observer from subject');
  509. end;
  510. procedure TXPSubjectTests.SetUp;
  511. begin
  512.   inherited;
  513.   FObserver := TCrackedObserver.Create;
  514.   FObserver2 := TCrackedObserver.Create;
  515.   FObserver3 := TCrackedObserver.Create;
  516.   FObserver4 := TCrackedObserver.Create;
  517. end;
  518. procedure TXPSubjectTests.TearDown;
  519. begin
  520.   FObserver := nil;
  521.   FObserver2 := nil;
  522.   FObserver3 := nil;
  523.   FObserver4 := nil;
  524.   inherited;
  525. end;
  526. procedure TXPSubjectTests.TestGetObserver;
  527. var
  528.   ASubject: IXPCrackedSubject;
  529. begin
  530.   ASubject := TCrackedSubject.Create;
  531.   CheckEquals(1, ASubject.RefCount, 'subject rc count on construction');
  532.   CheckEquals(0, ASubject.ObserverCount,
  533.     'empty observer count on construction');
  534.   Check(ASubject.Observers[0] = nil, 'observers[0] on empty subject');
  535.   CheckEquals(1, ASubject.RefCount,
  536.     'subject rc count after observers[0] on empty subject');
  537.   Check(ASubject.Observers[-1] = nil, 'observers[-1] on empty subject');
  538.   CheckEquals(1, ASubject.RefCount,
  539.     'subject rc count after observers[-1] on empty subject');
  540.   Check(ASubject.Observers[1] = nil, 'observers[1] on empty subject');
  541.   CheckEquals(1, ASubject.RefCount,
  542.     'subject rc count after observers[1] on empty subject');
  543.   // Add an observer
  544.   CheckEquals(1, FObserver.RefCount, 'observer rc before acquiring subject');
  545.   FObserver.Subject := ASubject;
  546.   Check(ASubject.AddObserver(FObserver, ASubject),
  547.     '1: adding first observer to subject') ;
  548.   CheckEquals(2, ASubject.RefCount, '1: subject rc after first observer');
  549.   CheckEquals(2, FObserver.RefCount, '1: observer rc after acquiring subject');
  550.   CheckEquals(1, ASubject.ObserverCount,
  551.     '1: observer count after first observer');
  552.   Check(ASubject.Observers[0] <> nil, 'observers[0] non-nil on empty subject');
  553.   // temporary interface created in above comparison not released until
  554.   // procedure scope exited
  555.   CheckEquals(3, FObserver.RefCount,
  556.     'observer rc after observers[0] non-nil comparison');
  557.   CheckEquals(1, FObserver2.RefCount, 'observer 2 rc before re-assignment');
  558.   FObserver2 := FObserver;
  559.   // temporary still present in rc after copy
  560.   CheckEquals(4, FObserver2.RefCount, 'observer 2 rc after re-assignment');
  561.   CheckEquals(4, FObserver.RefCount, 'observer rc after copying to observer 2');
  562.   FObserver := nil;
  563.   // temporary still present in rc after original ref released
  564.   CheckEquals(3, FObserver2.RefCount, 'observer 2 rc after FObserver nil''d');
  565.   Check(ASubject.Observers[0] = FObserver2,
  566.     'observers[0] = first observer on subject after first observer');
  567.   // temporary interface created in above comparison not released until
  568.   // procedure scope exited
  569.   CheckEquals(4, FObserver2.RefCount,
  570.     'observer rc after observers[0] = FObserver2 comparison');
  571.   CheckEquals(2, ASubject.RefCount,
  572.     'subject rc count after observers[0] on subject after first observer');
  573.   Check(ASubject.Observers[-1] = nil,
  574.     'observers[-1] on subject after first observer');
  575.   CheckEquals(2, ASubject.RefCount,
  576.     'subject rc count after observers[-1] on subject after first observer');
  577.   Check(ASubject.Observers[1] = nil,
  578.     'observers[1] on subject after first observer');
  579.   CheckEquals(2, ASubject.RefCount,
  580.     'subject rc count after observers[1] on subject after first observer');
  581.   // delete observer
  582.   // still carrying 2 in rc for temporaries created above
  583.   CheckEquals(4, FObserver2.RefCount, 'observer2 rc before discarding subject');
  584.   CheckEquals(2, ASubject.RefCount,
  585.     'subject rc count before sole observer detaches');
  586.   Check(ASubject.DeleteObserver(FObserver2), 'deleting sole observer');
  587.   CheckEquals(1, ASubject.RefCount, 'subject rc after sole observer deleted');
  588.   CheckEquals(3, FObserver2.RefCount,
  589.     'observer rc after detaching from subject');
  590.   CheckEquals(0, ASubject.ObserverCount,
  591.     'observer count after sole observer deleted');
  592.   Check(ASubject.Observers[0] = nil, 'observers[0] on newly empty subject');
  593.   CheckEquals(1, ASubject.RefCount,
  594.     'subject rc count after observers[0] on newly empty subject');
  595.   Check(ASubject.Observers[-1] = nil, 'observers[-1] on newly empty subject');
  596.   CheckEquals(1, ASubject.RefCount,
  597.     'subject rc count after observers[-1] on newly empty subject');
  598.   Check(ASubject.Observers[1] = nil, 'observers[1] on newly empty subject');
  599.   CheckEquals(1, ASubject.RefCount,
  600.     'subject rc count after observers[1] on newly empty subject');
  601.   // add observer again
  602.   CheckEquals(1, FObserver3.RefCount,
  603.     '2: observer rc before acquiring subject');
  604.   FObserver3.Subject := ASubject;
  605.   Check(ASubject.AddObserver(FObserver3, ASubject),
  606.     '2: adding observer to subject');
  607.   CheckEquals(2, ASubject.RefCount, '2: subject rc after observer');
  608.   CheckEquals(2, FObserver3.RefCount, '2: observer rc after acquiring subject');
  609.   CheckEquals(1, ASubject.ObserverCount, '2: observer count after observer');
  610.   Check(ASubject.Observers[0] = FObserver3,
  611.     'observers[0] = observer on subject after re-addition');
  612.   // temporary interface created in above comparison not released until
  613.   // procedure scope exited
  614.   CheckEquals(3, FObserver3.RefCount,
  615.     'observer rc after observers[0] = FObserver3 comparison');
  616.   CheckEquals(2, ASubject.RefCount,
  617.     'subject rc count after observers[0] on subject after re-addition');
  618.   Check(ASubject.Observers[-1] = nil,
  619.     'observers[-1] on subject after first observer');
  620.   CheckEquals(2, ASubject.RefCount,
  621.     'subject rc count after observers[-1] on subject after re-addition');
  622.   Check(ASubject.Observers[1] = nil,
  623.     'observers[1] on subject after first observer');
  624.   CheckEquals(2, ASubject.RefCount,
  625.     'subject rc count after observers[1] on subject after re-addition');
  626.   // add a second observer
  627.   CheckEquals(1, FObserver4.RefCount,
  628.     '3: observer rc before acquiring subject');
  629.   FObserver4.Subject := ASubject;
  630.   Check(ASubject.AddObserver(FObserver4, ASubject),
  631.     '3: adding observer to subject');
  632.   CheckEquals(3, ASubject.RefCount, '3: subject rc after observer');
  633.   CheckEquals(2, FObserver4.RefCount, '3: observer rc after acquiring subject');
  634.   CheckEquals(2, ASubject.ObserverCount, '3: observer count after observer');
  635.   Check(ASubject.Observers[1] = FObserver4,
  636.     'observers[1] = second observer on subject');
  637.   // temporary interface created in above comparison not released until
  638.   // procedure scope exited
  639.   CheckEquals(3, FObserver4.RefCount,
  640.     'observer rc after observers[1] = FObserver4 comparison');
  641.   Check(ASubject.Observers[0] = FObserver3,
  642.     'observers[0] = FObserver3 on subject after second observer');
  643.   // temporary interface created in above comparison not released until
  644.   // procedure scope exited
  645.   CheckEquals(4, FObserver3.RefCount,
  646.     'observer rc after observers[0] = FObserver3 comparison');
  647.   CheckEquals(3, ASubject.RefCount,
  648.     'subject rc count after second observer');
  649.   Check(ASubject.Observers[-1] = nil,
  650.     'observers[-1] on subject after second observer');
  651.   CheckEquals(3, ASubject.RefCount,
  652.     'subject rc count after observers[-1] on subject after second observer');
  653.   Check(ASubject.Observers[2] = nil,
  654.     'observers[2] on subject after second observer');
  655.   CheckEquals(3, ASubject.RefCount,
  656.     'subject rc count after observers[2] on subject after second observer');
  657.  // delete observers
  658.   ASubject.DeleteObservers;
  659.   CheckEquals(1, ASubject.RefCount, 'subject rc count after delete observers');
  660.   CheckEquals(0, ASubject.ObserverCount,
  661.     'empty observer count after delete observers');
  662.   Check(ASubject.Observers[0] = nil, 'observers[0] after delete observers');
  663.   CheckEquals(1, ASubject.RefCount,
  664.     'subject rc count after observers[0] after delete observers');
  665.   Check(ASubject.Observers[-1] = nil, 'observers[-1] after delete observers');
  666.   CheckEquals(1, ASubject.RefCount,
  667.     'subject rc count after observers[-1] after delete observers');
  668.   Check(ASubject.Observers[1] = nil, 'observers[1] after delete observers');
  669.   CheckEquals(1, ASubject.RefCount,
  670.     'subject rc count after observers[1] after delete observers');
  671. end;
  672. { TXPParentTests }
  673. procedure TXPParentTests.SetUp;
  674. begin
  675.   inherited;
  676.   FParent := TCrackedParent.Create;
  677. end;
  678. procedure TXPParentTests.TearDown;
  679. begin
  680.   FParent := nil;
  681.   inherited;
  682. end;
  683. procedure TXPParentTests.TestCreate;
  684. begin
  685.   CheckEquals(1, FParent.RefCount, 'parent rc after construction');
  686.   CheckEquals(0, FParent.Count, 'no children after parent construction');
  687.   Check(FParent.Parent = nil,
  688.     'parent.parent is unassigned after uninitialised contruction')
  689. end;
  690. procedure TXPParentTests.TestAccessParent;
  691. var
  692.   Child: IXPCrackedParent;
  693.   Child2: IXPCrackedParent;
  694. begin
  695.   CheckEquals(1, FParent.RefCount, 'parent rc after construction');
  696.   // Create parented child
  697.   Child := TCrackedParent.Create(FParent);
  698.   CheckEquals(2, FParent.RefCount, 'parent rc after child added');
  699.   CheckEquals(2, Child.RefCount, 'child rc after child added to parent');
  700.   Check(Child.Parent = FParent, 'failed to get parent on parented child');
  701.   // temporary interface created above won't be released until we leave proc
  702.   // scope (tested in D6) compiler optimization setting doesn't affect this
  703.   // result
  704.   CheckEquals(2, Child.RefCount, 'child rc before children[0] = child failed');
  705.   Check(FParent.Children[0] = Child as IXPObserver,
  706.     'children[0] = child failed');
  707.   // new temporaries created by LHS *and* RHS
  708.   CheckEquals(4, Child.RefCount, 'child rc after children[0] = child failed');
  709.   // Create unparented child
  710.   Child2 := TCrackedParent.Create;
  711.   Check(Child2.Parent = nil, 'got parent on unparented child');
  712.   CheckEquals(1, Child2.RefCount, 'child2 rc after construction');
  713.   CheckEquals(3, FParent.RefCount, 'parent rc before child2 assigned');
  714.   // Parent unparented child
  715.   Child2.Parent := FParent;
  716.   CheckEquals(4, FParent.RefCount, 'parent rc after child2 added to parent');
  717.   Check(Child2.Parent <> nil, 'failed to get parent for child2');
  718.   // persistent temporary interface created above (again)
  719.   CheckEquals(5, FParent.RefCount, 'parent rc after child2 added to parent');
  720.   CheckEquals(2, Child2.RefCount, 'child2 rc after child2 added to parent');
  721.   // unparent first child
  722.   // still carrying two temps in rc
  723.   CheckEquals(4, Child.RefCount, 'child rc before unparenting');
  724.   CheckEquals(5, FParent.RefCount, 'parent rc before unparenting child');
  725.   CheckEquals(2, FParent.Count, 'observer count before unparenting child');
  726.   Child.Parent := nil;
  727.   // still carrying two temps in rc
  728.   CheckEquals(3, Child.RefCount, 'child rc after unparenting');
  729.   CheckEquals(4, FParent.RefCount, 'parent rc after unparenting child');
  730.   CheckEquals(1, FParent.Count, 'observer count after unparenting child');
  731.   CheckEquals(2, Child2.RefCount,
  732.     'child2 rc before equality check on children[0]');
  733.   Check(FParent.Children[0] = Child2 as IXPObserver,
  734.     'child2 moved down to first slot in list');
  735.   // new temporaries created by LHS *and* RHS
  736.   CheckEquals(4, Child2.RefCount,
  737.     'child2 rc after equality check on children[0]');
  738. end;
  739. procedure TXPParentTests.TestReleaseSubject;
  740. var
  741.   Child: IXPCrackedParent;
  742.   Child2: IXPCrackedParent;
  743.   Child3: IXPCrackedParent;
  744.   Child4: IXPCrackedParent;
  745.   Child21: IXPCrackedParent;
  746.   Child22: IXPCrackedParent;
  747.   Child211: IXPCrackedParent;
  748.   Child212: IXPCrackedParent;
  749. begin
  750.   // Create first generation of children
  751.   CheckEquals(1, FParent.RefCount, 'parent rc after construction');
  752.   Child := TCrackedParent.Create(FParent);
  753.   CheckEquals(2, FParent.RefCount, 'parent rc after child added');
  754.   CheckEquals(2, Child.RefCount, 'child rc after child added to parent');
  755.   Child2 := TCrackedParent.Create(FParent);
  756.   CheckEquals(3, FParent.RefCount, 'parent rc after child2 added');
  757.   CheckEquals(2, Child2.RefCount, 'child2 rc after child2 added to parent');
  758.   Child3 := TCrackedParent.Create(FParent);
  759.   CheckEquals(4, FParent.RefCount, 'parent rc after child3 added');
  760.   CheckEquals(2, Child3.RefCount, 'child3 rc after child3 added to parent');
  761.   Child4 := TCrackedParent.Create(FParent);
  762.   CheckEquals(5, FParent.RefCount, 'parent rc after child4 added');
  763.   CheckEquals(2, Child4.RefCount, 'child4 rc after child4 added to parent');
  764.   // Create second generation of children
  765.   Child21 := TCrackedParent.Create(Child2);
  766.   CheckEquals(3, Child2.RefCount, 'child2 rc after child21 added');
  767.   CheckEquals(2, Child21.RefCount, 'child21 rc after child21 added to parent');
  768.   Child22 := TCrackedParent.Create(Child2);
  769.   CheckEquals(4, Child2.RefCount, 'child2 rc after child22 added');
  770.   CheckEquals(2, Child22.RefCount, 'child22 rc after child22 added to parent');
  771.   // Create third generation of children
  772.   Child211 := TCrackedParent.Create(Child21);
  773.   CheckEquals(3, Child21.RefCount, 'child21 rc after child211 added');
  774.   CheckEquals(2, Child211.RefCount,
  775.     'child211 rc after child211 added to parent');
  776.   Child212 := TCrackedParent.Create(Child21);
  777.   CheckEquals(4, Child21.RefCount, 'child21 rc after child212 added');
  778.   CheckEquals(2, Child212.RefCount,
  779.     'child212 rc after child212 added to parent');
  780.   // Release ancestor
  781.   FParent := nil;
  782.   CheckEquals(1, Child212.RefCount, 'child212 rc after ancestor released');
  783.   CheckEquals(1, Child211.RefCount, 'child211 rc after ancestor released');
  784.   CheckEquals(1, Child21.RefCount, 'child21 rc after ancestor released');
  785.   CheckEquals(1, Child22.RefCount, 'child22 rc after ancestor released');
  786.   CheckEquals(1, Child4.RefCount, 'child4 rc after ancestor released');
  787.   CheckEquals(1, Child3.RefCount, 'child3 rc after ancestor released');
  788.   CheckEquals(1, Child2.RefCount, 'child2 rc after ancestor released');
  789.   CheckEquals(1, Child.RefCount, 'child rc after ancestor released');
  790. end;
  791. { TCrackedObserver }
  792. destructor TCrackedObserver.Destroy;
  793. begin
  794.   if FSubject <> nil then
  795.     FSubject.DeleteObserver(self);
  796.   inherited;
  797. end;
  798. function TCrackedObserver.GetSubject: IXPSubject;
  799. begin
  800.   Result := FSubject;
  801. end;
  802. function TCrackedObserver.RefCount: integer;
  803. begin
  804.   Result := FRefCount;
  805. end;
  806. procedure TCrackedObserver.ReleaseSubject(const Subject: IXPSubject;
  807.   const Context: pointer);
  808. begin
  809. //  ff (FSubject <> nil)  and ((FSubject as IXPSubject) = Subject) then
  810.   if Subject = FSubject then
  811.     FSubject := nil;
  812. end;
  813. procedure TCrackedObserver.SetSubject(const Subject: IXPSubject);
  814. begin
  815.   FSubject := Subject;
  816. end;
  817. { TCrackedSubject }
  818. function TCrackedSubject.RefCount: integer;
  819. begin
  820.   Result := FRefCount;
  821. end;
  822. { TCrackedSubjects }
  823. destructor TCrackedSubjects.Destroy;
  824. begin
  825.   inherited;
  826. end;
  827. function TCrackedSubjects.RefCount: integer;
  828. begin
  829.   Result := FRefCount;
  830. end;
  831. { TCrackedParent }
  832. function TCrackedParent.RefCount: integer;
  833. begin
  834.   Result := FRefCount;
  835. end;
  836. initialization
  837.   TestFramework.RegisterTest('XPObserverTests Suite',
  838.     TXPSubjectsTests.Suite);
  839.   TestFramework.RegisterTest('XPObserverTests Suite',
  840.     TXPSubjectTests.Suite);
  841.   TestFramework.RegisterTest('XPObserverTests Suite',
  842.     TXPParentTests.Suite);
  843. end.