FSTArraySortedDictionaryTests.m 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. #import "Firestore/third_party/Immutable/FSTArraySortedDictionary.h"
  2. #import <XCTest/XCTest.h>
  3. @interface FSTArraySortedDictionary (Test)
  4. // Override methods to return subtype.
  5. - (FSTArraySortedDictionary *)dictionaryBySettingObject:(id)aValue forKey:(id)aKey;
  6. - (FSTArraySortedDictionary *)dictionaryByRemovingObjectForKey:(id)aKey;
  7. @end
  8. @interface FSTArraySortedDictionaryTests : XCTestCase
  9. @end
  10. @implementation FSTArraySortedDictionaryTests
  11. - (NSComparator)defaultComparator {
  12. return ^(id obj1, id obj2) {
  13. NSAssert([obj1 respondsToSelector:@selector(compare:)] &&
  14. [obj2 respondsToSelector:@selector(compare:)],
  15. @"Objects must support compare: %@ %@", obj1, obj2);
  16. return [obj1 compare:obj2];
  17. };
  18. }
  19. - (void)testSearchForSpecificKey {
  20. FSTArraySortedDictionary *map =
  21. [[FSTArraySortedDictionary alloc] initWithComparator:[self defaultComparator]];
  22. map = [map dictionaryBySettingObject:@1 forKey:@1];
  23. map = [map dictionaryBySettingObject:@2 forKey:@2];
  24. XCTAssertEqualObjects([map objectForKey:@1], @1, @"Found first object");
  25. XCTAssertEqualObjects([map objectForKey:@2], @2, @"Found second object");
  26. XCTAssertEqualObjects(map[@1], @1, @"Found first object");
  27. XCTAssertEqualObjects(map[@2], @2, @"Found second object");
  28. XCTAssertNil([map objectForKey:@3], @"Properly not found object");
  29. }
  30. - (void)testRemoveKeyValuePair {
  31. FSTArraySortedDictionary *map =
  32. [[FSTArraySortedDictionary alloc] initWithComparator:[self defaultComparator]];
  33. map = [map dictionaryBySettingObject:@1 forKey:@1];
  34. map = [map dictionaryBySettingObject:@2 forKey:@2];
  35. FSTImmutableSortedDictionary *newMap = [map dictionaryByRemovingObjectForKey:@1];
  36. XCTAssertEqualObjects([newMap objectForKey:@2], @2, @"Found second object");
  37. XCTAssertNil([newMap objectForKey:@1], @"Properly not found object");
  38. // Make sure the original one is not mutated
  39. XCTAssertEqualObjects([map objectForKey:@1], @1, @"Found first object");
  40. XCTAssertEqualObjects([map objectForKey:@2], @2, @"Found second object");
  41. }
  42. - (void)testMoreRemovals {
  43. FSTArraySortedDictionary *map =
  44. [[FSTArraySortedDictionary alloc] initWithComparator:[self defaultComparator]];
  45. map = [map dictionaryBySettingObject:@1 forKey:@1];
  46. map = [map dictionaryBySettingObject:@50 forKey:@50];
  47. map = [map dictionaryBySettingObject:@3 forKey:@3];
  48. map = [map dictionaryBySettingObject:@4 forKey:@4];
  49. map = [map dictionaryBySettingObject:@7 forKey:@7];
  50. map = [map dictionaryBySettingObject:@9 forKey:@9];
  51. map = [map dictionaryBySettingObject:@1 forKey:@20];
  52. map = [map dictionaryBySettingObject:@18 forKey:@18];
  53. map = [map dictionaryBySettingObject:@3 forKey:@2];
  54. map = [map dictionaryBySettingObject:@4 forKey:@71];
  55. map = [map dictionaryBySettingObject:@7 forKey:@42];
  56. map = [map dictionaryBySettingObject:@9 forKey:@88];
  57. XCTAssertNotNil([map objectForKey:@7], @"Found object");
  58. XCTAssertNotNil([map objectForKey:@3], @"Found object");
  59. XCTAssertNotNil([map objectForKey:@1], @"Found object");
  60. FSTImmutableSortedDictionary *m1 = [map dictionaryByRemovingObjectForKey:@7];
  61. FSTImmutableSortedDictionary *m2 = [map dictionaryByRemovingObjectForKey:@3];
  62. FSTImmutableSortedDictionary *m3 = [map dictionaryByRemovingObjectForKey:@1];
  63. XCTAssertNil([m1 objectForKey:@7], @"Removed object");
  64. XCTAssertNotNil([m1 objectForKey:@3], @"Found object");
  65. XCTAssertNotNil([m1 objectForKey:@1], @"Found object");
  66. XCTAssertNil([m2 objectForKey:@3], @"Removed object");
  67. XCTAssertNotNil([m2 objectForKey:@7], @"Found object");
  68. XCTAssertNotNil([m2 objectForKey:@1], @"Found object");
  69. XCTAssertNil([m3 objectForKey:@1], @"Removed object");
  70. XCTAssertNotNil([m3 objectForKey:@7], @"Found object");
  71. XCTAssertNotNil([m3 objectForKey:@3], @"Found object");
  72. }
  73. - (void)testRemovalBug {
  74. FSTArraySortedDictionary *map =
  75. [[FSTArraySortedDictionary alloc] initWithComparator:[self defaultComparator]];
  76. map = [map dictionaryBySettingObject:@1 forKey:@1];
  77. map = [map dictionaryBySettingObject:@2 forKey:@2];
  78. map = [map dictionaryBySettingObject:@3 forKey:@3];
  79. XCTAssertEqualObjects([map objectForKey:@1], @1, @"Found object");
  80. XCTAssertEqualObjects([map objectForKey:@2], @2, @"Found object");
  81. XCTAssertEqualObjects([map objectForKey:@3], @3, @"Found object");
  82. FSTImmutableSortedDictionary *m1 = [map dictionaryByRemovingObjectForKey:@2];
  83. XCTAssertEqualObjects([m1 objectForKey:@1], @1, @"Found object");
  84. XCTAssertEqualObjects([m1 objectForKey:@3], @3, @"Found object");
  85. XCTAssertNil([m1 objectForKey:@2], @"Removed object");
  86. }
  87. - (void)testIncreasing {
  88. int total = 100;
  89. FSTArraySortedDictionary *map =
  90. [[FSTArraySortedDictionary alloc] initWithComparator:[self defaultComparator]];
  91. for (int i = 0; i < total; i++) {
  92. NSNumber *item = @(i);
  93. map = [map dictionaryBySettingObject:item forKey:item];
  94. }
  95. XCTAssertTrue(map.count == 100, @"Check if all 100 objects are in the map");
  96. for (int i = 0; i < total; i++) {
  97. NSNumber *item = @(i);
  98. map = [map dictionaryByRemovingObjectForKey:item];
  99. }
  100. XCTAssertTrue(map.count == 0, @"Check if all 100 objects were removed");
  101. }
  102. - (void)testOverride {
  103. FSTArraySortedDictionary *map =
  104. [[FSTArraySortedDictionary alloc] initWithComparator:[self defaultComparator]];
  105. map = [map dictionaryBySettingObject:@10 forKey:@10];
  106. map = [map dictionaryBySettingObject:@8 forKey:@10];
  107. XCTAssertEqualObjects([map objectForKey:@10], @8, @"Found first object");
  108. }
  109. - (void)testEmpty {
  110. FSTArraySortedDictionary *map =
  111. [[FSTArraySortedDictionary alloc] initWithComparator:[self defaultComparator]];
  112. map = [map dictionaryBySettingObject:@10 forKey:@10];
  113. map = [map dictionaryByRemovingObjectForKey:@10];
  114. XCTAssertTrue([map isEmpty], @"Properly empty");
  115. }
  116. - (void)testEmptyGet {
  117. FSTArraySortedDictionary *map =
  118. [[FSTArraySortedDictionary alloc] initWithComparator:[self defaultComparator]];
  119. XCTAssertNil([map objectForKey:@"something"], @"Properly nil");
  120. }
  121. - (void)testEmptyCount {
  122. FSTArraySortedDictionary *map =
  123. [[FSTArraySortedDictionary alloc] initWithComparator:[self defaultComparator]];
  124. XCTAssertTrue([map count] == 0, @"Properly zero count");
  125. }
  126. - (void)testEmptyRemoval {
  127. FSTArraySortedDictionary *map =
  128. [[FSTArraySortedDictionary alloc] initWithComparator:[self defaultComparator]];
  129. map = [map dictionaryByRemovingObjectForKey:@"something"];
  130. XCTAssertTrue(map.count == 0, @"Properly zero count");
  131. }
  132. - (void)testReverseTraversal {
  133. FSTArraySortedDictionary *map =
  134. [[FSTArraySortedDictionary alloc] initWithComparator:[self defaultComparator]];
  135. map = [map dictionaryBySettingObject:@1 forKey:@1];
  136. map = [map dictionaryBySettingObject:@5 forKey:@5];
  137. map = [map dictionaryBySettingObject:@3 forKey:@3];
  138. map = [map dictionaryBySettingObject:@2 forKey:@2];
  139. map = [map dictionaryBySettingObject:@4 forKey:@4];
  140. __block int next = 5;
  141. [map enumerateKeysAndObjectsReverse:YES
  142. usingBlock:^(id key, id value, BOOL *stop) {
  143. XCTAssertEqualObjects(key, @(next), @"Properly equal");
  144. next = next - 1;
  145. }];
  146. }
  147. - (void)testInsertionAndRemovalOfAHundredItems {
  148. NSUInteger n = 100;
  149. NSMutableArray *toInsert = [NSMutableArray arrayWithCapacity:n];
  150. NSMutableArray *toRemove = [NSMutableArray arrayWithCapacity:n];
  151. for (NSUInteger i = 0; i < n; i++) {
  152. [toInsert addObject:@(i)];
  153. [toRemove addObject:@(i)];
  154. }
  155. [self shuffleArray:toInsert];
  156. [self shuffleArray:toRemove];
  157. FSTArraySortedDictionary *map =
  158. [[FSTArraySortedDictionary alloc] initWithComparator:[self defaultComparator]];
  159. // add them to the dictionary
  160. for (NSUInteger i = 0; i < n; i++) {
  161. map = [map dictionaryBySettingObject:toInsert[i] forKey:toInsert[i]];
  162. }
  163. XCTAssertTrue(map.count == n, @"Check if all N objects are in the map");
  164. // check the order is correct
  165. __block int next = 0;
  166. [map enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
  167. XCTAssertEqualObjects(key, @(next), @"Correct key");
  168. XCTAssertEqualObjects(value, @(next), @"Correct value");
  169. next = next + 1;
  170. }];
  171. XCTAssertEqual(next, n, @"Check we traversed all of the items");
  172. // remove them
  173. for (NSUInteger i = 0; i < n; i++) {
  174. map = [map dictionaryByRemovingObjectForKey:toRemove[i]];
  175. }
  176. XCTAssertEqual(map.count, 0, @"Check we removed all of the items");
  177. }
  178. - (void)shuffleArray:(NSMutableArray *)array {
  179. NSUInteger count = array.count;
  180. for (NSUInteger i = 0; i < count; i++) {
  181. NSUInteger nElements = count - i;
  182. NSUInteger n = (arc4random() % nElements) + i;
  183. [array exchangeObjectAtIndex:i withObjectAtIndex:n];
  184. }
  185. }
  186. - (void)testBalanceProblem {
  187. NSArray *toInsert = @[ @1, @7, @8, @5, @2, @6, @4, @0, @3 ];
  188. FSTArraySortedDictionary *map =
  189. [[FSTArraySortedDictionary alloc] initWithComparator:[self defaultComparator]];
  190. // add them to the dictionary
  191. for (NSUInteger i = 0; i < toInsert.count; i++) {
  192. map = [map dictionaryBySettingObject:toInsert[i] forKey:toInsert[i]];
  193. }
  194. XCTAssertTrue(map.count == toInsert.count, @"Check if all N objects are in the map");
  195. // check the order is correct
  196. __block int next = 0;
  197. [map enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
  198. XCTAssertEqualObjects(key, @(next), @"Correct key");
  199. XCTAssertEqualObjects(value, @(next), @"Correct value");
  200. next = next + 1;
  201. }];
  202. XCTAssertEqual((int)next, (int)toInsert.count, @"Check we traversed all of the items");
  203. }
  204. // This is a macro instead of a method so that the failures show on the proper lines.
  205. #define ASSERT_ENUMERATOR(enumerator, start, end, step) \
  206. do { \
  207. NSEnumerator *e = (enumerator); \
  208. id next = nil; \
  209. for (NSUInteger i = (start); i != (end); i += (step)) { \
  210. next = [e nextObject]; \
  211. XCTAssertNotNil(next, @"expected %lu. got nil.", (unsigned long)i); \
  212. XCTAssertEqualObjects(next, @(i), "expected %lu. got %@.", (unsigned long)i, next); \
  213. } \
  214. next = [e nextObject]; \
  215. XCTAssertNil(next, @"expected nil. got %@.", next); \
  216. } while (0)
  217. - (void)testEnumerator {
  218. NSUInteger n = 100;
  219. NSMutableArray *toInsert = [NSMutableArray arrayWithCapacity:n];
  220. for (int i = 0; i < n; i++) {
  221. [toInsert addObject:@(i)];
  222. }
  223. [self shuffleArray:toInsert];
  224. FSTArraySortedDictionary *map =
  225. [[FSTArraySortedDictionary alloc] initWithComparator:self.defaultComparator];
  226. // add them to the dictionary
  227. for (NSUInteger i = 0; i < n; i++) {
  228. map = [map dictionaryBySettingObject:toInsert[i] forKey:toInsert[i]];
  229. }
  230. XCTAssertTrue(map.count == n, @"Check if all N objects are in the map");
  231. ASSERT_ENUMERATOR([map keyEnumerator], 0, 100, 1);
  232. }
  233. - (void)testReverseEnumerator {
  234. NSUInteger n = 20;
  235. NSMutableArray *toInsert = [NSMutableArray arrayWithCapacity:n];
  236. for (int i = 0; i < n; i++) {
  237. [toInsert addObject:@(i)];
  238. }
  239. [self shuffleArray:toInsert];
  240. FSTImmutableSortedDictionary *map =
  241. [[FSTArraySortedDictionary alloc] initWithComparator:[self defaultComparator]];
  242. // Add them to the dictionary.
  243. for (NSUInteger i = 0; i < n; i++) {
  244. map = [map dictionaryBySettingObject:toInsert[i] forKey:toInsert[i]];
  245. }
  246. XCTAssertTrue(map.count == n, @"Check if all N objects are in the map");
  247. XCTAssertTrue([map isKindOfClass:FSTArraySortedDictionary.class],
  248. @"Make sure we still have a array backed dictionary");
  249. ASSERT_ENUMERATOR([map reverseKeyEnumerator], n - 1, -1, -1);
  250. }
  251. - (void)testEnumeratorFrom {
  252. // Create a dictionary with the even numbers in [2, 42).
  253. NSUInteger n = 20;
  254. NSMutableArray *toInsert = [NSMutableArray arrayWithCapacity:n];
  255. for (int i = 0; i < n; i++) {
  256. [toInsert addObject:@(i * 2 + 2)];
  257. }
  258. [self shuffleArray:toInsert];
  259. FSTImmutableSortedDictionary *map =
  260. [[FSTArraySortedDictionary alloc] initWithComparator:[self defaultComparator]];
  261. // Add them to the dictionary.
  262. for (NSUInteger i = 0; i < n; i++) {
  263. map = [map dictionaryBySettingObject:toInsert[i] forKey:toInsert[i]];
  264. }
  265. XCTAssertTrue(map.count == n, @"Check if all N objects are in the map");
  266. XCTAssertTrue([map isKindOfClass:FSTArraySortedDictionary.class],
  267. @"Make sure we still have a array backed dictionary");
  268. // Test from before keys.
  269. ASSERT_ENUMERATOR([map keyEnumeratorFrom:@0], 2, n * 2 + 2, 2);
  270. // Test from after keys.
  271. ASSERT_ENUMERATOR([map keyEnumeratorFrom:@100], 0, 0, 2);
  272. // Test from key in map.
  273. ASSERT_ENUMERATOR([map keyEnumeratorFrom:@10], 10, n * 2 + 2, 2);
  274. // Test from in between keys.
  275. ASSERT_ENUMERATOR([map keyEnumeratorFrom:@11], 12, n * 2 + 2, 2);
  276. }
  277. - (void)testEnumeratorFromTo {
  278. // Create a dictionary with the even numbers in [2, 42).
  279. NSUInteger n = 20;
  280. NSMutableArray *toInsert = [NSMutableArray arrayWithCapacity:n];
  281. for (int i = 0; i < n; i++) {
  282. [toInsert addObject:@(i * 2 + 2)];
  283. }
  284. [self shuffleArray:toInsert];
  285. FSTImmutableSortedDictionary *map =
  286. [[FSTArraySortedDictionary alloc] initWithComparator:[self defaultComparator]];
  287. // Add them to the dictionary.
  288. for (NSUInteger i = 0; i < n; i++) {
  289. map = [map dictionaryBySettingObject:toInsert[i] forKey:toInsert[i]];
  290. }
  291. XCTAssertTrue(map.count == n, @"Check if all N objects are in the map");
  292. XCTAssertTrue([map isKindOfClass:FSTArraySortedDictionary.class],
  293. @"Make sure we still have an array backed dictionary");
  294. ASSERT_ENUMERATOR([map keyEnumeratorFrom:@0 to:@1], 2, 2, 2); // before to before
  295. ASSERT_ENUMERATOR([map keyEnumeratorFrom:@0 to:@100], 2, n * 2 + 2, 2); // before to after
  296. ASSERT_ENUMERATOR([map keyEnumeratorFrom:@0 to:@6], 2, 6, 2); // before to key in map
  297. ASSERT_ENUMERATOR([map keyEnumeratorFrom:@0 to:@7], 2, 8, 2); // before to in between keys
  298. ASSERT_ENUMERATOR([map keyEnumeratorFrom:@100 to:@0], 2, 2, 2); // after to before
  299. ASSERT_ENUMERATOR([map keyEnumeratorFrom:@100 to:@110], 2, 2, 2); // after to after
  300. ASSERT_ENUMERATOR([map keyEnumeratorFrom:@100 to:@6], 2, 2, 2); // after to key in map
  301. ASSERT_ENUMERATOR([map keyEnumeratorFrom:@100 to:@7], 2, 2, 2); // after to in between
  302. ASSERT_ENUMERATOR([map keyEnumeratorFrom:@6 to:@0], 6, 6, 2); // key in map to before
  303. ASSERT_ENUMERATOR([map keyEnumeratorFrom:@6 to:@100], 6, n * 2 + 2, 2); // key in map to after
  304. ASSERT_ENUMERATOR([map keyEnumeratorFrom:@6 to:@10], 6, 10, 2); // key in map to key in map
  305. ASSERT_ENUMERATOR([map keyEnumeratorFrom:@6 to:@11], 6, 12, 2); // key in map to in between
  306. ASSERT_ENUMERATOR([map keyEnumeratorFrom:@7 to:@0], 8, 8, 2); // in between to before
  307. ASSERT_ENUMERATOR([map keyEnumeratorFrom:@7 to:@100], 8, n * 2 + 2, 2); // in between to after
  308. ASSERT_ENUMERATOR([map keyEnumeratorFrom:@7 to:@10], 8, 10, 2); // in between to key in map
  309. ASSERT_ENUMERATOR([map keyEnumeratorFrom:@7 to:@13], 8, 14, 2); // in between to in between
  310. }
  311. - (void)testReverseEnumeratorFrom {
  312. NSUInteger n = 20;
  313. NSMutableArray *toInsert = [NSMutableArray arrayWithCapacity:n];
  314. for (int i = 0; i < n; i++) {
  315. [toInsert addObject:@(i * 2 + 2)];
  316. }
  317. [self shuffleArray:toInsert];
  318. FSTImmutableSortedDictionary *map =
  319. [[FSTArraySortedDictionary alloc] initWithComparator:[self defaultComparator]];
  320. // add them to the dictionary
  321. for (NSUInteger i = 0; i < n; i++) {
  322. map = [map dictionaryBySettingObject:toInsert[i] forKey:toInsert[i]];
  323. }
  324. XCTAssertTrue(map.count == n, @"Check if all N objects are in the map");
  325. XCTAssertTrue([map isKindOfClass:FSTArraySortedDictionary.class],
  326. @"Make sure we still have a array backed dictionary");
  327. // Test from before keys.
  328. ASSERT_ENUMERATOR([map reverseKeyEnumeratorFrom:@0], 0, 0, -2);
  329. // Test from after keys.
  330. ASSERT_ENUMERATOR([map reverseKeyEnumeratorFrom:@100], n * 2, 0, -2);
  331. // Test from key in map.
  332. ASSERT_ENUMERATOR([map reverseKeyEnumeratorFrom:@10], 10, 0, -2);
  333. // Test from in between keys.
  334. ASSERT_ENUMERATOR([map reverseKeyEnumeratorFrom:@11], 10, 0, -2);
  335. }
  336. #undef ASSERT_ENUMERATOR
  337. - (void)testIndexOf {
  338. FSTArraySortedDictionary *map =
  339. [[FSTArraySortedDictionary alloc] initWithComparator:[self defaultComparator]];
  340. map = [map dictionaryBySettingObject:@1 forKey:@1];
  341. map = [map dictionaryBySettingObject:@50 forKey:@50];
  342. map = [map dictionaryBySettingObject:@3 forKey:@3];
  343. map = [map dictionaryBySettingObject:@4 forKey:@4];
  344. map = [map dictionaryBySettingObject:@7 forKey:@7];
  345. map = [map dictionaryBySettingObject:@9 forKey:@9];
  346. XCTAssertEqual([map indexOfKey:@0], NSNotFound);
  347. XCTAssertEqual([map indexOfKey:@1], 0);
  348. XCTAssertEqual([map indexOfKey:@2], NSNotFound);
  349. XCTAssertEqual([map indexOfKey:@3], 1);
  350. XCTAssertEqual([map indexOfKey:@4], 2);
  351. XCTAssertEqual([map indexOfKey:@5], NSNotFound);
  352. XCTAssertEqual([map indexOfKey:@6], NSNotFound);
  353. XCTAssertEqual([map indexOfKey:@7], 3);
  354. XCTAssertEqual([map indexOfKey:@8], NSNotFound);
  355. XCTAssertEqual([map indexOfKey:@9], 4);
  356. XCTAssertEqual([map indexOfKey:@50], 5);
  357. }
  358. @end