mirror of
https://github.com/firebase/firebase-ios-sdk
synced 2026-11-09 22:36:01 +05:45
590 lines
19 KiB
Objective-C
590 lines
19 KiB
Objective-C
/*
|
|
* 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 "FirebaseDatabase/Tests/Integration/FEventTests.h"
|
|
#import "FirebaseDatabase/Tests/Helpers/FEventTester.h"
|
|
#import "FirebaseDatabase/Tests/Helpers/FTestHelpers.h"
|
|
#import "FirebaseDatabase/Tests/Helpers/FTupleEventTypeString.h"
|
|
|
|
@implementation FEventTests
|
|
|
|
- (void)testInvalidEventType {
|
|
FIRDatabaseReference* f = [FTestHelpers getRandomNode];
|
|
XCTAssertThrows([f observeEventType:-4
|
|
withBlock:^(FIRDataSnapshot* s){
|
|
}],
|
|
@"Invalid event type properly throws an error");
|
|
}
|
|
|
|
- (void)testWriteLeafExpectValueChanged {
|
|
FTupleFirebase* tuple = [FTestHelpers getRandomNodePair];
|
|
FIRDatabaseReference* writeNode = tuple.one;
|
|
FIRDatabaseReference* readNode = tuple.two;
|
|
|
|
__block BOOL done = NO;
|
|
[writeNode setValue:@1234
|
|
withCompletionBlock:^(NSError* error, FIRDatabaseReference* ref) {
|
|
done = YES;
|
|
}];
|
|
[self waitUntil:^BOOL {
|
|
return done;
|
|
}];
|
|
|
|
[super snapWaiter:readNode
|
|
withBlock:^(FIRDataSnapshot* s) {
|
|
XCTAssertEqualObjects([s value], @1234, @"Proper value in snapshot");
|
|
}];
|
|
}
|
|
|
|
- (void)testWRiteLeafNodeThenExpectValueEvent {
|
|
FIRDatabaseReference* writeNode = [FTestHelpers getRandomNode];
|
|
[writeNode setValue:@42];
|
|
|
|
[super snapWaiter:writeNode
|
|
withBlock:^(FIRDataSnapshot* s) {
|
|
XCTAssertEqualObjects([s value], @42, @"Proper value in snapshot");
|
|
}];
|
|
}
|
|
|
|
- (void)testWriteLeafNodeThenExpectChildAddedEventThenValueEvent {
|
|
FIRDatabaseReference* writeNode = [FTestHelpers getRandomNode];
|
|
|
|
[[writeNode child:@"foo"] setValue:@878787];
|
|
|
|
NSArray* lookingFor = @[
|
|
[[FTupleEventTypeString alloc] initWithFirebase:writeNode
|
|
withEvent:FIRDataEventTypeChildAdded
|
|
withString:@"foo"],
|
|
[[FTupleEventTypeString alloc] initWithFirebase:writeNode
|
|
withEvent:FIRDataEventTypeValue
|
|
withString:nil],
|
|
];
|
|
|
|
FEventTester* et = [[FEventTester alloc] initFrom:self];
|
|
[et addLookingFor:lookingFor];
|
|
[et wait];
|
|
|
|
[super snapWaiter:writeNode
|
|
withBlock:^(FIRDataSnapshot* s) {
|
|
XCTAssertEqualObjects([[s childSnapshotForPath:@"foo"] value], @878787,
|
|
@"Got proper value");
|
|
}];
|
|
}
|
|
|
|
- (void)testSetMultipleEventListenersOnSameNode {
|
|
FTupleFirebase* tuple = [FTestHelpers getRandomNodePair];
|
|
FIRDatabaseReference* writeNode = tuple.one;
|
|
FIRDatabaseReference* readNode = tuple.two;
|
|
|
|
[writeNode setValue:@42];
|
|
|
|
// two write nodes
|
|
FEventTester* et = [[FEventTester alloc] initFrom:self];
|
|
[et addLookingFor:@[ [[FTupleEventTypeString alloc] initWithFirebase:writeNode
|
|
withEvent:FIRDataEventTypeValue
|
|
withString:nil] ]];
|
|
[et wait];
|
|
|
|
et = [[FEventTester alloc] initFrom:self];
|
|
[et addLookingFor:@[ [[FTupleEventTypeString alloc] initWithFirebase:writeNode
|
|
withEvent:FIRDataEventTypeValue
|
|
withString:nil] ]];
|
|
[et wait];
|
|
|
|
// two read nodes
|
|
et = [[FEventTester alloc] initFrom:self];
|
|
[et addLookingFor:@[ [[FTupleEventTypeString alloc] initWithFirebase:readNode
|
|
withEvent:FIRDataEventTypeValue
|
|
withString:nil] ]];
|
|
[et wait];
|
|
|
|
et = [[FEventTester alloc] initFrom:self];
|
|
[et addLookingFor:@[ [[FTupleEventTypeString alloc] initWithFirebase:readNode
|
|
withEvent:FIRDataEventTypeValue
|
|
withString:nil] ]];
|
|
[et wait];
|
|
}
|
|
|
|
- (void)testUnsubscribeEventsAndConfirmThatEventsNoLongerFire {
|
|
FIRDatabaseReference* node = [FTestHelpers getRandomNode];
|
|
__block int numValueCB = 0;
|
|
|
|
FIRDatabaseHandle handle = [node observeEventType:FIRDataEventTypeValue
|
|
withBlock:^(FIRDataSnapshot* s) {
|
|
numValueCB = numValueCB + 1;
|
|
}];
|
|
|
|
// Set
|
|
for (int i = 0; i < 3; i++) {
|
|
[node setValue:[NSNumber numberWithInt:i]];
|
|
}
|
|
|
|
// bye
|
|
[node removeObserverWithHandle:handle];
|
|
|
|
// set again
|
|
for (int i = 10; i < 15; i++) {
|
|
[node setValue:[NSNumber numberWithInt:i]];
|
|
}
|
|
|
|
for (int i = 20; i < 25; i++) {
|
|
[node setValue:[NSNumber numberWithInt:i]];
|
|
}
|
|
|
|
// Should just be 3
|
|
[self waitUntil:^BOOL {
|
|
return numValueCB == 3;
|
|
}];
|
|
}
|
|
|
|
- (void)testCanWriteACompoundObjectAndGetMoreGranularEventsForIndividualChanges {
|
|
FTupleFirebase* tuple = [FTestHelpers getRandomNodePair];
|
|
FIRDatabaseReference* writeNode = tuple.one;
|
|
FIRDatabaseReference* readNode = tuple.two;
|
|
|
|
__block BOOL done = NO;
|
|
[writeNode setValue:@{@"a" : @10, @"b" : @20}
|
|
withCompletionBlock:^(NSError* error, FIRDatabaseReference* ref) {
|
|
done = YES;
|
|
}];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return done;
|
|
}];
|
|
|
|
NSArray* lookingForW = @[
|
|
[[FTupleEventTypeString alloc] initWithFirebase:[writeNode child:@"a"]
|
|
withEvent:FIRDataEventTypeValue
|
|
withString:nil],
|
|
[[FTupleEventTypeString alloc] initWithFirebase:[writeNode child:@"b"]
|
|
withEvent:FIRDataEventTypeValue
|
|
withString:nil],
|
|
];
|
|
|
|
NSArray* lookingForR = @[
|
|
[[FTupleEventTypeString alloc] initWithFirebase:[readNode child:@"a"]
|
|
withEvent:FIRDataEventTypeValue
|
|
withString:nil],
|
|
[[FTupleEventTypeString alloc] initWithFirebase:[readNode child:@"b"]
|
|
withEvent:FIRDataEventTypeValue
|
|
withString:nil],
|
|
];
|
|
|
|
FEventTester* etW = [[FEventTester alloc] initFrom:self];
|
|
[etW addLookingFor:lookingForW];
|
|
[etW wait];
|
|
|
|
FEventTester* etR = [[FEventTester alloc] initFrom:self];
|
|
[etR addLookingFor:lookingForR];
|
|
[etR wait];
|
|
|
|
// Modify compound but just change one of them
|
|
|
|
lookingForW = @[ [[FTupleEventTypeString alloc] initWithFirebase:[writeNode child:@"b"]
|
|
withEvent:FIRDataEventTypeValue
|
|
withString:nil] ];
|
|
lookingForR = @[ [[FTupleEventTypeString alloc] initWithFirebase:[readNode child:@"b"]
|
|
withEvent:FIRDataEventTypeValue
|
|
withString:nil] ];
|
|
|
|
[etW addLookingFor:lookingForW];
|
|
[etR addLookingFor:lookingForR];
|
|
|
|
[writeNode setValue:@{@"a" : @10, @"b" : @30}];
|
|
|
|
[etW wait];
|
|
[etR wait];
|
|
}
|
|
|
|
- (void)testValueEventIsFiredForEmptyNode {
|
|
FIRDatabaseReference* node = [FTestHelpers getRandomNode];
|
|
|
|
__block BOOL valueFired = NO;
|
|
|
|
[node observeEventType:FIRDataEventTypeValue
|
|
withBlock:^(FIRDataSnapshot* s) {
|
|
XCTAssertTrue([[s value] isEqual:[NSNull null]], @"Value is properly nil");
|
|
valueFired = YES;
|
|
}];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return valueFired;
|
|
}];
|
|
}
|
|
|
|
- (void)testCorrectEventsRaisedWhenLeafTurnsIntoInternalNode {
|
|
FIRDatabaseReference* node = [FTestHelpers getRandomNode];
|
|
NSMutableString* eventString = [[NSMutableString alloc] init];
|
|
|
|
[node observeEventType:FIRDataEventTypeValue
|
|
withBlock:^(FIRDataSnapshot* s) {
|
|
if ([s hasChildren]) {
|
|
[eventString appendString:@", got children"];
|
|
} else {
|
|
[eventString appendFormat:@", value %@", [s value]];
|
|
}
|
|
}];
|
|
|
|
[node observeEventType:FIRDataEventTypeChildAdded
|
|
withBlock:^(FIRDataSnapshot* s) {
|
|
[eventString appendFormat:@", child_added %@", [s key]];
|
|
}];
|
|
|
|
[node setValue:@42];
|
|
[node setValue:@{@"a" : @2}];
|
|
[node setValue:@84];
|
|
__block BOOL done = NO;
|
|
[node setValue:nil
|
|
withCompletionBlock:^(NSError* error, FIRDatabaseReference* ref) {
|
|
done = YES;
|
|
}];
|
|
[self waitUntil:^BOOL {
|
|
return done;
|
|
}];
|
|
|
|
XCTAssertEqualObjects(@", value 42, child_added a, got children, value 84, value <null>",
|
|
eventString, @"Proper order seen");
|
|
}
|
|
|
|
- (void)testRegisteringCallbackMultipleTimesAndUnregistering {
|
|
FIRDatabaseReference* node = [FTestHelpers getRandomNode];
|
|
__block int changes = 0;
|
|
|
|
fbt_void_datasnapshot cb = ^(FIRDataSnapshot* snapshot) {
|
|
changes = changes + 1;
|
|
};
|
|
|
|
FIRDatabaseHandle handle1 = [node observeEventType:FIRDataEventTypeValue withBlock:cb];
|
|
FIRDatabaseHandle handle2 = [node observeEventType:FIRDataEventTypeValue withBlock:cb];
|
|
FIRDatabaseHandle handle3 = [node observeEventType:FIRDataEventTypeValue withBlock:cb];
|
|
|
|
__block BOOL done = NO;
|
|
|
|
[node setValue:@42
|
|
withCompletionBlock:^(NSError* error, FIRDatabaseReference* ref) {
|
|
done = YES;
|
|
}];
|
|
[self waitUntil:^BOOL {
|
|
return done;
|
|
}];
|
|
done = NO;
|
|
|
|
XCTAssertTrue(changes == 3, @"Saw 3 callback events %d", changes);
|
|
|
|
[node removeObserverWithHandle:handle1];
|
|
[node setValue:@84
|
|
withCompletionBlock:^(NSError* error, FIRDatabaseReference* ref) {
|
|
done = YES;
|
|
}];
|
|
[self waitUntil:^BOOL {
|
|
return done;
|
|
}];
|
|
done = NO;
|
|
|
|
XCTAssertTrue(changes == 5, @"Saw 5 callback events %d", changes);
|
|
|
|
[node removeObserverWithHandle:handle2];
|
|
[node setValue:@168
|
|
withCompletionBlock:^(NSError* error, FIRDatabaseReference* ref) {
|
|
done = YES;
|
|
}];
|
|
[self waitUntil:^BOOL {
|
|
return done;
|
|
}];
|
|
done = NO;
|
|
|
|
XCTAssertTrue(changes == 6, @"Saw 6 callback events %d", changes);
|
|
|
|
[node removeObserverWithHandle:handle3];
|
|
[node setValue:@376
|
|
withCompletionBlock:^(NSError* error, FIRDatabaseReference* ref) {
|
|
done = YES;
|
|
}];
|
|
[self waitUntil:^BOOL {
|
|
return done;
|
|
}];
|
|
done = NO;
|
|
|
|
XCTAssertTrue(changes == 6, @"Saw 6 callback events %d", changes);
|
|
|
|
NSLog(@"callbacks: %d", changes);
|
|
}
|
|
|
|
- (void)testUnregisteringTheSameCallbackTooManyTimesDoesNothing {
|
|
FIRDatabaseReference* node = [FTestHelpers getRandomNode];
|
|
|
|
fbt_void_datasnapshot cb = ^(FIRDataSnapshot* snapshot) {
|
|
};
|
|
|
|
FIRDatabaseHandle handle1 = [node observeEventType:FIRDataEventTypeValue withBlock:cb];
|
|
[node removeObserverWithHandle:handle1];
|
|
[node removeObserverWithHandle:handle1];
|
|
|
|
XCTAssertTrue(YES, @"Properly reached end of test without throwing errors.");
|
|
}
|
|
|
|
- (void)testOnceValueFiresExactlyOnce {
|
|
FIRDatabaseReference* path = [FTestHelpers getRandomNode];
|
|
__block BOOL firstCall = YES;
|
|
|
|
[path observeSingleEventOfType:FIRDataEventTypeValue
|
|
withBlock:^(FIRDataSnapshot* snapshot) {
|
|
XCTAssertTrue(firstCall, @"Properly saw first call");
|
|
firstCall = NO;
|
|
XCTAssertEqualObjects(@42, [snapshot value], @"Properly saw node value");
|
|
}];
|
|
|
|
[path setValue:@42];
|
|
[path setValue:@84];
|
|
|
|
__block BOOL done = NO;
|
|
|
|
[path setValue:nil
|
|
withCompletionBlock:^(NSError* error, FIRDatabaseReference* ref) {
|
|
done = YES;
|
|
}];
|
|
[self waitUntil:^BOOL {
|
|
return done;
|
|
}];
|
|
}
|
|
|
|
- (void)testOnceChildAddedFiresExaclyOnce {
|
|
__block int badCount = 0;
|
|
|
|
// for(int i = 0; i < 100; i++) {
|
|
|
|
FIRDatabaseReference* path = [FTestHelpers getRandomNode];
|
|
__block BOOL firstCall = YES;
|
|
|
|
__block BOOL done = NO;
|
|
|
|
[path observeSingleEventOfType:FIRDataEventTypeChildAdded
|
|
withBlock:^(FIRDataSnapshot* snapshot) {
|
|
XCTAssertTrue(firstCall, @"Properly saw first call");
|
|
firstCall = NO;
|
|
XCTAssertEqualObjects(@42, [snapshot value], @"Properly saw node value");
|
|
XCTAssertEqualObjects(@"foo", [snapshot key],
|
|
@"Properly saw the first node");
|
|
if (![[snapshot value] isEqual:@42]) {
|
|
exit(-1);
|
|
badCount = badCount + 1;
|
|
}
|
|
|
|
done = YES;
|
|
}];
|
|
|
|
[[path child:@"foo"] setValue:@42];
|
|
[[path child:@"bar"] setValue:@84]; // XXX FIXME sometimes this event fires first
|
|
[[path child:@"foo"] setValue:@168];
|
|
|
|
// [path setValue:nil withCompletionBlock:^(BOOL status) { done = YES; }];
|
|
[self waitUntil:^BOOL {
|
|
return done;
|
|
}];
|
|
|
|
// }
|
|
|
|
NSLog(@"BADCOUNT: %d", badCount);
|
|
}
|
|
|
|
- (void)testOnceValueFiresExacltyOnceEvenIfThereIsASetInsideCallback {
|
|
FIRDatabaseReference* path = [FTestHelpers getRandomNode];
|
|
__block BOOL firstCall = YES;
|
|
__block BOOL done = NO;
|
|
|
|
[path observeSingleEventOfType:FIRDataEventTypeValue
|
|
withBlock:^(FIRDataSnapshot* snapshot) {
|
|
XCTAssertTrue(firstCall, @"Properly saw first call");
|
|
if (firstCall) {
|
|
firstCall = NO;
|
|
XCTAssertEqualObjects(@42, [snapshot value], @"Properly saw node value");
|
|
[path setValue:@43];
|
|
done = YES;
|
|
} else {
|
|
XCTFail(@"Callback got called more than once.");
|
|
}
|
|
}];
|
|
|
|
[path setValue:@42];
|
|
[path setValue:@84];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return done;
|
|
}];
|
|
}
|
|
|
|
- (void)testOnceChildAddedFiresOnceEvenWithCompoundObject {
|
|
FIRDatabaseReference* path = [FTestHelpers getRandomNode];
|
|
__block BOOL firstCall = YES;
|
|
|
|
[path observeSingleEventOfType:FIRDataEventTypeChildAdded
|
|
withBlock:^(FIRDataSnapshot* snapshot) {
|
|
XCTAssertTrue(firstCall, @"Properly saw first call");
|
|
firstCall = NO;
|
|
XCTAssertEqualObjects(@84, [snapshot value], @"Properly saw node value");
|
|
XCTAssertEqualObjects(@"bar", [snapshot key],
|
|
@"Properly saw the first node");
|
|
}];
|
|
|
|
[path setValue:@{@"foo" : @42, @"bar" : @84}];
|
|
|
|
__block BOOL done = NO;
|
|
|
|
[path setValue:nil
|
|
withCompletionBlock:^(NSError* error, FIRDatabaseReference* ref) {
|
|
done = YES;
|
|
}];
|
|
[self waitUntil:^BOOL {
|
|
return done;
|
|
}];
|
|
}
|
|
|
|
- (void)testOnEmptyChildFires {
|
|
FIRDatabaseReference* node = [FTestHelpers getRandomNode];
|
|
__block BOOL done = NO;
|
|
|
|
[node observeEventType:FIRDataEventTypeValue
|
|
withBlock:^(FIRDataSnapshot* snapshot){
|
|
}];
|
|
[[node child:@"test"] observeEventType:FIRDataEventTypeValue
|
|
withBlock:^(FIRDataSnapshot* snapshot) {
|
|
XCTAssertTrue([[snapshot value] isEqual:[NSNull null]],
|
|
@"Properly saw nil child node");
|
|
done = YES;
|
|
}];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return done;
|
|
}];
|
|
}
|
|
|
|
- (void)testOnEmptyChildEvenAfterParentIsSynched {
|
|
FIRDatabaseReference* node = [FTestHelpers getRandomNode];
|
|
__block BOOL parentDone = NO;
|
|
__block BOOL done = NO;
|
|
|
|
[node observeEventType:FIRDataEventTypeValue
|
|
withBlock:^(FIRDataSnapshot* snapshot) {
|
|
parentDone = YES;
|
|
}];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return parentDone;
|
|
}];
|
|
|
|
[[node child:@"test"]
|
|
observeEventType:FIRDataEventTypeValue
|
|
withBlock:^(FIRDataSnapshot* snapshot) {
|
|
XCTAssertTrue([[snapshot value] isEqual:[NSNull null]], @"Child is properly nil");
|
|
done = YES;
|
|
}];
|
|
|
|
// This test really isn't in the same spirit as the JS test; we can't currently make sure that the
|
|
// test fires right away since the ON and callback are async
|
|
|
|
[self waitUntil:^BOOL {
|
|
return done;
|
|
}];
|
|
|
|
XCTAssertTrue(done, @"Done fired.");
|
|
}
|
|
|
|
- (void)testEventsAreRaisedChildRemovedChildAddedChildMoved {
|
|
FIRDatabaseReference* node = [FTestHelpers getRandomNode];
|
|
|
|
NSMutableArray* events = [[NSMutableArray alloc] init];
|
|
|
|
[node observeEventType:FIRDataEventTypeChildAdded
|
|
withBlock:^(FIRDataSnapshot* snap) {
|
|
[events addObject:[NSString stringWithFormat:@"added %@", [snap key]]];
|
|
}];
|
|
|
|
[node observeEventType:FIRDataEventTypeChildRemoved
|
|
withBlock:^(FIRDataSnapshot* snap) {
|
|
[events addObject:[NSString stringWithFormat:@"removed %@", [snap key]]];
|
|
}];
|
|
|
|
[node observeEventType:FIRDataEventTypeChildMoved
|
|
withBlock:^(FIRDataSnapshot* snap) {
|
|
[events addObject:[NSString stringWithFormat:@"moved %@", [snap key]]];
|
|
}];
|
|
|
|
__block BOOL done = NO;
|
|
|
|
[node setValue:@{
|
|
@"a" : @{@".value" : @1, @".priority" : @0},
|
|
@"b" : @{@".value" : @1, @".priority" : @1},
|
|
@"c" : @{@".value" : @1, @".priority" : @2},
|
|
@"d" : @{@".value" : @1, @".priority" : @3},
|
|
@"e" : @{@".value" : @1, @".priority" : @4},
|
|
@"f" : @{@".value" : @1, @".priority" : @5},
|
|
}
|
|
withCompletionBlock:^(NSError* error, FIRDatabaseReference* ref) {
|
|
done = YES;
|
|
}];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return done;
|
|
}];
|
|
|
|
[events removeAllObjects];
|
|
|
|
done = NO;
|
|
|
|
[node setValue:@{
|
|
@"a" : @{@".value" : @1, @".priority" : @5},
|
|
@"aa" : @{@".value" : @1, @".priority" : @0},
|
|
@"b" : @{@".value" : @1, @".priority" : @1},
|
|
@"bb" : @{@".value" : @1, @".priority" : @2},
|
|
@"d" : @{@".value" : @1, @".priority" : @3},
|
|
@"e" : @{@".value" : @1, @".priority" : @6},
|
|
}
|
|
withCompletionBlock:^(NSError* error, FIRDatabaseReference* ref) {
|
|
done = YES;
|
|
}];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return done;
|
|
}];
|
|
|
|
XCTAssertEqualObjects(@"removed c, removed f, added aa, added bb, moved a, moved e",
|
|
[events componentsJoinedByString:@", "], @"Got expected results");
|
|
}
|
|
|
|
- (void)testIntegerToDoubleConversions {
|
|
FIRDatabaseReference* node = [FTestHelpers getRandomNode];
|
|
|
|
NSMutableArray<NSString*>* events = [[NSMutableArray alloc] init];
|
|
|
|
[node observeEventType:FIRDataEventTypeValue
|
|
withBlock:^(FIRDataSnapshot* snap) {
|
|
[events addObject:[NSString stringWithFormat:@"value %@", [snap value]]];
|
|
}];
|
|
|
|
for (NSNumber* number in @[ @1, @1.0, @1, @1.1 ]) {
|
|
[self waitForCompletionOf:node setValue:number];
|
|
}
|
|
|
|
XCTAssertEqualObjects(@"value 1, value 1.1", [events componentsJoinedByString:@", "],
|
|
@"Got expected results");
|
|
}
|
|
|
|
- (void)testEventsAreRaisedProperlyWithOnQueryLimits {
|
|
// xxx impl query
|
|
}
|
|
|
|
@end
|