aboutsummaryrefslogtreecommitdiffhomepage
path: root/Example/Database/Tests/Unit/FCompoundHashTest.m
diff options
context:
space:
mode:
Diffstat (limited to 'Example/Database/Tests/Unit/FCompoundHashTest.m')
-rw-r--r--Example/Database/Tests/Unit/FCompoundHashTest.m141
1 files changed, 141 insertions, 0 deletions
diff --git a/Example/Database/Tests/Unit/FCompoundHashTest.m b/Example/Database/Tests/Unit/FCompoundHashTest.m
new file mode 100644
index 0000000..15e6d10
--- /dev/null
+++ b/Example/Database/Tests/Unit/FCompoundHashTest.m
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2017 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import <Foundation/Foundation.h>
+
+#import "FCompoundHash.h"
+#import "FTestHelpers.h"
+#import "FEmptyNode.h"
+#import "FStringUtilities.h"
+#import "FEmptyNode.h"
+
+@interface FCompoundHashTest : XCTestCase
+
+@end
+
+@implementation FCompoundHashTest
+
+static FCompoundHashSplitStrategy NEVER_SPLIT_STRATEGY = ^BOOL(FCompoundHashBuilder *builder) {
+ return NO;
+};
+
+- (FCompoundHashSplitStrategy)splitAtPaths:(NSArray *)paths {
+ return ^BOOL(FCompoundHashBuilder *builder) {
+ return [paths containsObject:builder.currentPath];
+ };
+}
+
+- (void)testEmptyNodeYieldsEmptyHash {
+ FCompoundHash *hash = [FCompoundHash fromNode:[FEmptyNode emptyNode]];
+ XCTAssertEqualObjects(hash.posts, @[]);
+ XCTAssertEqualObjects(hash.hashes, @[@""]);
+}
+
+- (void)testCompoundHashIsAlwaysFollowedByEmptyHash {
+ id<FNode> node = NODE(@{@"foo": @"bar"});
+ FCompoundHash *hash = [FCompoundHash fromNode:node splitStrategy:NEVER_SPLIT_STRATEGY];
+ NSString *expectedHash = [FStringUtilities base64EncodedSha1:@"(\"foo\":(string:\"bar\"))"];
+
+ XCTAssertEqualObjects(hash.posts, @[PATH(@"foo")]);
+ XCTAssertEqualObjects(hash.hashes, (@[expectedHash, @""]));
+}
+
+- (void)testCompoundHashCanSplitAtPriority {
+ id<FNode> node = NODE((@{@"foo": @{@"!beforePriority": @"before", @".priority": @"prio", @"afterPriority": @"after"}, @"qux": @"qux"}));
+ FCompoundHash *hash = [FCompoundHash fromNode:node splitStrategy:[self splitAtPaths:@[PATH(@"foo/.priority")]]];
+ NSString *firstHash = [FStringUtilities base64EncodedSha1:@"(\"foo\":(\"!beforePriority\":(string:\"before\"),\".priority\":(string:\"prio\")))"];
+ NSString *secondHash = [FStringUtilities base64EncodedSha1:@"(\"foo\":(\"afterPriority\":(string:\"after\")),\"qux\":(string:\"qux\"))"];
+ XCTAssertEqualObjects(hash.posts, (@[PATH(@"foo/.priority"), PATH(@"qux")]));
+ XCTAssertEqualObjects(hash.hashes, (@[firstHash, secondHash, @""]));
+}
+
+- (void)testHashesPriorityLeafNodes {
+ id<FNode> node = NODE((@{@"foo": @{@".value": @"bar", @".priority": @"baz"}}));
+ FCompoundHash *hash = [FCompoundHash fromNode:node splitStrategy:NEVER_SPLIT_STRATEGY];
+ NSString *expectedHash = [FStringUtilities base64EncodedSha1:@"(\"foo\":(priority:string:\"baz\":string:\"bar\"))"];
+
+ XCTAssertEqualObjects(hash.posts, @[PATH(@"foo")]);
+ XCTAssertEqualObjects(hash.hashes, (@[expectedHash, @""]));
+}
+
+- (void)testHashingFollowsFirebaseKeySemantics {
+ id<FNode> node = NODE((@{@"1": @"one", @"2": @"two", @"10": @"ten"}));
+ // 10 is after 2 in Firebase key semantics, but would be before 2 in string semantics
+ FCompoundHash *hash = [FCompoundHash fromNode:node splitStrategy:[self splitAtPaths:@[PATH(@"2")]]];
+ NSString *firstHash = [FStringUtilities base64EncodedSha1:@"(\"1\":(string:\"one\"),\"2\":(string:\"two\"))"];
+ NSString *secondHash = [FStringUtilities base64EncodedSha1:@"(\"10\":(string:\"ten\"))"];
+ XCTAssertEqualObjects(hash.posts, (@[PATH(@"2"), PATH(@"10")]));
+ XCTAssertEqualObjects(hash.hashes, (@[firstHash, secondHash, @""]));
+}
+
+- (void)testHashingOnChildBoundariesWorks {
+ id<FNode> node = NODE((@{@"bar": @{@"deep": @"value"}, @"foo": @{@"other-deep": @"value"}}));
+ FCompoundHash *hash = [FCompoundHash fromNode:node splitStrategy:[self splitAtPaths:@[PATH(@"bar/deep")]]];
+ NSString *firstHash = [FStringUtilities base64EncodedSha1:@"(\"bar\":(\"deep\":(string:\"value\")))"];
+ NSString *secondHash = [FStringUtilities base64EncodedSha1:@"(\"foo\":(\"other-deep\":(string:\"value\")))"];
+ XCTAssertEqualObjects(hash.posts, (@[PATH(@"bar/deep"), PATH(@"foo/other-deep")]));
+ XCTAssertEqualObjects(hash.hashes, (@[firstHash, secondHash, @""]));
+}
+
+- (void)testCommasAreSetForNestedChildren {
+ id<FNode> node = NODE((@{@"bar": @{@"deep": @"value"}, @"foo": @{@"other-deep": @"value"}}));
+ FCompoundHash *hash = [FCompoundHash fromNode:node splitStrategy:NEVER_SPLIT_STRATEGY];
+ NSString *expectedHash = [FStringUtilities base64EncodedSha1:@"(\"bar\":(\"deep\":(string:\"value\")),\"foo\":(\"other-deep\":(string:\"value\")))"];
+
+ XCTAssertEqualObjects(hash.posts, @[PATH(@"foo/other-deep")]);
+ XCTAssertEqualObjects(hash.hashes, (@[expectedHash, @""]));
+}
+
+- (void)testQuotedStringsAndKeys {
+ id<FNode> node = NODE((@{@"\"": @"\\", @"\"\\\"\\": @"\"\\\"\\"}));
+ FCompoundHash *hash = [FCompoundHash fromNode:node splitStrategy:NEVER_SPLIT_STRATEGY];
+ NSString *expectedHash = [FStringUtilities base64EncodedSha1:@"(\"\\\"\":(string:\"\\\\\"),\"\\\"\\\\\\\"\\\\\":(string:\"\\\"\\\\\\\"\\\\\"))"];
+
+ XCTAssertEqualObjects(hash.posts, @[PATH(@"\"\\\"\\")]);
+ XCTAssertEqualObjects(hash.hashes, (@[expectedHash, @""]));
+}
+
+- (void)testDefaultSplitHasSensibleAmountOfHashes {
+ NSMutableDictionary *dict = [NSMutableDictionary dictionary];
+ for (int i = 0; i < 500; i++) {
+ // roughly 15-20 bytes serialized per node, 10k total
+ dict[[NSString stringWithFormat:@"%d", i]] = @"value";
+ }
+ id<FNode> node10k = NODE(dict);
+
+ dict = [NSMutableDictionary dictionary];
+ for (int i = 0; i < 5000; i++) {
+ // roughly 15-20 bytes serialized per node, 100k total
+ dict[[NSString stringWithFormat:@"%d", i]] = @"value";
+ }
+ id<FNode> node100k = NODE(dict);
+
+ dict = [NSMutableDictionary dictionary];
+ for (int i = 0; i < 50000; i++) {
+ // roughly 15-20 bytes serialized per node, 1M total
+ dict[[NSString stringWithFormat:@"%d", i]] = @"value";
+ }
+ id<FNode> node1M = NODE(dict);
+
+ FCompoundHash *hash10k = [FCompoundHash fromNode:node10k];
+ FCompoundHash *hash100k = [FCompoundHash fromNode:node100k];
+ FCompoundHash *hash1M = [FCompoundHash fromNode:node1M];
+ XCTAssertEqualWithAccuracy(hash10k.hashes.count, 15, 3);
+ XCTAssertEqualWithAccuracy(hash100k.hashes.count, 50, 5);
+ XCTAssertEqualWithAccuracy(hash1M.hashes.count, 150, 10);
+}
+
+@end