/* * 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 #import #import "FRangeMerge.h" #import "FNode.h" #import "FTestHelpers.h" #import "FEmptyNode.h" @interface FRangeMergeTest : XCTestCase @end @implementation FRangeMergeTest - (void)testSmokeTest { id node = NODE((@{@"bar": @"bar-value", @"foo": @{@"a": @{@"deep-a-1": @1, @"deep-a-2": @2}, @"b": @"b", @"c": @"c", @"d": @"d"}, @"quu": @"quu-value"})); id updates = NODE((@{@"foo": @{@"a": @{@"deep-a-2": @"new-a-2", @"deep-a-3": @3}, @"b-2": @"new-b", @"c": @"new-c" }})); FRangeMerge *merge = [[FRangeMerge alloc] initWithStart:PATH(@"foo/a/deep-a-1") end:PATH(@"foo/c") updates:updates]; id expected = NODE((@{@"bar": @"bar-value", @"foo": @{@"a": @{@"deep-a-1": @1, @"deep-a-2": @"new-a-2", @"deep-a-3": @3}, @"b-2": @"new-b", @"c": @"new-c", @"d": @"d"}, @"quu": @"quu-value"})); id actual = [merge applyToNode:node]; XCTAssertEqualObjects(actual, expected); } - (void)testStartIsExclusive { id node = NODE((@{@"bar": @"bar-value", @"foo": @"foo-value", @"quu": @"quu-value"})); id updates = NODE((@{@"foo": @"new-foo-value"})); FRangeMerge *merge = [[FRangeMerge alloc] initWithStart:PATH(@"bar") end:PATH(@"foo") updates:updates]; id expected = NODE((@{@"bar": @"bar-value", @"foo": @"new-foo-value", @"quu": @"quu-value"})); id actual = [merge applyToNode:node]; XCTAssertEqualObjects(actual, expected); } - (void)testStartIsExclusiveButIncludesChildren { id node = NODE((@{@"bar": @"bar-value", @"foo": @"foo-value", @"quu": @"quu-value"})); id updates = NODE((@{@"bar": @{@"bar-child": @"bar-child-value"}, @"foo": @"new-foo-value"})); FRangeMerge *merge = [[FRangeMerge alloc] initWithStart:PATH(@"bar") end:PATH(@"foo") updates:updates]; id expected = NODE((@{@"bar": @{@"bar-child": @"bar-child-value"}, @"foo": @"new-foo-value", @"quu": @"quu-value"})); id actual = [merge applyToNode:node]; XCTAssertEqualObjects(actual, expected); } - (void)testEndIsInclusive { id node = NODE((@{@"bar": @"bar-value", @"foo": @"foo-value", @"quu": @"quu-value"})); id updates = NODE((@{@"baz": @"baz-value"})); FRangeMerge *merge = [[FRangeMerge alloc] initWithStart:PATH(@"bar") end:PATH(@"foo") updates:updates]; // foo should be deleted id expected = NODE((@{@"bar": @"bar-value", @"baz": @"baz-value", @"quu": @"quu-value"})); id actual = [merge applyToNode:node]; XCTAssertEqualObjects(actual, expected); } - (void)testEndIsInclusiveButExcludesChildren { id node = NODE((@{@"bar": @"bar-value", @"foo": @{@"foo-child": @"foo-child-value"}, @"quu": @"quu-value"})); id updates = NODE((@{@"baz": @"baz-value"})); FRangeMerge *merge = [[FRangeMerge alloc] initWithStart:PATH(@"bar") end:PATH(@"foo") updates:updates]; // foo should be deleted id expected = NODE((@{@"bar": @"bar-value", @"baz": @"baz-value", @"foo": @{@"foo-child": @"foo-child-value"}, @"quu": @"quu-value"})); id actual = [merge applyToNode:node]; XCTAssertEqualObjects(actual, expected); } - (void)testCanUpdateLeafNode { id node = NODE(@"leaf-value"); id updates = NODE((@{@"bar": @"bar-value"})); FRangeMerge *merge = [[FRangeMerge alloc] initWithStart:nil end:PATH(@"foo") updates:updates]; id expected = NODE((@{@"bar": @"bar-value"})); id actual = [merge applyToNode:node]; XCTAssertEqualObjects(actual, expected); } - (void)testCanReplaceLeafNodeWithLeafNode{ id node = NODE(@"leaf-value"); id updates = NODE(@"new-leaf-value"); FRangeMerge *merge = [[FRangeMerge alloc] initWithStart:nil end:PATH(@"") updates:updates]; id expected = NODE(@"new-leaf-value"); id actual = [merge applyToNode:node]; XCTAssertEqualObjects(actual, expected); } - (void)testLeafsAreUpdatedWhenRangesIncludeDeeperPath { id node = NODE((@{@"foo": @{@"bar": @"bar-value"}})); id updates = NODE((@{@"foo": @{@"bar": @"new-bar-value"}})); FRangeMerge *merge = [[FRangeMerge alloc] initWithStart:PATH(@"foo") end:PATH(@"foo/bar/deep") updates:updates]; id expected = NODE((@{@"foo": @{@"bar": @"new-bar-value"}})); id actual = [merge applyToNode:node]; XCTAssertEqualObjects(actual, expected); } - (void)testLeafsAreNotUpdatedWhenRangesIncludeDeeperPaths { id node = NODE((@{@"foo": @{@"bar": @"bar-value"}})); id updates = NODE((@{@"foo": @{@"bar": @"new-bar-value"}})); FRangeMerge *merge = [[FRangeMerge alloc] initWithStart:PATH(@"foo/bar") end:PATH(@"foo/bar/deep") updates:updates]; id expected = NODE((@{@"foo": @{@"bar": @"bar-value"}})); id actual = [merge applyToNode:node]; XCTAssertEqualObjects(actual, expected); } - (void)testUpdatingEntireRangeUpdatesEverything { id node = [FEmptyNode emptyNode]; id updates = NODE((@{@"foo": @"foo-value", @"bar": @{@"child": @"bar-child-value"}})); FRangeMerge *merge = [[FRangeMerge alloc] initWithStart:nil end:nil updates:updates]; id expected = NODE((@{@"foo": @"foo-value", @"bar": @{@"child": @"bar-child-value"}})); id actual = [merge applyToNode:node]; XCTAssertEqualObjects(actual, expected); } - (void)testUpdatingRangeWithUnboundedLeftPostWorks { id node = NODE((@{@"bar": @"bar-value", @"foo": @"foo-value"})); id updates = NODE((@{@"bar": @"new-bar"})); FRangeMerge *merge = [[FRangeMerge alloc] initWithStart:nil end:PATH(@"bar") updates:updates]; id expected = NODE((@{@"bar": @"new-bar", @"foo": @"foo-value"})); id actual = [merge applyToNode:node]; XCTAssertEqualObjects(actual, expected); } - (void)testUpdatingRangeWithRightPostChildOfLeftPostWorks { id node = NODE((@{@"foo": @{@"a": @"a", @"b": @{@"1": @"1", @"2": @"2"}, @"c": @"c"}})); id updates = NODE((@{@"foo": @{@"a": @"new-a", @"b": @{@"1": @"new-1"}}})); FRangeMerge *merge = [[FRangeMerge alloc] initWithStart:PATH(@"foo") end:PATH(@"foo/b/1") updates:updates]; id expected = NODE((@{@"foo": @{@"a": @"new-a", @"b": @{@"1": @"new-1", @"2": @"2"}, @"c": @"c"}})); id actual = [merge applyToNode:node]; XCTAssertEqualObjects(actual, expected); } - (void)testUpdatingRangeWithRightPostChildOfLeftPostWorksWithIntegerKeys { id node = NODE((@{@"foo": @{@"a": @"a", @"b": @{@"1": @"1", @"2": @"2", @"10": @"10"}, @"c": @"c"}})); id updates = NODE((@{@"foo": @{@"a": @"new-a", @"b": @{@"1": @"new-1"}}})); FRangeMerge *merge = [[FRangeMerge alloc] initWithStart:PATH(@"foo") end:PATH(@"foo/b/2") updates:updates]; id expected = NODE((@{@"foo": @{@"a": @"new-a", @"b": @{@"1": @"new-1", @"10": @"10"}, @"c": @"c"}})); id actual = [merge applyToNode:node]; XCTAssertEqualObjects(actual, expected); } - (void)testUpdatingLeafIncludesPriority { id node = NODE((@{@"bar": @"bar-value", @"foo": @"foo-value", @"quu": @"quu-value"})); id updates = NODE((@{@"foo": @{@".value": @"new-foo", @".priority": @"prio"}})); FRangeMerge *merge = [[FRangeMerge alloc] initWithStart:PATH(@"bar") end:PATH(@"foo") updates:updates]; id expected = NODE((@{@"bar": @"bar-value", @"foo": @{@".value": @"new-foo", @".priority": @"prio" }, @"quu": @"quu-value"})); id actual = [merge applyToNode:node]; XCTAssertEqualObjects(actual, expected); } - (void)testUpdatingPriorityInChildrenNodeWorks { id node = NODE((@{@"bar": @"bar-value", @"foo": @"foo-value"})); id updates = NODE((@{@"bar": @"new-bar", @".priority": @"prio"})); FRangeMerge *merge = [[FRangeMerge alloc] initWithStart:nil end:PATH(@"bar") updates:updates]; id expected = NODE((@{@"bar": @"new-bar", @"foo": @"foo-value", @".priority": @"prio"})); id actual = [merge applyToNode:node]; XCTAssertEqualObjects(actual, expected); } // TODO: this test should actuall;y work, but priorities on empty nodes are ignored :( - (void)updatingPriorityInChildrenNodeWorksAlone { id node = NODE((@{@"bar": @"bar-value", @"foo": @"foo-value"})); id updates = NODE((@{@".priority": @"prio" })); FRangeMerge *merge = [[FRangeMerge alloc] initWithStart:nil end:PATH(@".priority") updates:updates]; id expected = NODE((@{@"bar": @"bar-value", @"foo": @"foo-value", @".priority": @"prio"})); id actual = [merge applyToNode:node]; XCTAssertEqualObjects(actual, expected); } - (void)testUpdatingPriorityOnInitiallyEmptyNodeDoesNotBreak { id node = NODE((@{})); id updates = NODE((@{@".priority": @"prio", @"foo": @"foo-value" })); FRangeMerge *merge = [[FRangeMerge alloc] initWithStart:nil end:PATH(@"foo") updates:updates]; id expected = NODE((@{@"foo": @"foo-value", @".priority": @"prio"})); id actual = [merge applyToNode:node]; XCTAssertEqualObjects(actual, expected); } - (void)testPriorityIsDeletedWhenIncludedInChildrenRange { id node = NODE((@{@"bar": @"bar-value", @"foo": @"foo-value", @".priority": @"prio"})); id updates = NODE((@{@"bar": @"new-bar"})); FRangeMerge *merge = [[FRangeMerge alloc] initWithStart:nil end:PATH(@"bar") updates:updates]; // deletes priority id expected = NODE((@{@"bar": @"new-bar", @"foo": @"foo-value"})); id actual = [merge applyToNode:node]; XCTAssertEqualObjects(actual, expected); } - (void)testPriorityIsIncludedInOpenStart { id node = NODE((@{@"foo": @{@"bar": @"bar-value"}})); id updates = NODE((@{@".priority": @"prio", @"baz": @"baz"})); FRangeMerge *merge = [[FRangeMerge alloc] initWithStart:nil end:PATH(@"foo/bar") updates:updates]; id expected = NODE((@{@"baz": @"baz", @".priority": @"prio"})); id actual = [merge applyToNode:node]; XCTAssertEqualObjects(actual, expected); } - (void)testPriorityIsIncludedInOpenEnd { id node = NODE(@"leaf-node"); id updates = NODE((@{@".priority": @"prio", @"foo": @"bar"})); FRangeMerge *merge = [[FRangeMerge alloc] initWithStart:PATH(@"/") end:nil updates:updates]; id expected = NODE((@{@"foo": @"bar", @".priority": @"prio"})); id actual = [merge applyToNode:node]; XCTAssertEqualObjects(actual, expected); } @end