tests.generics.hashmaps.pas 10 KB

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