FArraySortedDictionaryTest.m 19 KB

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