FArraySortedDictionaryTest.m 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. /*
  2. * Copyright 2017 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import <XCTest/XCTest.h>
  17. #import "FArraySortedDictionary.h"
  18. #import "FTreeSortedDictionary.h"
  19. @interface FArraySortedDictionaryTests : XCTestCase
  20. @end
  21. @implementation FArraySortedDictionaryTests
  22. - (NSComparator)defaultComparator {
  23. return ^(id obj1, id obj2) {
  24. if ([obj1 respondsToSelector:@selector(compare:)] &&
  25. [obj2 respondsToSelector:@selector(compare:)]) {
  26. return [obj1 compare:obj2];
  27. } else {
  28. if (obj1 < obj2) {
  29. return (NSComparisonResult)NSOrderedAscending;
  30. } else if (obj1 > obj2) {
  31. return (NSComparisonResult)NSOrderedDescending;
  32. } else {
  33. return (NSComparisonResult)NSOrderedSame;
  34. }
  35. }
  36. };
  37. }
  38. - (void)testCreateNode {
  39. FImmutableSortedDictionary *map = [[[FArraySortedDictionary alloc]
  40. initWithComparator:[self defaultComparator]] insertKey:@"key" withValue:@"value"];
  41. XCTAssertEqual(map.count, 1, @"Contains one element");
  42. }
  43. - (void)testGetNilReturnsNil {
  44. FImmutableSortedDictionary *map1 = [[[FArraySortedDictionary alloc]
  45. initWithComparator:[self defaultComparator]] insertKey:@"key" withValue:@"value"];
  46. XCTAssertNil([map1 get:nil]);
  47. FImmutableSortedDictionary *map2 =
  48. [[[FArraySortedDictionary alloc] initWithComparator:^NSComparisonResult(id obj1, id obj2) {
  49. return [obj1 compare:obj2];
  50. }] insertKey:@"key" withValue:@"value"];
  51. XCTAssertNil([map2 get:nil]);
  52. }
  53. - (void)testSearchForSpecificKey {
  54. FImmutableSortedDictionary *map = [[[[FArraySortedDictionary alloc]
  55. initWithComparator:[self defaultComparator]] insertKey:@1 withValue:@1] insertKey:@2
  56. withValue:@2];
  57. XCTAssertEqualObjects([map get:@1], @1, @"Found first object");
  58. XCTAssertEqualObjects([map get:@2], @2, @"Found second object");
  59. XCTAssertNil([map get:@3], @"Properly not found object");
  60. }
  61. - (void)testRemoveKeyValuePair {
  62. FImmutableSortedDictionary *map = [[[[FArraySortedDictionary alloc]
  63. initWithComparator:[self defaultComparator]] insertKey:@1 withValue:@1] insertKey:@2
  64. withValue:@2];
  65. FImmutableSortedDictionary *newMap = [map removeKey:@1];
  66. XCTAssertEqualObjects([newMap get:@2], @2, @"Found second object");
  67. XCTAssertNil([newMap get:@1], @"Properly not found object");
  68. // Make sure the original one is not mutated
  69. XCTAssertEqualObjects([map get:@1], @1, @"Found first object");
  70. XCTAssertEqualObjects([map get:@2], @2, @"Found second object");
  71. }
  72. - (void)testMoreRemovals {
  73. FImmutableSortedDictionary *map =
  74. [[[[[[[[[[[[[[FArraySortedDictionary alloc] initWithComparator:[self defaultComparator]]
  75. insertKey:@1
  76. withValue:@1] insertKey:@50 withValue:@50] insertKey:@3 withValue:@3] insertKey:@4
  77. withValue:@4]
  78. insertKey:@7
  79. withValue:@7] insertKey:@9 withValue:@9] insertKey:@20 withValue:@20] insertKey:@18
  80. withValue:@18]
  81. insertKey:@2
  82. withValue:@2] insertKey:@71 withValue:@71] insertKey:@42 withValue:@42] insertKey:@88
  83. withValue:@88];
  84. XCTAssertNotNil([map get:@7], @"Found object");
  85. XCTAssertNotNil([map get:@3], @"Found object");
  86. XCTAssertNotNil([map get:@1], @"Found object");
  87. FImmutableSortedDictionary *m1 = [map removeKey:@7];
  88. FImmutableSortedDictionary *m2 = [map removeKey:@3];
  89. FImmutableSortedDictionary *m3 = [map removeKey:@1];
  90. XCTAssertNil([m1 get:@7], @"Removed object");
  91. XCTAssertNotNil([m1 get:@3], @"Found object");
  92. XCTAssertNotNil([m1 get:@1], @"Found object");
  93. XCTAssertNil([m2 get:@3], @"Removed object");
  94. XCTAssertNotNil([m2 get:@7], @"Found object");
  95. XCTAssertNotNil([m2 get:@1], @"Found object");
  96. XCTAssertNil([m3 get:@1], @"Removed object");
  97. XCTAssertNotNil([m3 get:@7], @"Found object");
  98. XCTAssertNotNil([m3 get:@3], @"Found object");
  99. }
  100. - (void)testRemovalBug {
  101. FImmutableSortedDictionary *map =
  102. [[[[[FArraySortedDictionary alloc] initWithComparator:[self defaultComparator]]
  103. insertKey:@1
  104. withValue:@1] insertKey:@2 withValue:@2] insertKey:@3 withValue:@3];
  105. XCTAssertEqualObjects([map get:@1], @1, @"Found object");
  106. XCTAssertEqualObjects([map get:@2], @2, @"Found object");
  107. XCTAssertEqualObjects([map get:@3], @3, @"Found object");
  108. FImmutableSortedDictionary *m1 = [map removeKey:@2];
  109. XCTAssertEqualObjects([m1 get:@1], @1, @"Found object");
  110. XCTAssertEqualObjects([m1 get:@3], @3, @"Found object");
  111. XCTAssertNil([m1 get:@2], @"Removed object");
  112. }
  113. - (void)testIncreasing {
  114. int total = 20;
  115. FImmutableSortedDictionary *map =
  116. [[FArraySortedDictionary alloc] initWithComparator:[self defaultComparator]];
  117. for (int i = 0; i < total; i++) {
  118. NSNumber *item = [NSNumber numberWithInt:i];
  119. map = [map insertKey:item withValue:item];
  120. }
  121. XCTAssertTrue([map count] == 20, @"Check if all 100 objects are in the map");
  122. for (int i = 0; i < total; i++) {
  123. NSNumber *item = [NSNumber numberWithInt:i];
  124. map = [map removeKey:item];
  125. }
  126. XCTAssertTrue([map count] == 0, @"Check if all 100 objects were removed");
  127. }
  128. - (void)testOverride {
  129. FImmutableSortedDictionary *map = [[[[FArraySortedDictionary alloc]
  130. initWithComparator:[self defaultComparator]] insertKey:@10 withValue:@10] insertKey:@10
  131. withValue:@8];
  132. XCTAssertEqualObjects([map get:@10], @8, @"Found first object");
  133. }
  134. - (void)testEmpty {
  135. FImmutableSortedDictionary *map = [[[[FArraySortedDictionary alloc]
  136. initWithComparator:[self defaultComparator]] insertKey:@10 withValue:@10] removeKey:@10];
  137. XCTAssertTrue([map isEmpty], @"Properly empty");
  138. }
  139. - (void)testEmptyGet {
  140. FImmutableSortedDictionary *map =
  141. [[FArraySortedDictionary alloc] initWithComparator:[self defaultComparator]];
  142. XCTAssertNil([map get:@"something"], @"Properly nil");
  143. }
  144. - (void)testEmptyCount {
  145. FImmutableSortedDictionary *map =
  146. [[FArraySortedDictionary alloc] initWithComparator:[self defaultComparator]];
  147. XCTAssertTrue([map count] == 0, @"Properly zero count");
  148. }
  149. - (void)testEmptyRemoval {
  150. FImmutableSortedDictionary *map =
  151. [[FArraySortedDictionary alloc] initWithComparator:[self defaultComparator]];
  152. XCTAssertTrue([[map removeKey:@"sometjhing"] count] == 0, @"Properly zero count");
  153. }
  154. - (void)testReverseTraversal {
  155. FImmutableSortedDictionary *map =
  156. [[[[[[[FArraySortedDictionary alloc] initWithComparator:[self defaultComparator]]
  157. insertKey:@1
  158. withValue:@1] insertKey:@5 withValue:@5] insertKey:@3
  159. withValue:@3] insertKey:@2
  160. withValue:@2] insertKey:@4
  161. withValue:@4];
  162. __block int next = 5;
  163. [map enumerateKeysAndObjectsReverse:YES
  164. usingBlock:^(id key, id value, BOOL *stop) {
  165. XCTAssertEqualObjects(key, [NSNumber numberWithInt:next],
  166. @"Properly equal");
  167. next = next - 1;
  168. }];
  169. }
  170. - (void)testInsertionAndRemovalOfAHundredItems {
  171. int N = 20;
  172. NSMutableArray *toInsert = [[NSMutableArray alloc] initWithCapacity:N];
  173. NSMutableArray *toRemove = [[NSMutableArray alloc] initWithCapacity:N];
  174. for (int i = 0; i < N; i++) {
  175. [toInsert addObject:[NSNumber numberWithInt:i]];
  176. [toRemove addObject:[NSNumber numberWithInt:i]];
  177. }
  178. [self shuffleArray:toInsert];
  179. [self shuffleArray:toRemove];
  180. FImmutableSortedDictionary *map =
  181. [[FArraySortedDictionary alloc] initWithComparator:[self defaultComparator]];
  182. // add them to the dictionary
  183. for (int i = 0; i < N; i++) {
  184. map = [map insertKey:[toInsert objectAtIndex:i] withValue:[toInsert objectAtIndex:i]];
  185. }
  186. XCTAssertTrue([map count] == N, @"Check if all N objects are in the map");
  187. // check the order is correct
  188. __block int next = 0;
  189. [map enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
  190. XCTAssertEqualObjects(key, [NSNumber numberWithInt:next], @"Correct key");
  191. XCTAssertEqualObjects(value, [NSNumber numberWithInt:next], @"Correct value");
  192. next = next + 1;
  193. }];
  194. XCTAssertEqual(next, N, @"Check we traversed all of the items");
  195. // remove them
  196. for (int i = 0; i < N; i++) {
  197. map = [map removeKey:[toRemove objectAtIndex:i]];
  198. }
  199. XCTAssertEqual([map count], 0, @"Check we removed all of the items");
  200. }
  201. - (void)shuffleArray:(NSMutableArray *)array {
  202. NSUInteger count = [array count];
  203. for (NSUInteger i = 0; i < count; i++) {
  204. NSInteger nElements = count - i;
  205. NSInteger n = (arc4random() % nElements) + i;
  206. [array exchangeObjectAtIndex:i withObjectAtIndex:n];
  207. }
  208. }
  209. - (void)testOrderIsCorrect {
  210. NSArray *toInsert = [[NSArray alloc] initWithObjects:@1, @7, @8, @5, @2, @6, @4, @0, @3, nil];
  211. FImmutableSortedDictionary *map =
  212. [[FArraySortedDictionary alloc] initWithComparator:[self defaultComparator]];
  213. // add them to the dictionary
  214. for (int i = 0; i < [toInsert count]; i++) {
  215. map = [map insertKey:[toInsert objectAtIndex:i] withValue:[toInsert objectAtIndex:i]];
  216. }
  217. XCTAssertTrue([map count] == [toInsert count], @"Check if all N objects are in the map");
  218. // check the order is correct
  219. __block NSUInteger next = 0;
  220. [map enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
  221. XCTAssertEqualObjects(key, [NSNumber numberWithInteger:next], @"Correct key");
  222. XCTAssertEqualObjects(value, [NSNumber numberWithInteger:next], @"Correct value");
  223. next = next + 1;
  224. }];
  225. XCTAssertEqual(next, [toInsert count], @"Check we traversed all of the items");
  226. }
  227. - (void)testPredecessorKey {
  228. FImmutableSortedDictionary *map =
  229. [[[[[[[[FArraySortedDictionary alloc] initWithComparator:[self defaultComparator]]
  230. insertKey:@1
  231. withValue:@1] insertKey:@50 withValue:@50] insertKey:@3 withValue:@3]
  232. insertKey:@4
  233. withValue:@4] insertKey:@7 withValue:@7] insertKey:@9 withValue:@9];
  234. XCTAssertNil([map getPredecessorKey:@1], @"First object doesn't have a predecessor");
  235. XCTAssertEqualObjects([map getPredecessorKey:@3], @1, @"@1");
  236. XCTAssertEqualObjects([map getPredecessorKey:@4], @3, @"@3");
  237. XCTAssertEqualObjects([map getPredecessorKey:@7], @4, @"@4");
  238. XCTAssertEqualObjects([map getPredecessorKey:@9], @7, @"@7");
  239. XCTAssertEqualObjects([map getPredecessorKey:@50], @9, @"@9");
  240. XCTAssertThrows([map getPredecessorKey:@777], @"Expect exception about nonexistant key");
  241. }
  242. - (void)testEnumerator {
  243. int N = 20;
  244. NSMutableArray *toInsert = [[NSMutableArray alloc] initWithCapacity:N];
  245. for (int i = 0; i < N; i++) {
  246. [toInsert addObject:[NSNumber numberWithInt:i]];
  247. }
  248. [self shuffleArray:toInsert];
  249. FImmutableSortedDictionary *map =
  250. [[FArraySortedDictionary alloc] initWithComparator:[self defaultComparator]];
  251. // add them to the dictionary
  252. for (int i = 0; i < N; i++) {
  253. map = [map insertKey:[toInsert objectAtIndex:i] withValue:[toInsert objectAtIndex:i]];
  254. }
  255. XCTAssertTrue([map count] == N, @"Check if all N objects are in the map");
  256. XCTAssertTrue([map isKindOfClass:[FArraySortedDictionary class]],
  257. @"Make sure we still have a array backed dictionary");
  258. NSEnumerator *enumerator = [map keyEnumerator];
  259. id next = [enumerator nextObject];
  260. int correctValue = 0;
  261. while (next != nil) {
  262. XCTAssertEqualObjects(next, [NSNumber numberWithInt:correctValue], @"Correct key");
  263. next = [enumerator nextObject];
  264. correctValue = correctValue + 1;
  265. }
  266. }
  267. - (void)testReverseEnumerator {
  268. int N = 20;
  269. NSMutableArray *toInsert = [[NSMutableArray alloc] initWithCapacity:N];
  270. for (int i = 0; i < N; i++) {
  271. [toInsert addObject:[NSNumber numberWithInt:i]];
  272. }
  273. [self shuffleArray:toInsert];
  274. FImmutableSortedDictionary *map =
  275. [[FArraySortedDictionary alloc] initWithComparator:[self defaultComparator]];
  276. // add them to the dictionary
  277. for (int i = 0; i < N; i++) {
  278. map = [map insertKey:[toInsert objectAtIndex:i] withValue:[toInsert objectAtIndex:i]];
  279. }
  280. XCTAssertTrue([map count] == N, @"Check if all N objects are in the map");
  281. XCTAssertTrue([map isKindOfClass:[FArraySortedDictionary class]],
  282. @"Make sure we still have a array backed dictionary");
  283. NSEnumerator *enumerator = [map reverseKeyEnumerator];
  284. id next = [enumerator nextObject];
  285. int correctValue = N - 1;
  286. while (next != nil) {
  287. XCTAssertEqualObjects(next, [NSNumber numberWithInt:correctValue], @"Correct key");
  288. next = [enumerator nextObject];
  289. correctValue--;
  290. }
  291. }
  292. - (void)testEnumeratorFrom {
  293. int N = 20;
  294. NSMutableArray *toInsert = [[NSMutableArray alloc] initWithCapacity:N];
  295. for (int i = 0; i < N; i++) {
  296. [toInsert addObject:[NSNumber numberWithInt:i * 2]];
  297. }
  298. [self shuffleArray:toInsert];
  299. FImmutableSortedDictionary *map =
  300. [[FArraySortedDictionary alloc] initWithComparator:[self defaultComparator]];
  301. // add them to the dictionary
  302. for (int i = 0; i < N; i++) {
  303. map = [map insertKey:[toInsert objectAtIndex:i] withValue:[toInsert objectAtIndex:i]];
  304. }
  305. XCTAssertTrue([map count] == N, @"Check if all N objects are in the map");
  306. XCTAssertTrue([map isKindOfClass:[FArraySortedDictionary class]],
  307. @"Make sure we still have a array backed dictionary");
  308. // Test from inbetween keys
  309. {
  310. NSEnumerator *enumerator = [map keyEnumeratorFrom:@11];
  311. id next = [enumerator nextObject];
  312. int correctValue = 12;
  313. while (next != nil) {
  314. XCTAssertEqualObjects(next, [NSNumber numberWithInt:correctValue], @"Correct key");
  315. next = [enumerator nextObject];
  316. correctValue = correctValue + 2;
  317. }
  318. }
  319. // Test from key in map
  320. {
  321. NSEnumerator *enumerator = [map keyEnumeratorFrom:@10];
  322. id next = [enumerator nextObject];
  323. int correctValue = 10;
  324. while (next != nil) {
  325. XCTAssertEqualObjects(next, [NSNumber numberWithInt:correctValue], @"Correct key");
  326. next = [enumerator nextObject];
  327. correctValue = correctValue + 2;
  328. }
  329. }
  330. }
  331. - (void)testReverseEnumeratorFrom {
  332. int N = 20;
  333. NSMutableArray *toInsert = [[NSMutableArray alloc] initWithCapacity:N];
  334. for (int i = 0; i < N; i++) {
  335. [toInsert addObject:[NSNumber numberWithInt:i * 2]];
  336. }
  337. [self shuffleArray:toInsert];
  338. FImmutableSortedDictionary *map =
  339. [[FArraySortedDictionary alloc] initWithComparator:[self defaultComparator]];
  340. // add them to the dictionary
  341. for (int i = 0; i < N; i++) {
  342. map = [map insertKey:[toInsert objectAtIndex:i] withValue:[toInsert objectAtIndex:i]];
  343. }
  344. XCTAssertTrue([map count] == N, @"Check if all N objects are in the map");
  345. XCTAssertTrue([map isKindOfClass:[FArraySortedDictionary class]],
  346. @"Make sure we still have a array backed dictionary");
  347. // Test from inbetween keys
  348. {
  349. NSEnumerator *enumerator = [map reverseKeyEnumeratorFrom:@11];
  350. id next = [enumerator nextObject];
  351. int correctValue = 10;
  352. while (next != nil) {
  353. XCTAssertEqualObjects(next, [NSNumber numberWithInt:correctValue], @"Correct key");
  354. next = [enumerator nextObject];
  355. correctValue = correctValue - 2;
  356. }
  357. }
  358. // Test from key in map
  359. {
  360. NSEnumerator *enumerator = [map reverseKeyEnumeratorFrom:@10];
  361. id next = [enumerator nextObject];
  362. int correctValue = 10;
  363. while (next != nil) {
  364. XCTAssertEqualObjects(next, [NSNumber numberWithInt:correctValue], @"Correct key");
  365. next = [enumerator nextObject];
  366. correctValue = correctValue - 2;
  367. }
  368. }
  369. }
  370. - (void)testConversionToTreeMap {
  371. int N = SORTED_DICTIONARY_ARRAY_TO_RB_TREE_SIZE_THRESHOLD + 5;
  372. NSMutableArray *toInsert = [[NSMutableArray alloc] initWithCapacity:N];
  373. for (int i = 0; i < N; i++) {
  374. [toInsert addObject:[NSNumber numberWithInt:i]];
  375. }
  376. [self shuffleArray:toInsert];
  377. FImmutableSortedDictionary *dict =
  378. [FImmutableSortedDictionary dictionaryWithComparator:[self defaultComparator]];
  379. for (int i = 0; i < N; i++) {
  380. dict = [dict insertKey:toInsert[i] withValue:toInsert[i]];
  381. if (i < SORTED_DICTIONARY_ARRAY_TO_RB_TREE_SIZE_THRESHOLD) {
  382. XCTAssertTrue([dict isKindOfClass:[FArraySortedDictionary class]],
  383. @"We're below the threshold we should be an array backed implementation");
  384. XCTAssertEqual(dict.count, i + 1, @"Size doesn't match");
  385. } else {
  386. XCTAssertTrue([dict isKindOfClass:[FTreeSortedDictionary class]],
  387. @"We're above the threshold we should be a tree backed implementation");
  388. XCTAssertEqual(dict.count, i + 1, @"Size doesn't match");
  389. }
  390. }
  391. // check the order is correct
  392. __block NSUInteger next = 0;
  393. [dict enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
  394. XCTAssertEqualObjects(key, [NSNumber numberWithInteger:next], @"Correct key");
  395. XCTAssertEqualObjects(value, [NSNumber numberWithInteger:next], @"Correct value");
  396. next = next + 1;
  397. }];
  398. }
  399. @end