/* * 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 "FOrder.h" #import "FIRDatabaseReference.h" #import "FTypedefs_Private.h" #import "FTupleFirebase.h" #import "FTestHelpers.h" #import "FEventTester.h" #import "FTupleEventTypeString.h" @implementation FOrder - (void) testPushEnumerateAndCheckCorrectOrder { FIRDatabaseReference * node = [FTestHelpers getRandomNode]; for(int i = 0; i < 10; i++) { [[node childByAutoId] setValue:[NSNumber numberWithInt:i]]; } [super snapWaiter:node withBlock:^(FIRDataSnapshot * snapshot) { int expected = 0; for (FIRDataSnapshot * child in snapshot.children) { XCTAssertEqualObjects([NSNumber numberWithInt:expected], [child value], @"Expects values match."); expected = expected + 1; } XCTAssertTrue(expected == 10, @"Should get all of the children"); XCTAssertTrue(expected == snapshot.childrenCount, @"Snapshot should report correct count"); }]; } - (void) testPushEnumerateManyPathsWriteAndCheckOrder { FIRDatabaseReference * node = [FTestHelpers getRandomNode]; NSMutableArray* paths = [[NSMutableArray alloc] init]; for(int i = 0; i < 20; i++) { [paths addObject:[node childByAutoId]]; } for(int i = 0; i < 20; i++) { [(FIRDatabaseReference *)[paths objectAtIndex:i] setValue:[NSNumber numberWithInt:i]]; } [super snapWaiter:node withBlock:^(FIRDataSnapshot *snap) { int expected = 0; for (FIRDataSnapshot * child in snap.children) { XCTAssertEqualObjects([NSNumber numberWithInt:expected], [child value], @"Expects values match."); expected = expected + 1; } XCTAssertTrue(expected == 20, @"Should get all of the children"); XCTAssertTrue(expected == snap.childrenCount, @"Snapshot should report correct count"); }]; } - (void) testPushDataReconnectReadBackAndVerifyOrder { FTupleFirebase* tuple = [FTestHelpers getRandomNodePair]; __block int expected = 0; __block int nodesSet = 0; FIRDatabaseReference * node = tuple.one; for(int i = 0; i < 10; i++) { [[node childByAutoId] setValue:[NSNumber numberWithInt:i] withCompletionBlock:^(NSError* err, FIRDatabaseReference * ref) { nodesSet++; }]; } [self waitUntil:^BOOL{ return nodesSet == 10; }]; __block BOOL done = NO; [super snapWaiter:node withBlock:^(FIRDataSnapshot *snap) { expected = 0; //[snap forEach:^BOOL(FIRDataSnapshot *child) { for (FIRDataSnapshot * child in snap.children) { XCTAssertEqualObjects([NSNumber numberWithInt:expected], [child value], @"Expected child value"); expected = expected + 1; //return NO; } done = YES; }]; [self waitUntil:^BOOL{ return done; }]; done = NO; XCTAssertTrue(nodesSet == 10, @"All of the nodes have been set"); [super snapWaiter:tuple.two withBlock:^(FIRDataSnapshot *snap) { expected = 0; for (FIRDataSnapshot * child in snap.children) { XCTAssertEqualObjects([NSNumber numberWithInt:expected], [child value], @"Expected child value"); expected = expected + 1; } done = YES; XCTAssertTrue(expected == 10, @"Saw the expected number of children %d == 10", expected); }]; } - (void) testPushDataWithPrioritiesReconnectReadBackAndVerifyOrder { FTupleFirebase* tuple = [FTestHelpers getRandomNodePair]; __block int expected = 0; __block int nodesSet = 0; FIRDatabaseReference * node = tuple.one; for(int i = 0; i < 10; i++) { [[node childByAutoId] setValue:[NSNumber numberWithInt:i] andPriority:[NSNumber numberWithInt:(10 - i)] withCompletionBlock:^(NSError* error, FIRDatabaseReference * ref) { nodesSet = nodesSet + 1; }]; } [super snapWaiter:node withBlock:^(FIRDataSnapshot *snap) { expected = 9; for (FIRDataSnapshot * child in snap.children) { XCTAssertEqualObjects([child value], [NSNumber numberWithInt:expected], @"Expected child value as per priority"); expected = expected - 1; } XCTAssertTrue(expected == -1, @"Saw the expected number of children"); }]; [self waitUntil:^BOOL{ return nodesSet == 10; }]; XCTAssertTrue(nodesSet == 10, @"All of the nodes have been set"); [super snapWaiter:tuple.two withBlock:^(FIRDataSnapshot *snap) { expected = 9; for (FIRDataSnapshot * child in snap.children) { XCTAssertEqualObjects([child value], [NSNumber numberWithInt:expected], @"Expected child value as per priority"); expected = expected - 1; } XCTAssertTrue(expected == -1, @"Saw the expected number of children"); }]; } - (void) testPushDataWithExponentialPrioritiesReconnectReadBackAndVerifyOrder { FTupleFirebase* tuple = [FTestHelpers getRandomNodePair]; __block int expected = 0; __block int nodesSet = 0; FIRDatabaseReference * node = tuple.one; for(int i = 0; i < 10; i++) { [[node childByAutoId] setValue:[NSNumber numberWithInt:i] andPriority:[NSNumber numberWithDouble:(111111111111111111111111111111.0 / pow(10, i))] withCompletionBlock:^(NSError* error, FIRDatabaseReference * ref) { nodesSet = nodesSet + 1; }]; } [super snapWaiter:node withBlock:^(FIRDataSnapshot *snap) { expected = 9; for (FIRDataSnapshot * child in snap.children) { XCTAssertEqualObjects([child value], [NSNumber numberWithInt:expected], @"Expected child value as per priority"); expected = expected - 1; } XCTAssertTrue(expected == -1, @"Saw the expected number of children"); }]; WAIT_FOR(nodesSet == 10); [super snapWaiter:tuple.two withBlock:^(FIRDataSnapshot *snap) { expected = 9; for (FIRDataSnapshot * child in snap.children) { XCTAssertEqualObjects([child value], [NSNumber numberWithInt:expected], @"Expected child value as per priority"); expected = expected - 1; } XCTAssertTrue(expected == -1, @"Saw the expected number of children"); }]; } - (void) testThatNodesWithoutValuesAreNotEnumerated { FIRDatabaseReference * node = [FTestHelpers getRandomNode]; [node child:@"foo"]; [[node child:@"bar"] setValue:@"test"]; __block int items = 0; [super snapWaiter:node withBlock:^(FIRDataSnapshot *snap) { for (FIRDataSnapshot * child in snap.children) { items = items + 1; XCTAssertEqualObjects([child key], @"bar", @"Saw the child which had a value set and not the empty one"); } XCTAssertTrue(items == 1, @"Saw only the one that was actually set."); }]; } - (void) testChildMovedEventWhenPriorityChanges { FIRDatabaseReference * node = [FTestHelpers getRandomNode]; FEventTester* et = [[FEventTester alloc] initFrom:self]; NSArray* expect = @[ [[FTupleEventTypeString alloc] initWithFirebase:node withEvent:FIRDataEventTypeChildAdded withString:@"a"], [[FTupleEventTypeString alloc] initWithFirebase:node withEvent:FIRDataEventTypeValue withString:nil], [[FTupleEventTypeString alloc] initWithFirebase:node withEvent:FIRDataEventTypeChildAdded withString:@"b"], [[FTupleEventTypeString alloc] initWithFirebase:node withEvent:FIRDataEventTypeValue withString:nil], [[FTupleEventTypeString alloc] initWithFirebase:node withEvent:FIRDataEventTypeChildAdded withString:@"c"], [[FTupleEventTypeString alloc] initWithFirebase:node withEvent:FIRDataEventTypeValue withString:nil], [[FTupleEventTypeString alloc] initWithFirebase:node withEvent:FIRDataEventTypeChildMoved withString:@"a"], [[FTupleEventTypeString alloc] initWithFirebase:node withEvent:FIRDataEventTypeChildChanged withString:@"a"], [[FTupleEventTypeString alloc] initWithFirebase:node withEvent:FIRDataEventTypeValue withString:nil] ]; [et addLookingFor:expect]; [et waitForInitialization]; [[node child:@"a"] setValue:@"first" andPriority:@1]; [[node child:@"b"] setValue:@"second" andPriority:@2]; [[node child:@"c"] setValue:@"third" andPriority:@3]; [[node child:@"a"] setPriority:@15]; [et wait]; } - (void) testCanResetPriorityToNull { FIRDatabaseReference * node = [FTestHelpers getRandomNode]; [[node child:@"a"] setValue:@"a" andPriority:@1]; [[node child:@"b"] setValue:@"b" andPriority:@2]; FEventTester* et = [[FEventTester alloc] initFrom:self]; NSArray* expect = @[ [[FTupleEventTypeString alloc] initWithFirebase:node withEvent:FIRDataEventTypeChildAdded withString:@"a"], [[FTupleEventTypeString alloc] initWithFirebase:node withEvent:FIRDataEventTypeChildAdded withString:@"b"], [[FTupleEventTypeString alloc] initWithFirebase:node withEvent:FIRDataEventTypeValue withString:nil] ]; [et addLookingFor:expect]; [et wait]; expect = @[ [[FTupleEventTypeString alloc] initWithFirebase:node withEvent:FIRDataEventTypeChildMoved withString:@"b"], [[FTupleEventTypeString alloc] initWithFirebase:node withEvent:FIRDataEventTypeChildChanged withString:@"b"], [[FTupleEventTypeString alloc] initWithFirebase:node withEvent:FIRDataEventTypeValue withString:nil] ]; [et addLookingFor:expect]; [[node child:@"b"] setPriority:nil]; [et wait]; __block BOOL ready = NO; [[node child:@"b"] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) { XCTAssertTrue([snapshot priority] == [NSNull null], @"Should be null"); ready = YES; }]; [self waitUntil:^BOOL{ return ready; }]; } - (void) testInsertingANodeUnderALeafPreservesItsPriority { FIRDatabaseReference * node = [FTestHelpers getRandomNode]; __block FIRDataSnapshot * snap; [node observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot *s) { snap = s; }]; [node setValue:@"a" andPriority:@10]; [[node child:@"deeper"] setValue:@"deeper"]; [self waitUntil:^BOOL{ id result = [snap value]; NSDictionary* expected = @{@"deeper": @"deeper"}; return snap != nil && [result isKindOfClass:[NSDictionary class]] && [result isEqualToDictionary:expected]; }]; XCTAssertEqualObjects([snap priority], @10, @"Proper value"); } - (void) testVerifyOrderOfMixedNumbersStringNoPriorities { FTupleFirebase* tuple = [FTestHelpers getRandomNodePair]; NSArray* nodeAndPriorities = @[ @"alpha42", @"zed", @"noPriorityC", [NSNull null], @"num41", @500, @"noPriorityB", [NSNull null], @"num80", @4000.1, @"num50", @4000, @"num10", @24, @"alpha41", @"zed", @"alpha20", @"horse", @"num20", @123, @"num70", @4000.01, @"noPriorityA", [NSNull null], @"alpha30", @"tree", @"num30", @300, @"num60", @4000.001, @"alpha10", @"0horse", @"num42", @500, @"alpha40", @"zed", @"num40", @500 ]; __block int setsCompleted = 0; for (int i = 0; i < [nodeAndPriorities count]; i++) { FIRDatabaseReference * n = [tuple.one child:[nodeAndPriorities objectAtIndex:i++]]; [n setValue:@1 andPriority:[nodeAndPriorities objectAtIndex:i] withCompletionBlock:^(NSError* error, FIRDatabaseReference * ref) { setsCompleted = setsCompleted + 1; }]; } NSString* expected = @"noPriorityA, noPriorityB, noPriorityC, num10, num20, num30, num40, num41, num42, num50, num60, num70, num80, alpha10, alpha20, alpha30, alpha40, alpha41, alpha42, "; [super snapWaiter:tuple.one withBlock:^(FIRDataSnapshot *snap) { NSMutableString* output = [[NSMutableString alloc] init]; for (FIRDataSnapshot * n in snap.children) { [output appendFormat:@"%@, ", [n key]]; } XCTAssertTrue([expected isEqualToString:output], @"Proper order"); }]; WAIT_FOR(setsCompleted == [nodeAndPriorities count] / 2); [super snapWaiter:tuple.two withBlock:^(FIRDataSnapshot *snap) { NSMutableString* output = [[NSMutableString alloc] init]; for (FIRDataSnapshot * n in snap.children) { [output appendFormat:@"%@, ", [n key]]; } XCTAssertTrue([expected isEqualToString:output], @"Proper order"); }]; } - (void) testVerifyOrderOfIntegerNames { FIRDatabaseReference * ref = [FTestHelpers getRandomNode]; NSArray* keys = @[ @"foo", @"bar", @"03", @"0", @"100", @"20", @"5", @"3", @"003", @"9" ]; __block int setsCompleted = 0; for (int i = 0; i < [keys count]; i++) { FIRDatabaseReference * n = [ref child:[keys objectAtIndex:i]]; [n setValue:@1 withCompletionBlock:^(NSError* error, FIRDatabaseReference * ref) { setsCompleted = setsCompleted + 1; }]; } NSString* expected = @"0, 3, 03, 003, 5, 9, 20, 100, bar, foo, "; [super snapWaiter:ref withBlock:^(FIRDataSnapshot *snap) { NSMutableString* output = [[NSMutableString alloc] init]; for (FIRDataSnapshot * n in snap.children) { [output appendFormat:@"%@, ", [n key]]; } XCTAssertTrue([expected isEqualToString:output], @"Proper order"); }]; } - (void) testPrevNameIsCorrectOnChildAddedEvent { FIRDatabaseReference * node = [FTestHelpers getRandomNode]; [node setValue:@{@"a": @1, @"b": @2, @"c": @3}]; NSMutableString* added = [[NSMutableString alloc] init]; __block int count = 0; [node observeEventType:FIRDataEventTypeChildAdded andPreviousSiblingKeyWithBlock:^(FIRDataSnapshot *snap, NSString *prevName) { [added appendFormat:@"%@ %@, ", [snap key], prevName]; count++; }]; [self waitUntil:^BOOL{ return count == 3; }]; XCTAssertTrue([added isEqualToString:@"a (null), b a, c b, "], @"Proper order and prevname"); } - (void) testPrevNameIsCorrectWhenAddingNewNodes { FIRDatabaseReference * node = [FTestHelpers getRandomNode]; [node setValue:@{@"b": @2, @"c": @3, @"d": @4}]; NSMutableString* added = [[NSMutableString alloc] init]; __block int count = 0; [node observeEventType:FIRDataEventTypeChildAdded andPreviousSiblingKeyWithBlock:^(FIRDataSnapshot *snap, NSString *prevName) { [added appendFormat:@"%@ %@, ", [snap key], prevName]; count++; }]; [self waitUntil:^BOOL{ return count == 3; }]; XCTAssertTrue([added isEqualToString:@"b (null), c b, d c, "], @"Proper order and prevname"); [added setString:@""]; [[node child:@"a"] setValue:@1]; [self waitUntil:^BOOL{ return count == 4; }]; XCTAssertTrue([added isEqualToString:@"a (null), "], @"Proper insertion of new node"); [added setString:@""]; [[node child:@"e"] setValue:@5]; [self waitUntil:^BOOL{ return count == 5; }]; XCTAssertTrue([added isEqualToString:@"e d, "], @"Proper insertion of new node"); } - (void) testPrevNameIsCorrectWhenAddingNewNodesWithJSON { FIRDatabaseReference * node = [FTestHelpers getRandomNode]; [node setValue:@{@"b": @2, @"c": @3, @"d": @4}]; NSMutableString* added = [[NSMutableString alloc] init]; __block int count = 0; [node observeEventType:FIRDataEventTypeChildAdded andPreviousSiblingKeyWithBlock:^(FIRDataSnapshot *snap, NSString *prevName) { [added appendFormat:@"%@ %@, ", [snap key], prevName]; count++; }]; [self waitUntil:^BOOL{ return count == 3; }]; XCTAssertTrue([added isEqualToString:@"b (null), c b, d c, "], @"Proper order and prevname"); [added setString:@""]; [node setValue:@{@"a": @1, @"b": @2, @"c": @3, @"d": @4}]; [self waitUntil:^BOOL{ return count == 4; }]; XCTAssertTrue([added isEqualToString:@"a (null), "], @"Proper insertion of new node"); [added setString:@""]; [node setValue:@{@"a": @1, @"b": @2, @"c": @3, @"d": @4, @"e": @5}]; [self waitUntil:^BOOL{ return count == 5; }]; XCTAssertTrue([added isEqualToString:@"e d, "], @"Proper insertion of new node"); } - (void) testPrevNameIsCorrectWhenMovingNodes { FIRDatabaseReference * node = [FTestHelpers getRandomNode]; NSMutableString* moved = [[NSMutableString alloc] init]; __block int count = 0; [node observeEventType:FIRDataEventTypeChildMoved andPreviousSiblingKeyWithBlock:^(FIRDataSnapshot *snapshot, NSString *prevName) { [moved appendFormat:@"%@ %@, ", snapshot.key, prevName]; count++; }]; [[node child:@"a"] setValue:@"a" andPriority:@1]; [[node child:@"b"] setValue:@"a" andPriority:@2]; [[node child:@"c"] setValue:@"a" andPriority:@3]; [[node child:@"d"] setValue:@"a" andPriority:@4]; [[node child:@"d"] setPriority:@0]; [self waitUntil:^BOOL{ return count == 1; }]; XCTAssertTrue([moved isEqualToString:@"d (null), "], @"Got first move"); [moved setString:@""]; [[node child:@"a"] setPriority:@4]; [self waitUntil:^BOOL{ return count == 2; }]; XCTAssertTrue([moved isEqualToString:@"a c, "], @"Got second move"); [moved setString:@""]; [[node child:@"c"] setPriority:@0.5]; [self waitUntil:^BOOL{ return count == 3; }]; XCTAssertTrue([moved isEqualToString:@"c d, "], @"Got third move"); } - (void) testPrevNameIsCorrectWhenSettingWholeJsonDict { FIRDatabaseReference * node = [FTestHelpers getRandomNode]; NSMutableString* moved = [[NSMutableString alloc] init]; __block int count = 0; [node observeEventType:FIRDataEventTypeChildMoved andPreviousSiblingKeyWithBlock:^(FIRDataSnapshot *snapshot, NSString *prevName) { [moved appendFormat:@"%@ %@, ", snapshot.key, prevName]; count++; }]; [node setValue:@{ @"a": @{@".value": @"a", @".priority": @1}, @"b": @{@".value": @"b", @".priority": @2}, @"c": @{@".value": @"c", @".priority": @3}, @"d": @{@".value": @"d", @".priority": @4} }]; [node setValue:@{ @"d": @{@".value": @"d", @".priority": @0}, @"a": @{@".value": @"a", @".priority": @1}, @"b": @{@".value": @"b", @".priority": @2}, @"c": @{@".value": @"c", @".priority": @3} }]; [self waitUntil:^BOOL{ return count == 1; }]; XCTAssertTrue([moved isEqualToString:@"d (null), "], @"Got move"); [moved setString:@""]; [node setValue:@{ @"d": @{@".value": @"d", @".priority": @0}, @"b": @{@".value": @"b", @".priority": @2}, @"c": @{@".value": @"c", @".priority": @3}, @"a": @{@".value": @"a", @".priority": @4} }]; [self waitUntil:^BOOL{ return count == 2; }]; XCTAssertTrue([moved isEqualToString:@"a c, "], @"Got move"); [moved setString:@""]; [node setValue:@{ @"d": @{@".value": @"d", @".priority": @0}, @"c": @{@".value": @"c", @".priority": @0.5}, @"b": @{@".value": @"b", @".priority": @2}, @"a": @{@".value": @"a", @".priority": @4} }]; [self waitUntil:^BOOL{ return count == 3; }]; XCTAssertTrue([moved isEqualToString:@"c d, "], @"Got move"); } - (void) testCase595NoChildMovedEventWhenDeletingPrioritizedGrandchild { FIRDatabaseReference * node = [FTestHelpers getRandomNode]; __block int moves = 0; [node observeEventType:FIRDataEventTypeChildMoved withBlock:^(FIRDataSnapshot *snapshot) { moves++; }]; __block BOOL ready = NO; [[node child:@"test/foo"] setValue:@42 andPriority:@"5"]; [[node child:@"test/foo2"] setValue:@42 andPriority:@"10"]; [[node child:@"test/foo"] removeValue]; [[node child:@"test/foo"] removeValueWithCompletionBlock:^(NSError *error, FIRDatabaseReference * ref) { ready = YES; }]; [self waitUntil:^BOOL{ return ready; }]; XCTAssertTrue(moves == 0, @"Nothing should have moved"); } - (void) testCanSetAValueWithPriZero { FIRDatabaseReference * node = [FTestHelpers getRandomNode]; __block FIRDataSnapshot * snap = nil; [node observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot *s) { snap = s; }]; [node setValue:@"test" andPriority:@0]; [self waitUntil:^BOOL{ return snap != nil; }]; XCTAssertEqualObjects([snap value], @"test", @"Proper value"); XCTAssertEqualObjects([snap priority], @0, @"Proper value"); } - (void) testCanSetObjectWithPriZero { FIRDatabaseReference * node = [FTestHelpers getRandomNode]; __block FIRDataSnapshot * snap = nil; [node observeEventType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot *s) { snap = s; }]; [node setValue:@{@"x": @"test", @"y": @7} andPriority:@0]; [self waitUntil:^BOOL{ return snap != nil; }]; XCTAssertEqualObjects([[snap value] objectForKey:@"x"], @"test", @"Proper value"); XCTAssertEqualObjects([[snap value] objectForKey:@"y"], @7, @"Proper value"); XCTAssertEqualObjects([snap priority], @0, @"Proper value"); } @end