FCompoundHashTest.m 6.3 KB

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