tests.generics.dictionary.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  1. unit tests.generics.dictionary;
  2. {$mode objfpc}
  3. interface
  4. uses
  5. fpcunit, testregistry, Classes, SysUtils, Generics.Defaults, Generics.Collections;
  6. Type
  7. TMySimpleDict = Class(Specialize TDictionary<Integer,String>);
  8. {$IFDEF FPC}
  9. EDictionary = EListError;
  10. TMyPair = specialize TPair<Integer,String>;
  11. {$ENDIF}
  12. { TTestSimpleDictionary }
  13. TTestSimpleDictionary = Class(TTestCase)
  14. Private
  15. FDict : TMySimpleDict;
  16. FnotifyMessage : String;
  17. FCurrentKeyNotify : Integer;
  18. FCurrentValueNotify : Integer;
  19. FExpectKeys : Array of Integer;
  20. FExpectValues : Array of String;
  21. FExpectValueAction,
  22. FExpectKeyAction: Array of TCollectionNotification;
  23. procedure DoAdd(aCount: Integer; aOffset: Integer=0);
  24. procedure DoAdd2;
  25. Procedure DoneExpectKeys;
  26. Procedure DoneExpectValues;
  27. procedure DoGetValue(aKey: Integer; Match: String; ExceptionClass: TClass=nil);
  28. procedure DoKeyNotify(ASender: TObject; const AItem: Integer; AAction: TCollectionNotification);
  29. procedure DoValueNotify(ASender: TObject; const AItem: String; AAction: TCollectionNotification);
  30. Public
  31. Procedure SetExpectKeys(aMessage : string; AKeys : Array of Integer; AActions : Array of TCollectionNotification; DoReverse : Boolean = False);
  32. Procedure SetExpectValues(aMessage : string; AKeys : Array of String; AActions : Array of TCollectionNotification; DoReverse : Boolean = False);
  33. Procedure SetUp; override;
  34. Procedure TearDown; override;
  35. Property Dict : TMySimpleDict Read FDict;
  36. Published
  37. Procedure TestEmpty;
  38. Procedure TestAdd;
  39. Procedure TestClear;
  40. Procedure TestTryGetValue;
  41. Procedure TestGetValue;
  42. Procedure TestSetValue;
  43. Procedure TestAddDuplicate;
  44. Procedure TestAddOrSet;
  45. Procedure TestTryAdd;
  46. Procedure TestContainsKey;
  47. Procedure TestContainsValue;
  48. Procedure TestDelete;
  49. Procedure TestToArray;
  50. procedure TestKeys;
  51. Procedure TestValues;
  52. Procedure TestEnumerator;
  53. Procedure TestNotification;
  54. procedure TestNotificationDelete;
  55. procedure TestValueNotification;
  56. procedure TestValueNotificationDelete;
  57. procedure TestKeyValueNotificationSet;
  58. end;
  59. implementation
  60. { TTestSimpleDictionary }
  61. procedure TTestSimpleDictionary.SetUp;
  62. begin
  63. inherited SetUp;
  64. FDict:=TMySimpleDict.Create;
  65. FCurrentKeyNotify:=0;
  66. FCurrentValueNotify:=0;
  67. FExpectKeys:=[];
  68. FExpectKeyAction:=[];
  69. FExpectValues:=[];
  70. FExpectValueAction:=[];
  71. end;
  72. procedure TTestSimpleDictionary.TearDown;
  73. begin
  74. // So we don't get clear messages
  75. FDict.OnKeyNotify:=Nil;
  76. FDict.OnValueNotify:=Nil;
  77. FreeAndNil(FDict);
  78. inherited TearDown;
  79. end;
  80. procedure TTestSimpleDictionary.TestEmpty;
  81. begin
  82. AssertNotNull('Have dictionary',Dict);
  83. AssertEquals('empty dictionary',0,Dict.Count);
  84. end;
  85. procedure TTestSimpleDictionary.DoAdd(aCount : Integer; aOffset : Integer=0);
  86. Var
  87. I : Integer;
  88. begin
  89. if aOffset=-1 then
  90. aOffset:=Dict.Count;
  91. For I:=aOffset+1 to aOffset+aCount do
  92. Dict.Add(I,IntToStr(i));
  93. end;
  94. procedure TTestSimpleDictionary.TestAdd;
  95. begin
  96. DoAdd(1);
  97. AssertEquals('Count OK',1,Dict.Count);
  98. AssertTrue('Has added value',Dict.ContainsKey(1));
  99. DoAdd(1,1);
  100. AssertEquals('Count OK',2,Dict.Count);
  101. AssertTrue('Has added value',Dict.ContainsKey(2));
  102. end;
  103. procedure TTestSimpleDictionary.TestClear;
  104. begin
  105. DoAdd(3);
  106. AssertEquals('Count OK',3,Dict.Count);
  107. Dict.Clear;
  108. AssertEquals('Count after clear OK',0,Dict.Count);
  109. end;
  110. procedure TTestSimpleDictionary.TestTryGetValue;
  111. Var
  112. I : integer;
  113. SI,A : string;
  114. begin
  115. DoAdd(3);
  116. For I:=1 to 3 do
  117. begin
  118. SI:=IntToStr(I);
  119. AssertTrue('Have value '+SI,Dict.TryGetValue(I,A));
  120. AssertEquals('Value is correct '+SI,SI,A);
  121. end;
  122. AssertFalse('Have no value 4',Dict.TryGetValue(4,A));
  123. end;
  124. procedure TTestSimpleDictionary.DoGetValue(aKey: Integer; Match: String; ExceptionClass: TClass);
  125. Var
  126. EC : TClass;
  127. A,EM : String;
  128. begin
  129. EC:=Nil;
  130. try
  131. A:=Dict.Items[aKey];
  132. except
  133. On E : Exception do
  134. begin
  135. EC:=E.ClassType;
  136. EM:=E.Message;
  137. end
  138. end;
  139. if ExceptionClass=Nil then
  140. begin
  141. if EC<>Nil then
  142. Fail('Got exception '+EC.ClassName+' with message: '+EM);
  143. AssertEquals('Value is correct for '+IntToStr(aKey),Match,A)
  144. end
  145. else
  146. begin
  147. if EC=Nil then
  148. Fail('Expected exception '+ExceptionClass.ClassName+' but got none');
  149. if EC<>ExceptionClass then
  150. Fail('Expected exception class '+ExceptionClass.ClassName+' but got '+EC.ClassName+' with message '+EM);
  151. end;
  152. end;
  153. procedure TTestSimpleDictionary.DoKeyNotify(ASender: TObject; const AItem: Integer; AAction: TCollectionNotification);
  154. begin
  155. Writeln(FnotifyMessage+' Notification ',FCurrentKeyNotify);
  156. AssertSame(FnotifyMessage+' Correct sender', FDict,aSender);
  157. if (FCurrentKeyNotify>=Length(FExpectKeys)) then
  158. Fail(FnotifyMessage+' Too many notificiations');
  159. AssertEquals(FnotifyMessage+' Notification Key no '+IntToStr(FCurrentKeyNotify),FExpectKeys[FCurrentKeyNotify],aItem);
  160. Inc(FCurrentKeyNotify);
  161. end;
  162. procedure TTestSimpleDictionary.DoValueNotify(ASender: TObject; const AItem: String; AAction: TCollectionNotification);
  163. begin
  164. Writeln(FnotifyMessage+' value Notification ',FCurrentValueNotify);
  165. AssertSame(FnotifyMessage+' value Correct sender', FDict,aSender);
  166. if (FCurrentValueNotify>=Length(FExpectValues)) then
  167. Fail(FnotifyMessage+' Too many value notificiations');
  168. AssertEquals(FnotifyMessage+' Notification value no '+IntToStr(FCurrentValueNotify),FExpectValues[FCurrentValueNotify],aItem);
  169. Inc(FCurrentValueNotify);
  170. end;
  171. procedure TTestSimpleDictionary.SetExpectKeys(aMessage: string; AKeys: array of Integer;
  172. AActions: array of TCollectionNotification; DoReverse: Boolean = False);
  173. Var
  174. I,L : integer;
  175. begin
  176. FnotifyMessage:=aMessage;
  177. FCurrentKeyNotify:=0;
  178. L:=Length(aKeys);
  179. AssertEquals('SetExpectkeys: Lengths arrays equal',l,Length(aActions));
  180. SetLength(FExpectKeys,L);
  181. SetLength(FExpectKeyAction,L);
  182. Dec(L);
  183. if DoReverse then
  184. For I:=0 to L do
  185. begin
  186. FExpectKeys[L-i]:=AKeys[i];
  187. FExpectKeyAction[L-i]:=AActions[I];
  188. end
  189. else
  190. For I:=0 to L do
  191. begin
  192. FExpectKeys[i]:=AKeys[i];
  193. FExpectKeyAction[i]:=AActions[I];
  194. end;
  195. end;
  196. procedure TTestSimpleDictionary.SetExpectValues(aMessage: string; AKeys: array of String;
  197. AActions: array of TCollectionNotification; DoReverse: Boolean);
  198. Var
  199. I,L : integer;
  200. begin
  201. FnotifyMessage:=aMessage;
  202. FCurrentValueNotify:=0;
  203. L:=Length(aKeys);
  204. AssertEquals('SetExpectValues: Lengths arrays equal',l,Length(aActions));
  205. SetLength(FExpectValues,L);
  206. SetLength(FExpectValueAction,L);
  207. Dec(L);
  208. if DoReverse then
  209. For I:=0 to L do
  210. begin
  211. FExpectValues[L-i]:=AKeys[i];
  212. FExpectValueAction[L-i]:=AActions[I];
  213. end
  214. else
  215. For I:=0 to L do
  216. begin
  217. FExpectValues[i]:=AKeys[i];
  218. FExpectValueAction[i]:=AActions[I];
  219. end;
  220. end;
  221. procedure TTestSimpleDictionary.TestGetValue;
  222. Var
  223. I : integer;
  224. begin
  225. DoAdd(3);
  226. For I:=1 to 3 do
  227. DoGetValue(I,IntToStr(I));
  228. DoGetValue(4,'4',EDictionary);
  229. end;
  230. procedure TTestSimpleDictionary.TestSetValue;
  231. begin
  232. TestGetValue;
  233. Dict.Items[3]:='Six';
  234. DoGetValue(3,'Six');
  235. end;
  236. procedure TTestSimpleDictionary.DoAdd2;
  237. begin
  238. Dict.Add(2,'A new 2');
  239. end;
  240. procedure TTestSimpleDictionary.DoneExpectKeys;
  241. begin
  242. AssertEquals(FnotifyMessage+' Expected number of keys seen',Length(FExpectKeys),FCurrentKeyNotify);
  243. end;
  244. procedure TTestSimpleDictionary.DoneExpectValues;
  245. begin
  246. AssertEquals(FnotifyMessage+' Expected number of values seen',Length(FExpectValues),FCurrentValueNotify);
  247. end;
  248. procedure TTestSimpleDictionary.TestAddDuplicate;
  249. begin
  250. DoAdd(3);
  251. AssertException('Cannot add duplicate',EDictionary,@DoAdd2);
  252. end;
  253. procedure TTestSimpleDictionary.TestAddOrSet;
  254. begin
  255. DoAdd(3);
  256. Dict.AddOrSetValue(2,'a new 2');
  257. DoGetValue(2,'a new 2');
  258. end;
  259. procedure TTestSimpleDictionary.TestTryAdd;
  260. begin
  261. AssertTrue(Dict.TryAdd(1, 'Foobar'));
  262. AssertFalse(Dict.TryAdd(1, 'Foo'));
  263. AssertTrue(Dict.TryAdd(2, 'Bar'));
  264. end;
  265. procedure TTestSimpleDictionary.TestContainsKey;
  266. Var
  267. I : Integer;
  268. begin
  269. DoAdd(3);
  270. For I:=1 to 3 do
  271. AssertTrue('Has '+IntToStr(i),Dict.ContainsKey(I));
  272. AssertFalse('Has not 4',Dict.ContainsKey(4));
  273. end;
  274. procedure TTestSimpleDictionary.TestContainsValue;
  275. Var
  276. I : Integer;
  277. begin
  278. DoAdd(3);
  279. For I:=1 to 3 do
  280. AssertTrue('Has '+IntToStr(i),Dict.ContainsValue(IntToStr(i)));
  281. AssertFalse('Has not 4',Dict.ContainsValue('4'));
  282. end;
  283. procedure TTestSimpleDictionary.TestDelete;
  284. begin
  285. DoAdd(3);
  286. Dict.Remove(2);
  287. AssertEquals('Count',2,Dict.Count);
  288. AssertFalse('Has not 2',Dict.ContainsKey(2));
  289. end;
  290. procedure TTestSimpleDictionary.TestToArray;
  291. Var
  292. {$ifdef fpc}
  293. A : specialize TArray<TMyPair>;
  294. {$else}
  295. A : specialize TArray<TMySimpleDict.TMyPair>;
  296. {$endif}
  297. I : Integer;
  298. SI : String;
  299. begin
  300. DoAdd(3);
  301. A:=Dict.ToArray;
  302. specialize TArrayHelper<TMyPair>.Sort(A{$ifndef fpc}, specialize TComparer<TMySimpleDict.TMyPair>.Default{$endif});
  303. AssertEquals('Length Ok',3,Length(A));
  304. For I:=1 to 3 do
  305. begin
  306. SI:=IntToStr(I);
  307. AssertEquals('key '+SI,I,A[i-1].Key);
  308. AssertEquals('Value '+SI,SI,A[i-1].Value);
  309. end;
  310. end;
  311. procedure TTestSimpleDictionary.TestKeys;
  312. Var
  313. A : Array of Integer;
  314. I : Integer;
  315. SI : String;
  316. begin
  317. DoAdd(3);
  318. A:=Dict.Keys.ToArray;
  319. specialize TArrayHelper<Integer>.Sort(A{$ifndef fpc}, specialize TComparer<Integer>.Default{$endif});
  320. AssertEquals('Length Ok',3,Length(A));
  321. For I:=1 to 3 do
  322. begin
  323. SI:=IntToStr(I);
  324. AssertEquals('key '+SI,I,A[i-1]);
  325. end;
  326. end;
  327. procedure TTestSimpleDictionary.TestValues;
  328. Var
  329. A : Array of String;
  330. I : Integer;
  331. SI : String;
  332. begin
  333. DoAdd(3);
  334. A:=Dict.Values.ToArray;
  335. specialize TArrayHelper<String>.Sort(A{$ifndef fpc}, specialize TComparer<String>.Default{$endif});
  336. AssertEquals('Length Ok',3,Length(A));
  337. For I:=1 to 3 do
  338. begin
  339. SI:=IntToStr(I);
  340. AssertEquals('Value '+SI,SI,A[i-1]);
  341. end;
  342. end;
  343. procedure TTestSimpleDictionary.TestEnumerator;
  344. type
  345. TStringList = specialize TList<String>;
  346. TIntegerList = specialize TList<Integer>;
  347. Var
  348. {$ifdef fpc}
  349. A : TMyPair;
  350. {$else}
  351. A : TMySimpleDict.TMyPair;
  352. {$endif}
  353. I,J : Integer;
  354. SI : String;
  355. IL: TIntegerList;
  356. SL: TStringList;
  357. begin
  358. DoAdd(3);
  359. IL:=Nil;
  360. SL:=TStringList.Create;
  361. try
  362. IL:=TIntegerList.Create;
  363. for I:=1 to 3 do begin
  364. IL.Add(I);
  365. SL.Add(IntToStr(I));
  366. end;
  367. I:=1;
  368. For A in Dict do
  369. begin
  370. SI:=IntToStr(I);
  371. J:=IL.IndexOf(A.Key);
  372. AssertTrue('key '+SI,J>=0);
  373. IL.Delete(J);
  374. J:=SL.IndexOf(A.Value);
  375. AssertTrue('value '+SI,J>=0);
  376. SL.Delete(J);
  377. Inc(I);
  378. end;
  379. finally
  380. IL.Free;
  381. SL.Free;
  382. end;
  383. end;
  384. procedure TTestSimpleDictionary.TestNotification;
  385. begin
  386. Dict.OnKeyNotify:=@DoKeyNotify;
  387. SetExpectKeys('Add',[1,2,3],[cnAdded,cnAdded,cnAdded]);
  388. DoAdd(3);
  389. DoneExpectKeys;
  390. end;
  391. Const
  392. {$IFDEF FPC}
  393. {$IFNDEF CPUWASM}
  394. ReverseDeleteNotification = True;
  395. {$ElSE}
  396. ReverseDeleteNotification = False;
  397. {$ENDIF}
  398. {$ELSE}
  399. ReverseDeleteNotification = False;
  400. {$ENDIF}
  401. procedure TTestSimpleDictionary.TestNotificationDelete;
  402. begin
  403. DoAdd(3);
  404. Dict.OnKeyNotify:=@DoKeyNotify;
  405. SetExpectKeys('Clear',[1,2,3],[cnRemoved,cnRemoved,cnRemoved],ReverseDeleteNotification);
  406. Dict.Clear;
  407. DoneExpectKeys;
  408. end;
  409. procedure TTestSimpleDictionary.TestValueNotification;
  410. begin
  411. Dict.OnValueNotify:=@DoValueNotify;
  412. SetExpectValues('Add',['1','2','3'],[cnAdded,cnAdded,cnAdded]);
  413. DoAdd(3);
  414. DoneExpectValues;
  415. end;
  416. procedure TTestSimpleDictionary.TestValueNotificationDelete;
  417. begin
  418. DoAdd(3);
  419. Dict.OnValueNotify:=@DoValueNotify;
  420. SetExpectValues('Clear',['1','2','3'],[cnRemoved,cnRemoved,cnRemoved],ReverseDeleteNotification);
  421. Dict.Clear;
  422. DoneExpectValues;
  423. end;
  424. procedure TTestSimpleDictionary.TestKeyValueNotificationSet;
  425. begin
  426. DoAdd(3);
  427. Dict.OnValueNotify:=@DoValueNotify;
  428. Dict.OnKeyNotify:=@DoKeyNotify;
  429. SetExpectValues('Set',['2','Six'],[cnRemoved,cnAdded]);
  430. SetExpectKeys('Set',[],[]);
  431. Dict[2]:='Six';
  432. DoneExpectKeys;
  433. DoneExpectValues;
  434. end;
  435. begin
  436. RegisterTest(TTestSimpleDictionary);
  437. end.