unit tccollection; {$mode objfpc}{$H+} interface uses Classes, SysUtils, fpcunit, testregistry; type { TMyItem } TMyItem = Class(TCollectionItem) private FNr: integer; protected // Expose function GetOwner: TPersistent; override; published Property Nr : integer Read FNr Write FNr; end; { TMyCollection } TMyCollection = Class(TCollection) Private FOwner : TPersistent; FUpdateCount : Integer; FLastNotifyItem, FLastUpdate : TCollectionItem; FNotifyCount : Integer; FLastNotify : TCollectionNotification; Function GetOwner : TPersistent; override; Public procedure Update(Item: TCollectionItem); override; procedure Notify(Item: TCollectionItem;Action: TCollectionNotification); override; Procedure ResetUpdate; Procedure ResetNotify; property PropName; end; { TTestTCollection } TTestTCollection= class(TTestCase) private procedure AccessNegativeIndex; procedure AccessTooBigIndex; procedure DeleteNegativeIndex; procedure DeleteTooBigIndex; procedure MoveNegativeIndex; procedure MoveTooBigIndex; protected FColl : TMyCollection; Function MyItem(I : integer) : TMyItem; procedure AddItems(ACount : Integer); procedure SetUp; override; procedure TearDown; override; published procedure TestCreate; procedure TestAdd; procedure TestItemCollection; procedure TestAddTwo; Procedure TestDelete; procedure TestClear; Procedure TestFreeItem; Procedure TestMoveForward; Procedure TestMoveBackward; Procedure TestID; Procedure TestItemOwner; Procedure TestDisplayName; procedure TestOwnerNamePath; Procedure TestItemNamePath; Procedure TestOwnerItemNamePath; Procedure TestChangeCollection; procedure TestAccesIndexOutOfBounds; procedure TestDeleteIndexOutOfBounds; procedure TestMoveIndexOutOfBounds; Procedure TestUpdateAdd; Procedure TestUpdateDelete; Procedure TestUpdateDisplayName; Procedure TestUpdateCount; Procedure TestUpdateCountNested; Procedure TestUpdateMove; Procedure TestNotifyAdd; Procedure TestNotifyDelete; end; implementation procedure TTestTCollection.TestCreate; begin AssertEquals('Item count 0 at create',0,FColl.Count); AssertEquals('ItemClass is TMyItem',TMyItem,FColl.ItemClass); end; procedure TTestTCollection.TestAdd; begin AddItems(1); AssertEquals('Item count is 1 after add',1,FColl.Count); AssertEquals('Item class is correct',FColl.ItemClass,FColl.Items[0].ClassType); AssertEquals('Item index is 0',0,FColl.Items[0].Index); AssertEquals('Item ID is 0',0,FColl.Items[0].Id); end; procedure TTestTCollection.TestItemCollection; begin AddItems(1); If MyItem(0).Collection<>FColl then Fail('Item''s Collection is not collection'); end; procedure TTestTCollection.TestAddTwo; Var I: Integer; begin AddItems(3); AssertEquals('Item count is 3 after add',3,FColl.Count); For I:=0 to 2 do begin AssertEquals(Format('Item %d class is correct',[i]),FColl.ItemClass,FColl.Items[i].ClassType); AssertEquals(Format('Item %d index is 0',[i]),i,FColl.Items[i].Index); AssertEquals(Format('Item %d ID is 0',[i]),i,FColl.Items[i].Id); AssertEquals(Format('Item %d ID is %d',[i,i+1]),i+1,MyItem(i).Nr); end; end; procedure TTestTCollection.TestDelete; begin AddItems(3); FColl.Delete(1); AssertEquals('Item count after delete',2,FColl.Count); AssertEquals('Item 0 ok after delete',1,MyItem(0).Nr); AssertEquals('Item 1 ok after delete',3,MyItem(1).Nr); end; procedure TTestTCollection.TestClear; begin AddItems(3); FColl.Clear; AssertEquals('Item count after clear',0,FColl.Count); end; procedure TTestTCollection.TestFreeItem; begin AddItems(3); MyItem(1).Free; AssertEquals('Item count after free',2,FColl.Count); AssertEquals('Item 0 ok after free',1,MyItem(0).Nr); AssertEquals('Item 1 ok after free',3,MyItem(1).Nr); end; procedure TTestTCollection.TestMoveForward; begin AddItems(5); MyItem(4).Index:=1; AssertEquals('Item 0 ok after move',1,MyItem(0).Nr); AssertEquals('Item 1 ok after move',5,MyItem(1).Nr); AssertEquals('Item 2 ok after move',2,MyItem(2).Nr); AssertEquals('Item 3 ok after move',3,MyItem(3).Nr); AssertEquals('Item 4 ok after move',4,MyItem(4).Nr); end; procedure TTestTCollection.TestMoveBackward; begin AddItems(5); MyItem(1).Index:=3; AssertEquals('Item 0 ok after move',1,MyItem(0).Nr); AssertEquals('Item 1 ok after move',3,MyItem(1).Nr); AssertEquals('Item 2 ok after move',4,MyItem(2).Nr); AssertEquals('Item 3 ok after move',2,MyItem(3).Nr); AssertEquals('Item 4 ok after move',5,MyItem(4).Nr); end; procedure TTestTCollection.TestID; Var I : TMyItem; begin AddItems(5); FColl.Delete(2); FColl.Delete(2); I:=TMyItem(FColl.Add); AssertEquals('ID keeps counting up',5,I.Id) end; procedure TTestTCollection.TestItemOwner; begin AddItems(1); If (MyItem(0).GetOwner<>FColl) then Fail('Item owner is not collection'); end; procedure TTestTCollection.TestDisplayName; begin AddItems(1); AssertEquals('Displayname is classname','TMyItem',MyItem(0).DisplayName); end; procedure TTestTCollection.TestItemNamePath; begin AddItems(2); AssertEquals('Item namepath is collection namepath+index',FColl.GetNamePath+'[0]',MyItem(0).GetNamePath); AssertEquals('Item namepath is collection namepath+index',FColl.GetNamePath+'[1]',MyItem(1).GetNamePath); end; procedure TTestTCollection.TestOwnerItemNamePath; Var P : TPersistent; begin P:=TPersistent.Create; try TMyCollection(FColl).FOwner:=P; AddItems(2); TMyCollection(FColl).PropName:='Something'; AssertEquals('Item namepath is collection namepath+index','TPersistent.Something[0]',MyItem(0).GetNamePath); finally P.Free; end; end; procedure TTestTCollection.TestOwnerNamePath; Var P : TPersistent; begin P:=TPersistent.Create; try TMyCollection(FColl).FOwner:=P; AddItems(2); TMyCollection(FColl).PropName:='Something'; AssertEquals('Namepath is collection namepath+index','TPersistent.Something',FColl.GetNamePath); finally P.Free; end; end; procedure TTestTCollection.TestChangeCollection; Var FCol2 : TCollection; I : TCollectionItem; begin AddItems(2); FCol2:=TCollection.Create(TMyItem); try I:=FCol2.Add; I.Collection:=FColl; AssertEquals('Moved item, count of source is zero',0,FCol2.Count); AssertEquals('Moved item, count of dest is 1',3,FColl.Count); AssertEquals('Moved item, index is 2',2,I.Index); If (FColl.Items[0].Collection<>FColl) then Fail('Collection owner is not set correctly after move'); AssertEquals('Moved item, ID is 2',2,I.ID); finally FCol2.free; end; end; procedure TTestTCollection.AccessNegativeIndex; begin FColl.Items[-1]; end; procedure TTestTCollection.AccessTooBigIndex; begin FColl.Items[3]; end; procedure TTestTCollection.TestAccesIndexOutOfBounds; begin AddItems(3); AssertException('Access Negative Index',EListError,@AccessNegativeIndex); AssertException('Access Index too big',EListError,@AccessTooBigIndex); end; procedure TTestTCollection.DeleteNegativeIndex; begin FColl.Delete(-1); end; procedure TTestTCollection.DeleteTooBigIndex; begin FColl.Delete(3); end; procedure TTestTCollection.TestDeleteIndexOutOfBounds; begin AddItems(3); AssertException('Delete Negative Index',EListError,@DeleteNegativeIndex); AssertException('Delete Index too big',EListError,@DeleteTooBigIndex); end; procedure TTestTCollection.MoveNegativeIndex; begin FColl.Items[1].Index:=-1; end; procedure TTestTCollection.MoveTooBigIndex; begin FColl.Items[1].Index:=3; end; procedure TTestTCollection.TestMoveIndexOutOfBounds; begin AddItems(3); AssertException('Move Negative first index',EListError,@MoveNegativeIndex); AssertException('Exchange Negative second index',EListError,@MoveTooBigIndex); end; procedure TTestTCollection.TestUpdateAdd; begin AddItems(1); If (FColl.FLastUpdate<>Nil) then Fail('update item found !'); AssertEquals('Update count is 1',1,FColl.FUpdateCount); end; procedure TTestTCollection.TestUpdateDelete; begin AddItems(1); FColl.ResetUpdate; FColl.Delete(0); If (FColl.FLastUpdate<>Nil) then Fail('update item found !'); AssertEquals('Update count is 1',1,FColl.FUpdateCount); end; procedure TTestTCollection.TestUpdateDisplayName; begin AddItems(1); FColl.ResetUpdate; MyItem(0).DisplayName:='Something'; AssertEquals('Display name notification. Update count is 1',1,FColl.FUpdateCount); If (FColl.FLastUpdate<>MyItem(0)) then Fail('No displayname update'); end; procedure TTestTCollection.TestUpdateCount; begin FColl.BeginUpdate; Try AddItems(2); AssertEquals('Beginupdate; adds. Update count is 0',0,FColl.FUpdateCount); If (FColl.FLastUpdate<>Nil) then Fail('Beginupdate; FlastUpdate not nil'); finally FColl.EndUpdate; end; AssertEquals('Endupdate; adds. Update count is 1',1,FColl.FUpdateCount); If (FColl.FLastUpdate<>Nil) then Fail('Endupdate; FlastUpdate not nil'); end; procedure TTestTCollection.TestUpdateCountNested; begin FColl.BeginUpdate; Try AddItems(2); FColl.BeginUpdate; Try AddItems(2); AssertEquals('Beginupdate 2; adds. Update count is 0',0,FColl.FUpdateCount); If (FColl.FLastUpdate<>Nil) then Fail('Beginupdate 2; FlastUpdate not nil'); finally FColl.EndUpdate; end; AssertEquals('Endupdate 1; Update count is 0',0,FColl.FUpdateCount); If (FColl.FLastUpdate<>Nil) then Fail('EndUpdate 1; FlastUpdate not nil'); finally FColl.EndUpdate; end; AssertEquals('Endupdate 2; adds. Update count is 1',1,FColl.FUpdateCount); If (FColl.FLastUpdate<>Nil) then Fail('Endupdate 2; FlastUpdate not nil'); end; procedure TTestTCollection.TestUpdateMove; begin AddItems(5); FColl.ResetUpdate; MyItem(4).Index:=2; AssertEquals('Moved item. Update count is 1',1,FColl.FUpdateCount); If (FColl.FLastUpdate<>Nil) then Fail('Moved item notification - not all items updated'); end; procedure TTestTCollection.TestNotifyAdd; begin AddItems(1); If (FColl.FLastNotifyItem<>MyItem(0)) then Fail('No notify item found !'); AssertEquals('Notify count is 1',1,FColl.FNotifyCount); AssertEquals('Notify action is cnAdded',Ord(cnAdded),Ord(FColl.FLastNotify)); end; procedure TTestTCollection.TestNotifyDelete; begin AddItems(3); FColl.ResetNotify; FColl.Delete(1); // cnDeleting/cnExtracing. Can't currently test for 2 events... AssertEquals('Notify count is 2',2,FColl.FNotifyCount); AssertEquals('Notify action is cnExtracted',Ord(cnExtracting),Ord(FColl.FLastNotify)); end; function TTestTCollection.MyItem(I: integer): TMyItem; begin Result:=TMyItem(FColl.Items[i]); end; procedure TTestTCollection.AddItems(ACount: Integer); Var I : integer; begin For I:=1 to ACount do TMyItem(FColl.Add).Nr:=I; end; procedure TTestTCollection.SetUp; begin FColl:=TMyCollection.Create(TMyItem); end; procedure TTestTCollection.TearDown; begin FreeAndNil(FColl); end; { TMyItem } function TMyItem.GetOwner: TPersistent; begin Result:=inherited GetOwner; end; { TMyCollection } function TMyCollection.GetOwner: TPersistent; begin Result:=FOwner; If (Result=Nil) then Result:=Inherited GetOwner; end; procedure TMyCollection.Update(Item: TCollectionItem); begin Inc(FUpdateCount); FLastUpdate:=Item; end; procedure TMyCollection.Notify(Item: TCollectionItem; Action: TCollectionNotification); begin Inc(FNotifyCount); FLastNotify:=Action; FLastNotifyItem:=Item; end; procedure TMyCollection.ResetUpdate; begin FUpdateCount:=0; FLastUpdate:=Nil; end; procedure TMyCollection.ResetNotify; begin FNotifyCount:=0; FLastNotifyItem:=Nil; end; initialization RegisterTest(TTestTCollection); end.