FEventTests.m 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  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 "FirebaseDatabase/Tests/Integration/FEventTests.h"
  17. #import "FirebaseDatabase/Tests/Helpers/FEventTester.h"
  18. #import "FirebaseDatabase/Tests/Helpers/FTestHelpers.h"
  19. #import "FirebaseDatabase/Tests/Helpers/FTupleEventTypeString.h"
  20. @implementation FEventTests
  21. - (void)testInvalidEventType {
  22. FIRDatabaseReference* f = [FTestHelpers getRandomNode];
  23. XCTAssertThrows([f observeEventType:-4
  24. withBlock:^(FIRDataSnapshot* s){
  25. }],
  26. @"Invalid event type properly throws an error");
  27. }
  28. - (void)testWriteLeafExpectValueChanged {
  29. FTupleFirebase* tuple = [FTestHelpers getRandomNodePair];
  30. FIRDatabaseReference* writeNode = tuple.one;
  31. FIRDatabaseReference* readNode = tuple.two;
  32. __block BOOL done = NO;
  33. [writeNode setValue:@1234
  34. withCompletionBlock:^(NSError* error, FIRDatabaseReference* ref) {
  35. done = YES;
  36. }];
  37. [self waitUntil:^BOOL {
  38. return done;
  39. }];
  40. [super snapWaiter:readNode
  41. withBlock:^(FIRDataSnapshot* s) {
  42. XCTAssertEqualObjects([s value], @1234, @"Proper value in snapshot");
  43. }];
  44. }
  45. - (void)testWRiteLeafNodeThenExpectValueEvent {
  46. FIRDatabaseReference* writeNode = [FTestHelpers getRandomNode];
  47. [writeNode setValue:@42];
  48. [super snapWaiter:writeNode
  49. withBlock:^(FIRDataSnapshot* s) {
  50. XCTAssertEqualObjects([s value], @42, @"Proper value in snapshot");
  51. }];
  52. }
  53. - (void)testWriteLeafNodeThenExpectChildAddedEventThenValueEvent {
  54. FIRDatabaseReference* writeNode = [FTestHelpers getRandomNode];
  55. [[writeNode child:@"foo"] setValue:@878787];
  56. NSArray* lookingFor = @[
  57. [[FTupleEventTypeString alloc] initWithFirebase:writeNode
  58. withEvent:FIRDataEventTypeChildAdded
  59. withString:@"foo"],
  60. [[FTupleEventTypeString alloc] initWithFirebase:writeNode
  61. withEvent:FIRDataEventTypeValue
  62. withString:nil],
  63. ];
  64. FEventTester* et = [[FEventTester alloc] initFrom:self];
  65. [et addLookingFor:lookingFor];
  66. [et wait];
  67. [super snapWaiter:writeNode
  68. withBlock:^(FIRDataSnapshot* s) {
  69. XCTAssertEqualObjects([[s childSnapshotForPath:@"foo"] value], @878787,
  70. @"Got proper value");
  71. }];
  72. }
  73. - (void)testSetMultipleEventListenersOnSameNode {
  74. FTupleFirebase* tuple = [FTestHelpers getRandomNodePair];
  75. FIRDatabaseReference* writeNode = tuple.one;
  76. FIRDatabaseReference* readNode = tuple.two;
  77. [writeNode setValue:@42];
  78. // two write nodes
  79. FEventTester* et = [[FEventTester alloc] initFrom:self];
  80. [et addLookingFor:@[ [[FTupleEventTypeString alloc] initWithFirebase:writeNode
  81. withEvent:FIRDataEventTypeValue
  82. withString:nil] ]];
  83. [et wait];
  84. et = [[FEventTester alloc] initFrom:self];
  85. [et addLookingFor:@[ [[FTupleEventTypeString alloc] initWithFirebase:writeNode
  86. withEvent:FIRDataEventTypeValue
  87. withString:nil] ]];
  88. [et wait];
  89. // two read nodes
  90. et = [[FEventTester alloc] initFrom:self];
  91. [et addLookingFor:@[ [[FTupleEventTypeString alloc] initWithFirebase:readNode
  92. withEvent:FIRDataEventTypeValue
  93. withString:nil] ]];
  94. [et wait];
  95. et = [[FEventTester alloc] initFrom:self];
  96. [et addLookingFor:@[ [[FTupleEventTypeString alloc] initWithFirebase:readNode
  97. withEvent:FIRDataEventTypeValue
  98. withString:nil] ]];
  99. [et wait];
  100. }
  101. - (void)testUnsubscribeEventsAndConfirmThatEventsNoLongerFire {
  102. FIRDatabaseReference* node = [FTestHelpers getRandomNode];
  103. __block int numValueCB = 0;
  104. FIRDatabaseHandle handle = [node observeEventType:FIRDataEventTypeValue
  105. withBlock:^(FIRDataSnapshot* s) {
  106. numValueCB = numValueCB + 1;
  107. }];
  108. // Set
  109. for (int i = 0; i < 3; i++) {
  110. [node setValue:[NSNumber numberWithInt:i]];
  111. }
  112. // bye
  113. [node removeObserverWithHandle:handle];
  114. // set again
  115. for (int i = 10; i < 15; i++) {
  116. [node setValue:[NSNumber numberWithInt:i]];
  117. }
  118. for (int i = 20; i < 25; i++) {
  119. [node setValue:[NSNumber numberWithInt:i]];
  120. }
  121. // Should just be 3
  122. [self waitUntil:^BOOL {
  123. return numValueCB == 3;
  124. }];
  125. }
  126. - (void)testCanWriteACompoundObjectAndGetMoreGranularEventsForIndividualChanges {
  127. FTupleFirebase* tuple = [FTestHelpers getRandomNodePair];
  128. FIRDatabaseReference* writeNode = tuple.one;
  129. FIRDatabaseReference* readNode = tuple.two;
  130. __block BOOL done = NO;
  131. [writeNode setValue:@{@"a" : @10, @"b" : @20}
  132. withCompletionBlock:^(NSError* error, FIRDatabaseReference* ref) {
  133. done = YES;
  134. }];
  135. [self waitUntil:^BOOL {
  136. return done;
  137. }];
  138. NSArray* lookingForW = @[
  139. [[FTupleEventTypeString alloc] initWithFirebase:[writeNode child:@"a"]
  140. withEvent:FIRDataEventTypeValue
  141. withString:nil],
  142. [[FTupleEventTypeString alloc] initWithFirebase:[writeNode child:@"b"]
  143. withEvent:FIRDataEventTypeValue
  144. withString:nil],
  145. ];
  146. NSArray* lookingForR = @[
  147. [[FTupleEventTypeString alloc] initWithFirebase:[readNode child:@"a"]
  148. withEvent:FIRDataEventTypeValue
  149. withString:nil],
  150. [[FTupleEventTypeString alloc] initWithFirebase:[readNode child:@"b"]
  151. withEvent:FIRDataEventTypeValue
  152. withString:nil],
  153. ];
  154. FEventTester* etW = [[FEventTester alloc] initFrom:self];
  155. [etW addLookingFor:lookingForW];
  156. [etW wait];
  157. FEventTester* etR = [[FEventTester alloc] initFrom:self];
  158. [etR addLookingFor:lookingForR];
  159. [etR wait];
  160. // Modify compound but just change one of them
  161. lookingForW = @[ [[FTupleEventTypeString alloc] initWithFirebase:[writeNode child:@"b"]
  162. withEvent:FIRDataEventTypeValue
  163. withString:nil] ];
  164. lookingForR = @[ [[FTupleEventTypeString alloc] initWithFirebase:[readNode child:@"b"]
  165. withEvent:FIRDataEventTypeValue
  166. withString:nil] ];
  167. [etW addLookingFor:lookingForW];
  168. [etR addLookingFor:lookingForR];
  169. [writeNode setValue:@{@"a" : @10, @"b" : @30}];
  170. [etW wait];
  171. [etR wait];
  172. }
  173. - (void)testValueEventIsFiredForEmptyNode {
  174. FIRDatabaseReference* node = [FTestHelpers getRandomNode];
  175. __block BOOL valueFired = NO;
  176. [node observeEventType:FIRDataEventTypeValue
  177. withBlock:^(FIRDataSnapshot* s) {
  178. XCTAssertTrue([[s value] isEqual:[NSNull null]], @"Value is properly nil");
  179. valueFired = YES;
  180. }];
  181. [self waitUntil:^BOOL {
  182. return valueFired;
  183. }];
  184. }
  185. - (void)testCorrectEventsRaisedWhenLeafTurnsIntoInternalNode {
  186. FIRDatabaseReference* node = [FTestHelpers getRandomNode];
  187. NSMutableString* eventString = [[NSMutableString alloc] init];
  188. [node observeEventType:FIRDataEventTypeValue
  189. withBlock:^(FIRDataSnapshot* s) {
  190. if ([s hasChildren]) {
  191. [eventString appendString:@", got children"];
  192. } else {
  193. [eventString appendFormat:@", value %@", [s value]];
  194. }
  195. }];
  196. [node observeEventType:FIRDataEventTypeChildAdded
  197. withBlock:^(FIRDataSnapshot* s) {
  198. [eventString appendFormat:@", child_added %@", [s key]];
  199. }];
  200. [node setValue:@42];
  201. [node setValue:@{@"a" : @2}];
  202. [node setValue:@84];
  203. __block BOOL done = NO;
  204. [node setValue:nil
  205. withCompletionBlock:^(NSError* error, FIRDatabaseReference* ref) {
  206. done = YES;
  207. }];
  208. [self waitUntil:^BOOL {
  209. return done;
  210. }];
  211. XCTAssertEqualObjects(@", value 42, child_added a, got children, value 84, value <null>",
  212. eventString, @"Proper order seen");
  213. }
  214. - (void)testRegisteringCallbackMultipleTimesAndUnregistering {
  215. FIRDatabaseReference* node = [FTestHelpers getRandomNode];
  216. __block int changes = 0;
  217. fbt_void_datasnapshot cb = ^(FIRDataSnapshot* snapshot) {
  218. changes = changes + 1;
  219. };
  220. FIRDatabaseHandle handle1 = [node observeEventType:FIRDataEventTypeValue withBlock:cb];
  221. FIRDatabaseHandle handle2 = [node observeEventType:FIRDataEventTypeValue withBlock:cb];
  222. FIRDatabaseHandle handle3 = [node observeEventType:FIRDataEventTypeValue withBlock:cb];
  223. __block BOOL done = NO;
  224. [node setValue:@42
  225. withCompletionBlock:^(NSError* error, FIRDatabaseReference* ref) {
  226. done = YES;
  227. }];
  228. [self waitUntil:^BOOL {
  229. return done;
  230. }];
  231. done = NO;
  232. XCTAssertTrue(changes == 3, @"Saw 3 callback events %d", changes);
  233. [node removeObserverWithHandle:handle1];
  234. [node setValue:@84
  235. withCompletionBlock:^(NSError* error, FIRDatabaseReference* ref) {
  236. done = YES;
  237. }];
  238. [self waitUntil:^BOOL {
  239. return done;
  240. }];
  241. done = NO;
  242. XCTAssertTrue(changes == 5, @"Saw 5 callback events %d", changes);
  243. [node removeObserverWithHandle:handle2];
  244. [node setValue:@168
  245. withCompletionBlock:^(NSError* error, FIRDatabaseReference* ref) {
  246. done = YES;
  247. }];
  248. [self waitUntil:^BOOL {
  249. return done;
  250. }];
  251. done = NO;
  252. XCTAssertTrue(changes == 6, @"Saw 6 callback events %d", changes);
  253. [node removeObserverWithHandle:handle3];
  254. [node setValue:@376
  255. withCompletionBlock:^(NSError* error, FIRDatabaseReference* ref) {
  256. done = YES;
  257. }];
  258. [self waitUntil:^BOOL {
  259. return done;
  260. }];
  261. done = NO;
  262. XCTAssertTrue(changes == 6, @"Saw 6 callback events %d", changes);
  263. NSLog(@"callbacks: %d", changes);
  264. }
  265. - (void)testUnregisteringTheSameCallbackTooManyTimesDoesNothing {
  266. FIRDatabaseReference* node = [FTestHelpers getRandomNode];
  267. fbt_void_datasnapshot cb = ^(FIRDataSnapshot* snapshot) {
  268. };
  269. FIRDatabaseHandle handle1 = [node observeEventType:FIRDataEventTypeValue withBlock:cb];
  270. [node removeObserverWithHandle:handle1];
  271. [node removeObserverWithHandle:handle1];
  272. XCTAssertTrue(YES, @"Properly reached end of test without throwing errors.");
  273. }
  274. - (void)testOnceValueFiresExactlyOnce {
  275. FIRDatabaseReference* path = [FTestHelpers getRandomNode];
  276. __block BOOL firstCall = YES;
  277. [path observeSingleEventOfType:FIRDataEventTypeValue
  278. withBlock:^(FIRDataSnapshot* snapshot) {
  279. XCTAssertTrue(firstCall, @"Properly saw first call");
  280. firstCall = NO;
  281. XCTAssertEqualObjects(@42, [snapshot value], @"Properly saw node value");
  282. }];
  283. [path setValue:@42];
  284. [path setValue:@84];
  285. __block BOOL done = NO;
  286. [path setValue:nil
  287. withCompletionBlock:^(NSError* error, FIRDatabaseReference* ref) {
  288. done = YES;
  289. }];
  290. [self waitUntil:^BOOL {
  291. return done;
  292. }];
  293. }
  294. - (void)testOnceChildAddedFiresExactlyOnce {
  295. for (int i = 0; i < 100; i++) {
  296. FIRDatabaseReference* path = [FTestHelpers getRandomNode];
  297. __block BOOL firstCall = YES;
  298. __block BOOL done = NO;
  299. [path observeSingleEventOfType:FIRDataEventTypeChildAdded
  300. withBlock:^(FIRDataSnapshot* snapshot) {
  301. XCTAssertTrue(firstCall, @"Properly saw first call");
  302. firstCall = NO;
  303. XCTAssertEqualObjects(@42, [snapshot value], @"Properly saw node value");
  304. XCTAssertEqualObjects(@"foo", [snapshot key],
  305. @"Properly saw the first node");
  306. if (![[snapshot value] isEqual:@42]) {
  307. exit(-1);
  308. }
  309. done = YES;
  310. }];
  311. [[path child:@"foo"] setValue:@42];
  312. [[path child:@"bar"] setValue:@84]; // XXX FIXME sometimes this event fires first
  313. [[path child:@"foo"] setValue:@168];
  314. // [path setValue:nil withCompletionBlock:^(BOOL status) { done = YES; }];
  315. [self waitUntil:^BOOL {
  316. return done;
  317. }];
  318. }
  319. }
  320. - (void)testOnceValueFiresExactlyOnceEvenIfThereIsASetInsideCallback {
  321. FIRDatabaseReference* path = [FTestHelpers getRandomNode];
  322. __block BOOL firstCall = YES;
  323. __block BOOL done = NO;
  324. [path observeSingleEventOfType:FIRDataEventTypeValue
  325. withBlock:^(FIRDataSnapshot* snapshot) {
  326. XCTAssertTrue(firstCall, @"Properly saw first call");
  327. if (firstCall) {
  328. firstCall = NO;
  329. XCTAssertEqualObjects(@42, [snapshot value], @"Properly saw node value");
  330. [path setValue:@43];
  331. done = YES;
  332. } else {
  333. XCTFail(@"Callback got called more than once.");
  334. }
  335. }];
  336. [path setValue:@42];
  337. [path setValue:@84];
  338. [self waitUntil:^BOOL {
  339. return done;
  340. }];
  341. }
  342. - (void)testOnceChildAddedFiresOnceEvenWithCompoundObject {
  343. FIRDatabaseReference* path = [FTestHelpers getRandomNode];
  344. __block BOOL firstCall = YES;
  345. [path observeSingleEventOfType:FIRDataEventTypeChildAdded
  346. withBlock:^(FIRDataSnapshot* snapshot) {
  347. XCTAssertTrue(firstCall, @"Properly saw first call");
  348. firstCall = NO;
  349. XCTAssertEqualObjects(@84, [snapshot value], @"Properly saw node value");
  350. XCTAssertEqualObjects(@"bar", [snapshot key],
  351. @"Properly saw the first node");
  352. }];
  353. [path setValue:@{@"foo" : @42, @"bar" : @84}];
  354. __block BOOL done = NO;
  355. [path setValue:nil
  356. withCompletionBlock:^(NSError* error, FIRDatabaseReference* ref) {
  357. done = YES;
  358. }];
  359. [self waitUntil:^BOOL {
  360. return done;
  361. }];
  362. }
  363. - (void)testOnEmptyChildFires {
  364. FIRDatabaseReference* node = [FTestHelpers getRandomNode];
  365. __block BOOL done = NO;
  366. [node observeEventType:FIRDataEventTypeValue
  367. withBlock:^(FIRDataSnapshot* snapshot){
  368. }];
  369. [[node child:@"test"] observeEventType:FIRDataEventTypeValue
  370. withBlock:^(FIRDataSnapshot* snapshot) {
  371. XCTAssertTrue([[snapshot value] isEqual:[NSNull null]],
  372. @"Properly saw nil child node");
  373. done = YES;
  374. }];
  375. [self waitUntil:^BOOL {
  376. return done;
  377. }];
  378. }
  379. - (void)testOnEmptyChildEvenAfterParentIsSynched {
  380. FIRDatabaseReference* node = [FTestHelpers getRandomNode];
  381. __block BOOL parentDone = NO;
  382. __block BOOL done = NO;
  383. [node observeEventType:FIRDataEventTypeValue
  384. withBlock:^(FIRDataSnapshot* snapshot) {
  385. parentDone = YES;
  386. }];
  387. [self waitUntil:^BOOL {
  388. return parentDone;
  389. }];
  390. [[node child:@"test"]
  391. observeEventType:FIRDataEventTypeValue
  392. withBlock:^(FIRDataSnapshot* snapshot) {
  393. XCTAssertTrue([[snapshot value] isEqual:[NSNull null]], @"Child is properly nil");
  394. done = YES;
  395. }];
  396. // This test really isn't in the same spirit as the JS test; we can't currently make sure that the
  397. // test fires right away since the ON and callback are async
  398. [self waitUntil:^BOOL {
  399. return done;
  400. }];
  401. XCTAssertTrue(done, @"Done fired.");
  402. }
  403. - (void)testEventsAreRaisedChildRemovedChildAddedChildMoved {
  404. FIRDatabaseReference* node = [FTestHelpers getRandomNode];
  405. NSMutableArray* events = [[NSMutableArray alloc] init];
  406. [node observeEventType:FIRDataEventTypeChildAdded
  407. withBlock:^(FIRDataSnapshot* snap) {
  408. [events addObject:[NSString stringWithFormat:@"added %@", [snap key]]];
  409. }];
  410. [node observeEventType:FIRDataEventTypeChildRemoved
  411. withBlock:^(FIRDataSnapshot* snap) {
  412. [events addObject:[NSString stringWithFormat:@"removed %@", [snap key]]];
  413. }];
  414. [node observeEventType:FIRDataEventTypeChildMoved
  415. withBlock:^(FIRDataSnapshot* snap) {
  416. [events addObject:[NSString stringWithFormat:@"moved %@", [snap key]]];
  417. }];
  418. __block BOOL done = NO;
  419. [node setValue:@{
  420. @"a" : @{@".value" : @1, @".priority" : @0},
  421. @"b" : @{@".value" : @1, @".priority" : @1},
  422. @"c" : @{@".value" : @1, @".priority" : @2},
  423. @"d" : @{@".value" : @1, @".priority" : @3},
  424. @"e" : @{@".value" : @1, @".priority" : @4},
  425. @"f" : @{@".value" : @1, @".priority" : @5},
  426. }
  427. withCompletionBlock:^(NSError* error, FIRDatabaseReference* ref) {
  428. done = YES;
  429. }];
  430. [self waitUntil:^BOOL {
  431. return done;
  432. }];
  433. [events removeAllObjects];
  434. done = NO;
  435. [node setValue:@{
  436. @"a" : @{@".value" : @1, @".priority" : @5},
  437. @"aa" : @{@".value" : @1, @".priority" : @0},
  438. @"b" : @{@".value" : @1, @".priority" : @1},
  439. @"bb" : @{@".value" : @1, @".priority" : @2},
  440. @"d" : @{@".value" : @1, @".priority" : @3},
  441. @"e" : @{@".value" : @1, @".priority" : @6},
  442. }
  443. withCompletionBlock:^(NSError* error, FIRDatabaseReference* ref) {
  444. done = YES;
  445. }];
  446. [self waitUntil:^BOOL {
  447. return done;
  448. }];
  449. XCTAssertEqualObjects(@"removed c, removed f, added aa, added bb, moved a, moved e",
  450. [events componentsJoinedByString:@", "], @"Got expected results");
  451. }
  452. - (void)testIntegerToDoubleConversions {
  453. FIRDatabaseReference* node = [FTestHelpers getRandomNode];
  454. NSMutableArray<NSString*>* events = [[NSMutableArray alloc] init];
  455. [node observeEventType:FIRDataEventTypeValue
  456. withBlock:^(FIRDataSnapshot* snap) {
  457. [events addObject:[NSString stringWithFormat:@"value %@", [snap value]]];
  458. }];
  459. for (NSNumber* number in @[ @1, @1.0, @1, @1.1 ]) {
  460. [self waitForCompletionOf:node setValue:number];
  461. }
  462. XCTAssertEqualObjects(@"value 1, value 1.1", [events componentsJoinedByString:@", "],
  463. @"Got expected results");
  464. }
  465. - (void)testEventsAreRaisedProperlyWithOnQueryLimits {
  466. // xxx impl query
  467. }
  468. @end