FCompoundHashTest.m 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  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 <Foundation/Foundation.h>
  17. #import "FCompoundHash.h"
  18. #import "FEmptyNode.h"
  19. #import "FStringUtilities.h"
  20. #import "FTestHelpers.h"
  21. @interface FCompoundHashTest : XCTestCase
  22. @end
  23. @implementation FCompoundHashTest
  24. static FCompoundHashSplitStrategy NEVER_SPLIT_STRATEGY = ^BOOL(FCompoundHashBuilder *builder) {
  25. return NO;
  26. };
  27. - (FCompoundHashSplitStrategy)splitAtPaths:(NSArray *)paths {
  28. return ^BOOL(FCompoundHashBuilder *builder) {
  29. return [paths containsObject:builder.currentPath];
  30. };
  31. }
  32. - (void)testEmptyNodeYieldsEmptyHash {
  33. FCompoundHash *hash = [FCompoundHash fromNode:[FEmptyNode emptyNode]];
  34. XCTAssertEqualObjects(hash.posts, @[]);
  35. XCTAssertEqualObjects(hash.hashes, @[ @"" ]);
  36. }
  37. - (void)testCompoundHashIsAlwaysFollowedByEmptyHash {
  38. id<FNode> node = NODE(@{@"foo" : @"bar"});
  39. FCompoundHash *hash = [FCompoundHash fromNode:node splitStrategy:NEVER_SPLIT_STRATEGY];
  40. NSString *expectedHash = [FStringUtilities base64EncodedSha1:@"(\"foo\":(string:\"bar\"))"];
  41. XCTAssertEqualObjects(hash.posts, @[ PATH(@"foo") ]);
  42. XCTAssertEqualObjects(hash.hashes, (@[ expectedHash, @"" ]));
  43. }
  44. - (void)testCompoundHashCanSplitAtPriority {
  45. id<FNode> node = NODE((@{
  46. @"foo" : @{@"!beforePriority" : @"before", @".priority" : @"prio", @"afterPriority" : @"after"},
  47. @"qux" : @"qux"
  48. }));
  49. FCompoundHash *hash = [FCompoundHash fromNode:node
  50. splitStrategy:[self splitAtPaths:@[ PATH(@"foo/.priority") ]]];
  51. NSString *firstHash = [FStringUtilities
  52. base64EncodedSha1:
  53. @"(\"foo\":(\"!beforePriority\":(string:\"before\"),\".priority\":(string:\"prio\")))"];
  54. NSString *secondHash = [FStringUtilities
  55. base64EncodedSha1:
  56. @"(\"foo\":(\"afterPriority\":(string:\"after\")),\"qux\":(string:\"qux\"))"];
  57. XCTAssertEqualObjects(hash.posts, (@[ PATH(@"foo/.priority"), PATH(@"qux") ]));
  58. XCTAssertEqualObjects(hash.hashes, (@[ firstHash, secondHash, @"" ]));
  59. }
  60. - (void)testHashesPriorityLeafNodes {
  61. id<FNode> node = NODE((@{@"foo" : @{@".value" : @"bar", @".priority" : @"baz"}}));
  62. FCompoundHash *hash = [FCompoundHash fromNode:node splitStrategy:NEVER_SPLIT_STRATEGY];
  63. NSString *expectedHash =
  64. [FStringUtilities base64EncodedSha1:@"(\"foo\":(priority:string:\"baz\":string:\"bar\"))"];
  65. XCTAssertEqualObjects(hash.posts, @[ PATH(@"foo") ]);
  66. XCTAssertEqualObjects(hash.hashes, (@[ expectedHash, @"" ]));
  67. }
  68. - (void)testHashingFollowsFirebaseKeySemantics {
  69. id<FNode> node = NODE((@{@"1" : @"one", @"2" : @"two", @"10" : @"ten"}));
  70. // 10 is after 2 in Firebase key semantics, but would be before 2 in string semantics
  71. FCompoundHash *hash = [FCompoundHash fromNode:node
  72. splitStrategy:[self splitAtPaths:@[ PATH(@"2") ]]];
  73. NSString *firstHash =
  74. [FStringUtilities base64EncodedSha1:@"(\"1\":(string:\"one\"),\"2\":(string:\"two\"))"];
  75. NSString *secondHash = [FStringUtilities base64EncodedSha1:@"(\"10\":(string:\"ten\"))"];
  76. XCTAssertEqualObjects(hash.posts, (@[ PATH(@"2"), PATH(@"10") ]));
  77. XCTAssertEqualObjects(hash.hashes, (@[ firstHash, secondHash, @"" ]));
  78. }
  79. - (void)testHashingOnChildBoundariesWorks {
  80. id<FNode> node = NODE((@{@"bar" : @{@"deep" : @"value"}, @"foo" : @{@"other-deep" : @"value"}}));
  81. FCompoundHash *hash = [FCompoundHash fromNode:node
  82. splitStrategy:[self splitAtPaths:@[ PATH(@"bar/deep") ]]];
  83. NSString *firstHash =
  84. [FStringUtilities base64EncodedSha1:@"(\"bar\":(\"deep\":(string:\"value\")))"];
  85. NSString *secondHash =
  86. [FStringUtilities base64EncodedSha1:@"(\"foo\":(\"other-deep\":(string:\"value\")))"];
  87. XCTAssertEqualObjects(hash.posts, (@[ PATH(@"bar/deep"), PATH(@"foo/other-deep") ]));
  88. XCTAssertEqualObjects(hash.hashes, (@[ firstHash, secondHash, @"" ]));
  89. }
  90. - (void)testCommasAreSetForNestedChildren {
  91. id<FNode> node = NODE((@{@"bar" : @{@"deep" : @"value"}, @"foo" : @{@"other-deep" : @"value"}}));
  92. FCompoundHash *hash = [FCompoundHash fromNode:node splitStrategy:NEVER_SPLIT_STRATEGY];
  93. NSString *expectedHash = [FStringUtilities
  94. base64EncodedSha1:
  95. @"(\"bar\":(\"deep\":(string:\"value\")),\"foo\":(\"other-deep\":(string:\"value\")))"];
  96. XCTAssertEqualObjects(hash.posts, @[ PATH(@"foo/other-deep") ]);
  97. XCTAssertEqualObjects(hash.hashes, (@[ expectedHash, @"" ]));
  98. }
  99. - (void)testQuotedStringsAndKeys {
  100. id<FNode> node = NODE((@{@"\"" : @"\\", @"\"\\\"\\" : @"\"\\\"\\"}));
  101. FCompoundHash *hash = [FCompoundHash fromNode:node splitStrategy:NEVER_SPLIT_STRATEGY];
  102. NSString *expectedHash = [FStringUtilities
  103. base64EncodedSha1:
  104. @"(\"\\\"\":(string:\"\\\\\"),\"\\\"\\\\\\\"\\\\\":(string:\"\\\"\\\\\\\"\\\\\"))"];
  105. XCTAssertEqualObjects(hash.posts, @[ PATH(@"\"\\\"\\") ]);
  106. XCTAssertEqualObjects(hash.hashes, (@[ expectedHash, @"" ]));
  107. }
  108. - (void)testDefaultSplitHasSensibleAmountOfHashes {
  109. NSMutableDictionary *dict = [NSMutableDictionary dictionary];
  110. for (int i = 0; i < 500; i++) {
  111. // roughly 15-20 bytes serialized per node, 10k total
  112. dict[[NSString stringWithFormat:@"%d", i]] = @"value";
  113. }
  114. id<FNode> node10k = NODE(dict);
  115. dict = [NSMutableDictionary dictionary];
  116. for (int i = 0; i < 5000; i++) {
  117. // roughly 15-20 bytes serialized per node, 100k total
  118. dict[[NSString stringWithFormat:@"%d", i]] = @"value";
  119. }
  120. id<FNode> node100k = NODE(dict);
  121. dict = [NSMutableDictionary dictionary];
  122. for (int i = 0; i < 50000; i++) {
  123. // roughly 15-20 bytes serialized per node, 1M total
  124. dict[[NSString stringWithFormat:@"%d", i]] = @"value";
  125. }
  126. id<FNode> node1M = NODE(dict);
  127. FCompoundHash *hash10k = [FCompoundHash fromNode:node10k];
  128. FCompoundHash *hash100k = [FCompoundHash fromNode:node100k];
  129. FCompoundHash *hash1M = [FCompoundHash fromNode:node1M];
  130. XCTAssertEqualWithAccuracy(hash10k.hashes.count, 15, 3);
  131. XCTAssertEqualWithAccuracy(hash100k.hashes.count, 50, 5);
  132. XCTAssertEqualWithAccuracy(hash1M.hashes.count, 150, 10);
  133. }
  134. @end