tests.generics.hashmaps.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. {
  2. This file is part of the Free Pascal/NewPascal run time library.
  3. Copyright (c) 2018 by Maciej Izak (hnb),
  4. member of the NewPascal development team (http://newpascal.org)
  5. Copyright(c) 2004-2018 DaThoX
  6. It contains tests for the Free Pascal generics library
  7. See the file COPYING.FPC, included in this distribution,
  8. for details about the copyright.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  12. Acknowledgment
  13. Thanks to Sphere 10 Software (http://sphere10.com) for sponsoring
  14. many new types, tests and major refactoring of entire library
  15. Thanks to Castle Game Engine (https://castle-engine.sourceforge.io)
  16. Part of tests for this module was copied from Castle Game Engine tests
  17. **********************************************************************}
  18. unit tests.generics.hashmaps;
  19. {$mode delphi}
  20. {$MACRO ON}
  21. interface
  22. uses
  23. fpcunit, testregistry, testutils, tests.generics.utils,
  24. typinfo, Classes, SysUtils, StrUtils, Generics.Collections, Generics.Defaults;
  25. type
  26. PCollectionNotification = ^TCollectionNotification;
  27. { TTestHashMaps }
  28. TTestHashMaps= class(TTestCollections)
  29. private
  30. procedure CountAsKey_Check(const AWhat: string; AValue, AExpectedValue: Integer;
  31. AAction: PCollectionNotification);
  32. procedure CountAsKey_Notify(const AKind: string; ASender: TObject; const AItem: Integer; AAction: TCollectionNotification);
  33. procedure CountAsKey_NotifyValue(ASender: TObject; const AItem: Integer; AAction: TCollectionNotification);
  34. procedure CountAsKey_NotifyKey(ASender: TObject; const AItem: Integer; AAction: TCollectionNotification);
  35. published
  36. procedure Test_CountAsKey_OpenAddressingLP;
  37. procedure Test_CountAsKey_OpenAddressingLPT;
  38. procedure Test_CountAsKey_OpenAddressingQP;
  39. procedure Test_CountAsKey_OpenAddressingDH;
  40. procedure Test_CountAsKey_CuckooD2;
  41. procedure Test_CountAsKey_CuckooD4;
  42. procedure Test_CountAsKey_CuckooD6;
  43. procedure Test_OpenAddressingLP_Notification;
  44. procedure Test_OpenAddressingLPT_Notification;
  45. procedure Test_OpenAddressingQP_Notification;
  46. procedure Test_OpenAddressingDH_Notification;
  47. procedure Test_CuckooD2_Notification;
  48. procedure Test_CuckooD4_Notification;
  49. procedure Test_CuckooD6_Notification;
  50. procedure Test_OpenAddressingLP_TrimExcess;
  51. procedure Test_CuckooD2_TrimExcess;
  52. procedure Test_ObjectDictionary;
  53. procedure Test_TryAddOrSetOrGetValue;
  54. procedure Test_TryGetValueEmpty_xxHash32;
  55. procedure Test_TryGetValueEmpty_xxHash32Pascal;
  56. end;
  57. implementation
  58. { TTestHashMaps }
  59. procedure TTestHashMaps.CountAsKey_Check(const AWhat: string; AValue, AExpectedValue: Integer;
  60. AAction: PCollectionNotification);
  61. var
  62. LCollectionNotificationStr: string;
  63. begin
  64. if Assigned(AAction) then
  65. LCollectionNotificationStr := GetEnumName(TypeInfo(TCollectionNotification), Ord(AAction^));
  66. AssertEquals(AWhat + LCollectionNotificationStr, AExpectedValue, AValue);
  67. end;
  68. procedure TTestHashMaps.CountAsKey_Notify(const AKind: string; ASender: TObject; const
  69. AItem: Integer; AAction: TCollectionNotification);
  70. var
  71. LCount: Integer;
  72. begin
  73. CountAsKey_Check('Item ('+AKind+')', AItem, 0, @AAction);
  74. LCount := TCustomDictionary<Integer, Integer, TDefaultHashFactory>(ASender).Count;
  75. case AAction of
  76. cnAdded:
  77. CountAsKey_Check('Count', LCount, 1, @AAction);
  78. cnRemoved:
  79. CountAsKey_Check('Count', LCount, 0, @AAction);
  80. cnExtracted: Halt(4);
  81. end;
  82. end;
  83. procedure TTestHashMaps.CountAsKey_NotifyValue(ASender: TObject; const AItem: Integer;
  84. AAction: TCollectionNotification);
  85. begin
  86. CountAsKey_Notify('Value', ASender, AItem, AAction);
  87. end;
  88. procedure TTestHashMaps.CountAsKey_NotifyKey(ASender: TObject; const AItem: Integer;
  89. AAction: TCollectionNotification);
  90. begin
  91. CountAsKey_Notify('Key', ASender, AItem, AAction);
  92. end;
  93. {$DEFINE TEST_COUNT_AS_KEY :=
  94. LDictionary.OnKeyNotify := CountAsKey_NotifyKey;
  95. LDictionary.OnValueNotify := CountAsKey_NotifyValue;
  96. CountAsKey_Check('Count', LDictionary.Count, 0, nil);
  97. LDictionary.Add(LDictionary.Count,LDictionary.Count);
  98. CountAsKey_Check('Item', LDictionary[0], 0, nil);
  99. LDictionary.Free
  100. }
  101. procedure TTestHashMaps.Test_CountAsKey_OpenAddressingLP;
  102. var
  103. LDictionary: TOpenAddressingLP<Integer, Integer>;
  104. begin
  105. // TOpenAddressingLP
  106. LDictionary := TOpenAddressingLP<Integer, Integer>.Create;
  107. TEST_COUNT_AS_KEY;
  108. end;
  109. procedure TTestHashMaps.Test_CountAsKey_OpenAddressingLPT;
  110. var
  111. LDictionary: TOpenAddressingLPT<Integer, Integer>;
  112. begin
  113. // TOpenAddressingLPT
  114. LDictionary := TOpenAddressingLPT<Integer, Integer>.Create;
  115. TEST_COUNT_AS_KEY;
  116. end;
  117. procedure TTestHashMaps.Test_CountAsKey_OpenAddressingQP;
  118. var
  119. LDictionary: TOpenAddressingQP<Integer, Integer>;
  120. begin
  121. // TOpenAddressingQP
  122. LDictionary := TOpenAddressingQP<Integer, Integer>.Create;
  123. TEST_COUNT_AS_KEY;
  124. end;
  125. procedure TTestHashMaps.Test_CountAsKey_OpenAddressingDH;
  126. var
  127. LDictionary: TOpenAddressingDH<Integer, Integer>;
  128. begin
  129. // TOpenAddressingDH
  130. LDictionary := TOpenAddressingDH<Integer, Integer>.Create;
  131. TEST_COUNT_AS_KEY;
  132. end;
  133. procedure TTestHashMaps.Test_CountAsKey_CuckooD2;
  134. var
  135. LDictionary: TCuckooD2<Integer, Integer>;
  136. begin
  137. // TCuckooD2
  138. LDictionary := TCuckooD2<Integer, Integer>.Create;
  139. TEST_COUNT_AS_KEY;
  140. end;
  141. procedure TTestHashMaps.Test_CountAsKey_CuckooD4;
  142. var
  143. LDictionary: TCuckooD4<Integer, Integer>;
  144. begin
  145. // TCuckooD4
  146. LDictionary := TCuckooD4<Integer, Integer>.Create;
  147. TEST_COUNT_AS_KEY;
  148. end;
  149. procedure TTestHashMaps.Test_CountAsKey_CuckooD6;
  150. var
  151. LDictionary: TCuckooD6<Integer, Integer>;
  152. begin
  153. // TCuckooD6
  154. LDictionary := TCuckooD6<Integer, Integer>.Create;
  155. TEST_COUNT_AS_KEY;
  156. end;
  157. {$DEFINE TEST_NOTIFICATIONS :=
  158. try
  159. LDictionary.OnKeyNotify := NotifyTestStr;
  160. LDictionary.OnValueNotify := NotifyTestStr;
  161. // Add
  162. NotificationAdd(LDictionary, ['Aaa', 'Bbb', 'Ccc', 'Ddd', 'Eee', 'Fff'], cnAdded);
  163. LDictionary.Add('Aaa', 'Bbb');
  164. LDictionary.Add('Ccc', 'Ddd');
  165. LDictionary.Add('Eee', 'Fff');
  166. AssertNotificationsExecutedStr;
  167. // Remove and ExtractPair
  168. NotificationAdd(LDictionary, ['Ccc', 'Ddd'], cnRemoved);
  169. LDictionary.Remove('Ccc');
  170. AssertNotificationsExecutedStr;
  171. NotificationAdd(LDictionary, ['Aaa', 'Bbb'], cnExtracted);
  172. with LDictionary.ExtractPair('Aaa') do
  173. begin
  174. AssertEquals(Key, 'Aaa');
  175. AssertEquals(Value, 'Bbb');
  176. end;
  177. AssertNotificationsExecutedStr;
  178. // Clear
  179. NotificationAdd(LDictionary, ['Eee', 'Fff'], cnRemoved);
  180. LDictionary.Clear;
  181. AssertNotificationsExecutedStr;
  182. // SetItem
  183. NotificationAdd(LDictionary, ['FPC', 'Polandball'], cnAdded);
  184. LDictionary.AddOrSetValue('FPC', 'Polandball');
  185. AssertNotificationsExecutedStr;
  186. NotificationAdd(LDictionary, 'Polandball', cnRemoved);
  187. NotificationAdd(LDictionary, 'xD', cnAdded);
  188. NotificationAdd(LDictionary, 'xD', cnRemoved);
  189. NotificationAdd(LDictionary, 'Polandball', cnAdded);
  190. LDictionary['FPC'] := 'xD';
  191. LDictionary.AddOrSetValue('FPC', 'Polandball');
  192. AssertNotificationsExecutedStr;
  193. finally
  194. NotificationAdd(LDictionary, ['FPC', 'Polandball'], cnRemoved);
  195. LDictionary.Free;
  196. AssertNotificationsExecutedStr;
  197. end
  198. }
  199. procedure TTestHashMaps.Test_OpenAddressingLP_Notification;
  200. var
  201. LDictionary: TOpenAddressingLP<string, string>;
  202. begin
  203. LDictionary := TOpenAddressingLP<string, string>.Create;
  204. TEST_NOTIFICATIONS;
  205. end;
  206. procedure TTestHashMaps.Test_OpenAddressingLPT_Notification;
  207. var
  208. LDictionary: TOpenAddressingLPT<string, string>;
  209. begin
  210. LDictionary := TOpenAddressingLPT<string, string>.Create;
  211. TEST_NOTIFICATIONS;
  212. end;
  213. procedure TTestHashMaps.Test_OpenAddressingQP_Notification;
  214. var
  215. LDictionary: TOpenAddressingQP<string, string>;
  216. begin
  217. LDictionary := TOpenAddressingQP<string, string>.Create;
  218. TEST_NOTIFICATIONS;
  219. end;
  220. procedure TTestHashMaps.Test_OpenAddressingDH_Notification;
  221. var
  222. LDictionary: TOpenAddressingDH<string, string>;
  223. begin
  224. LDictionary := TOpenAddressingDH<string, string>.Create;
  225. TEST_NOTIFICATIONS;
  226. end;
  227. procedure TTestHashMaps.Test_CuckooD2_Notification;
  228. var
  229. LDictionary: TCuckooD2<string, string>;
  230. begin
  231. LDictionary := TCuckooD2<string, string>.Create;
  232. TEST_NOTIFICATIONS;
  233. end;
  234. procedure TTestHashMaps.Test_CuckooD4_Notification;
  235. var
  236. LDictionary: TCuckooD4<string, string>;
  237. begin
  238. LDictionary := TCuckooD4<string, string>.Create;
  239. TEST_NOTIFICATIONS;
  240. end;
  241. procedure TTestHashMaps.Test_CuckooD6_Notification;
  242. var
  243. LDictionary: TCuckooD6<string, string>;
  244. begin
  245. LDictionary := TCuckooD6<string, string>.Create;
  246. TEST_NOTIFICATIONS;
  247. end;
  248. {$DEFINE TEST_TRIMEXCESS :=
  249. try
  250. for i := 1 to 8 do
  251. LDictionary.Add(i, EmptyRecord);
  252. LDictionary.Remove(1);
  253. CheckNotEquals(LDictionary.Capacity, LDictionary.Count);
  254. LDictionary.TrimExcess;
  255. AssertEquals(LDictionary.Capacity, 8);
  256. finally
  257. LDictionary.Free;
  258. end;
  259. }
  260. procedure TTestHashMaps.Test_OpenAddressingLP_TrimExcess;
  261. var
  262. LDictionary: TOpenAddressingLP<Integer, TEmptyRecord>;
  263. i: Integer;
  264. begin
  265. LDictionary := TOpenAddressingLP<Integer, TEmptyRecord>.Create;
  266. TEST_TRIMEXCESS;
  267. end;
  268. procedure TTestHashMaps.Test_CuckooD2_TrimExcess;
  269. var
  270. LDictionary: TCuckooD2<Integer, TEmptyRecord>;
  271. i: Integer;
  272. begin
  273. LDictionary := TCuckooD2<Integer, TEmptyRecord>.Create;
  274. TEST_TRIMEXCESS;
  275. end;
  276. procedure TTestHashMaps.Test_ObjectDictionary;
  277. begin
  278. with TObjectOpenAddressingLP<TGUID, TGUID>.Create do Free;
  279. with TObjectCuckooD2<TGUID, TGUID>.Create do Free;
  280. end;
  281. procedure TTestHashMaps.Test_TryAddOrSetOrGetValue;
  282. // modified test from Castle Game Engine (https://castle-engine.sourceforge.io)
  283. var
  284. LObjects: TDictionary<string, TObject>;
  285. LObject, LFoundObject: TObject;
  286. begin
  287. LObjects := TDictionary<string, TObject>.Create;
  288. try
  289. LObjects.TryGetValue('blah', LFoundObject);
  290. AssertTrue(nil = LFoundObject);
  291. LObject := TObject.Create;
  292. LObjects.AddOrSetValue('nope', LObject);
  293. LObjects.TryGetValue('blah', LFoundObject);
  294. AssertTrue(nil = LFoundObject);
  295. LObject := TObject.Create;
  296. LObjects.AddOrSetValue('blah', LObject);
  297. LObjects.TryGetValue('blah', LFoundObject);
  298. AssertTrue(LObject = LFoundObject);
  299. LObjects.Remove('blah');
  300. LObject.Free;
  301. LObjects.TryGetValue('blah', LFoundObject);
  302. AssertTrue(nil = LFoundObject);
  303. LObjects['nope'].Free;
  304. finally
  305. FreeAndNil(LObjects)
  306. end;
  307. end;
  308. // modified test from Castle Game Engine (https://castle-engine.io/)
  309. {$DEFINE TEST_TRYGETEMPTYVALUE :=
  310. try
  311. Map.AddOrSetValue('some key', 'some value');
  312. B := Map.TryGetValue('some key', V);
  313. AssertTrue(B);
  314. AssertEquals('some value', V);
  315. B := Map.TryGetValue('some other key', V);
  316. AssertFalse(B);
  317. B := Map.TryGetValue('', V);
  318. AssertFalse(B);
  319. finally
  320. FreeAndNil(Map)
  321. end;
  322. }
  323. procedure TTestHashMaps.Test_TryGetValueEmpty_xxHash32;
  324. var
  325. Map: TOpenAddressingLP<string, string, TxxHash32HashFactory>;
  326. V: String; B: Boolean;
  327. begin
  328. Map := TOpenAddressingLP<string, string, TxxHash32HashFactory>.Create;
  329. TEST_TRYGETEMPTYVALUE;
  330. end;
  331. procedure TTestHashMaps.Test_TryGetValueEmpty_xxHash32Pascal;
  332. var
  333. Map: TOpenAddressingLP<string, string, TxxHash32PascalHashFactory>;
  334. V: String; B: Boolean;
  335. begin
  336. Map := TOpenAddressingLP<string, string, TxxHash32PascalHashFactory>.Create;
  337. TEST_TRYGETEMPTYVALUE;
  338. end;
  339. begin
  340. RegisterTest(TTestHashMaps);
  341. end.