FCompoundWriteTest.m 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  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 <XCTest/XCTest.h>
  17. #import "FirebaseDatabase/Sources/FNamedNode.h"
  18. #import "FirebaseDatabase/Sources/Snapshot/FCompoundWrite.h"
  19. #import "FirebaseDatabase/Sources/Snapshot/FEmptyNode.h"
  20. #import "FirebaseDatabase/Sources/Snapshot/FLeafNode.h"
  21. #import "FirebaseDatabase/Sources/Snapshot/FNode.h"
  22. #import "FirebaseDatabase/Sources/Snapshot/FSnapshotUtilities.h"
  23. @interface FCompoundWriteTest : XCTestCase
  24. @end
  25. @implementation FCompoundWriteTest
  26. - (id<FNode>)leafNode {
  27. static id<FNode> node = nil;
  28. if (!node) {
  29. node = [FSnapshotUtilities nodeFrom:@"leaf-node"];
  30. }
  31. return node;
  32. }
  33. - (id<FNode>)priorityNode {
  34. static id<FNode> node = nil;
  35. if (!node) {
  36. node = [FSnapshotUtilities nodeFrom:@"prio"];
  37. }
  38. return node;
  39. }
  40. - (id<FNode>)baseNode {
  41. static id<FNode> node = nil;
  42. if (!node) {
  43. NSDictionary *base = @{@"child-1" : @"value-1", @"child-2" : @"value-2"};
  44. node = [FSnapshotUtilities nodeFrom:base];
  45. }
  46. return node;
  47. }
  48. - (void)assertAppliedCompoundWrite:(FCompoundWrite *)compoundWrite
  49. equalsNode:(id<FNode>)node
  50. withPriority:(id<FNode>)priority {
  51. id<FNode> updatedNode = [compoundWrite applyToNode:node];
  52. if (node.isEmpty) {
  53. XCTAssertEqualObjects([FEmptyNode emptyNode], updatedNode,
  54. @"Applied compound write should be empty. %@", updatedNode);
  55. } else {
  56. XCTAssertEqualObjects([node updatePriority:priority], updatedNode,
  57. @"Applied compound write should equal node with priority. %@",
  58. updatedNode);
  59. }
  60. }
  61. - (void)testEmptyMergeIsEmpty {
  62. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  63. XCTAssertTrue(compoundWrite.isEmpty, @"Empty write should be empty %@", compoundWrite);
  64. }
  65. - (void)testCompoundWriteWithPriorityUpdateIsNotEmpty {
  66. FCompoundWrite *compoundWrite = [[FCompoundWrite emptyWrite] addWrite:self.priorityNode
  67. atKey:@".priority"];
  68. XCTAssertFalse(compoundWrite.isEmpty, @"Priority update should not be empty %@", compoundWrite);
  69. }
  70. - (void)testCompoundWriteWithUpdateIsNotEmpty {
  71. FCompoundWrite *compoundWrite =
  72. [[FCompoundWrite emptyWrite] addWrite:self.leafNode
  73. atPath:[[FPath alloc] initWith:@"foo/bar"]];
  74. XCTAssertFalse(compoundWrite.isEmpty, @"Update should not be empty %@", compoundWrite);
  75. }
  76. - (void)testCompoundWriteWithRootUpdateIsNotEmpty {
  77. FCompoundWrite *compoundWrite = [[FCompoundWrite emptyWrite] addWrite:self.leafNode
  78. atPath:[FPath empty]];
  79. XCTAssertFalse(compoundWrite.isEmpty, @"Update at root should not be empty %@", compoundWrite);
  80. }
  81. - (void)testCompoundWriteWithEmptyRootUpdateIsNotEmpty {
  82. FCompoundWrite *compoundWrite = [[FCompoundWrite emptyWrite] addWrite:[FEmptyNode emptyNode]
  83. atPath:[FPath empty]];
  84. XCTAssertFalse(compoundWrite.isEmpty, @"Empty root update should not be empty %@", compoundWrite);
  85. }
  86. - (void)testCompoundWriteWithRootPriorityUpdateAndChildMergeIsNotEmpty {
  87. FCompoundWrite *compoundWrite = [[FCompoundWrite emptyWrite] addWrite:self.priorityNode
  88. atKey:@".priority"];
  89. compoundWrite = [compoundWrite childCompoundWriteAtPath:[[FPath alloc] initWith:@".priority"]];
  90. XCTAssertFalse(compoundWrite.isEmpty,
  91. @"Compound write with root priority update and child merge should not be empty.");
  92. }
  93. - (void)testAppliesLeafOverwrite {
  94. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  95. compoundWrite = [compoundWrite addWrite:self.leafNode atPath:[FPath empty]];
  96. id<FNode> updatedNode = [compoundWrite applyToNode:[FEmptyNode emptyNode]];
  97. XCTAssertEqualObjects(updatedNode, self.leafNode, @"Should get leaf node once applied %@",
  98. updatedNode);
  99. }
  100. - (void)testAppliesChildrenOverwrite {
  101. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  102. id<FNode> childNode = [[FEmptyNode emptyNode] updateImmediateChild:@"child"
  103. withNewChild:self.leafNode];
  104. compoundWrite = [compoundWrite addWrite:childNode atPath:[FPath empty]];
  105. id<FNode> updatedNode = [compoundWrite applyToNode:[FEmptyNode emptyNode]];
  106. XCTAssertEqualObjects(updatedNode, childNode, @"Child overwrite should work");
  107. }
  108. - (void)testAddsChildNode {
  109. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  110. id<FNode> expectedNode = [[FEmptyNode emptyNode] updateImmediateChild:@"child"
  111. withNewChild:self.leafNode];
  112. compoundWrite = [compoundWrite addWrite:self.leafNode atKey:@"child"];
  113. id<FNode> updatedNode = [compoundWrite applyToNode:[FEmptyNode emptyNode]];
  114. XCTAssertEqualObjects(updatedNode, expectedNode, @"Adding child node should work %@",
  115. updatedNode);
  116. }
  117. - (void)testAddsDeepChildNode {
  118. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  119. FPath *path = [[FPath alloc] initWith:@"deep/deep/node"];
  120. id<FNode> expectedNode = [[FEmptyNode emptyNode] updateChild:path withNewChild:self.leafNode];
  121. compoundWrite = [compoundWrite addWrite:self.leafNode atPath:path];
  122. id<FNode> updatedNode = [compoundWrite applyToNode:[FEmptyNode emptyNode]];
  123. XCTAssertEqualObjects(updatedNode, expectedNode, @"Should add deep child node correctly");
  124. }
  125. - (void)testOverwritesExistingChild {
  126. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  127. FPath *path = [[FPath alloc] initWith:@"child-1"];
  128. compoundWrite = [compoundWrite addWrite:self.leafNode atPath:path];
  129. id<FNode> updatedNode = [compoundWrite applyToNode:self.baseNode];
  130. id<FNode> expectedNode = [self.baseNode updateImmediateChild:[path getFront]
  131. withNewChild:self.leafNode];
  132. XCTAssertEqualObjects(updatedNode, expectedNode, @"Overwriting existing child should work.");
  133. }
  134. - (void)testUpdatesExistingChild {
  135. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  136. FPath *path = [[FPath alloc] initWith:@"child-1/foo"];
  137. compoundWrite = [compoundWrite addWrite:self.leafNode atPath:path];
  138. id<FNode> updatedNode = [compoundWrite applyToNode:self.baseNode];
  139. id<FNode> expectedNode = [self.baseNode updateChild:path withNewChild:self.leafNode];
  140. XCTAssertEqualObjects(updatedNode, expectedNode, @"Updating existing child should work");
  141. }
  142. - (void)testDoesntUpdatePriorityOnEmptyNode {
  143. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  144. compoundWrite = [compoundWrite addWrite:self.priorityNode atKey:@".priority"];
  145. [self assertAppliedCompoundWrite:compoundWrite
  146. equalsNode:[FEmptyNode emptyNode]
  147. withPriority:[FEmptyNode emptyNode]];
  148. }
  149. - (void)testUpdatesPriorityOnNode {
  150. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  151. compoundWrite = [compoundWrite addWrite:self.priorityNode atKey:@".priority"];
  152. id<FNode> node = [FSnapshotUtilities nodeFrom:@"value"];
  153. [self assertAppliedCompoundWrite:compoundWrite equalsNode:node withPriority:self.priorityNode];
  154. }
  155. - (void)testUpdatesPriorityOfChild {
  156. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  157. FPath *path = [[FPath alloc] initWith:@"child-1/.priority"];
  158. compoundWrite = [compoundWrite addWrite:self.priorityNode atPath:path];
  159. id<FNode> updatedNode = [compoundWrite applyToNode:self.baseNode];
  160. id<FNode> expectedNode = [self.baseNode updateChild:path withNewChild:self.priorityNode];
  161. XCTAssertEqualObjects(updatedNode, expectedNode, @"Updating priority of child should work.");
  162. }
  163. - (void)testDoesntUpdatePriorityOfNonExistentChild {
  164. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  165. FPath *path = [[FPath alloc] initWith:@"child-3/.priority"];
  166. compoundWrite = [compoundWrite addWrite:self.priorityNode atPath:path];
  167. id<FNode> updatedNode = [compoundWrite applyToNode:self.baseNode];
  168. XCTAssertEqualObjects(updatedNode, self.baseNode,
  169. @"Should not update priority of nonexistent child");
  170. }
  171. - (void)testDeepUpdateExistingUpdates {
  172. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  173. id<FNode> update1 = [FSnapshotUtilities nodeFrom:@{@"foo" : @"foo-value", @"bar" : @"bar-value"}];
  174. id<FNode> update2 = [FSnapshotUtilities nodeFrom:@"baz-value"];
  175. id<FNode> update3 = [FSnapshotUtilities nodeFrom:@"new-foo-value"];
  176. compoundWrite = [compoundWrite addWrite:update1 atPath:[[FPath alloc] initWith:@"child-1"]];
  177. compoundWrite = [compoundWrite addWrite:update2 atPath:[[FPath alloc] initWith:@"child-1/baz"]];
  178. compoundWrite = [compoundWrite addWrite:update3 atPath:[[FPath alloc] initWith:@"child-1/foo"]];
  179. NSDictionary *expectedChild1 =
  180. @{@"foo" : @"new-foo-value", @"bar" : @"bar-value", @"baz" : @"baz-value"};
  181. id<FNode> expectedNode =
  182. [self.baseNode updateImmediateChild:@"child-1"
  183. withNewChild:[FSnapshotUtilities nodeFrom:expectedChild1]];
  184. id<FNode> updatedNode = [compoundWrite applyToNode:self.baseNode];
  185. XCTAssertEqualObjects(updatedNode, expectedNode,
  186. @"Deep update with existing updates should work.");
  187. }
  188. - (void)testShallowUpdateRemovesDeepUpdate {
  189. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  190. id<FNode> update1 = [FSnapshotUtilities nodeFrom:@"new-foo-value"];
  191. id<FNode> update2 = [FSnapshotUtilities nodeFrom:@"baz-value"];
  192. id<FNode> update3 = [FSnapshotUtilities nodeFrom:@{@"foo" : @"foo-value", @"bar" : @"bar-value"}];
  193. compoundWrite = [compoundWrite addWrite:update1 atPath:[[FPath alloc] initWith:@"child-1/foo"]];
  194. compoundWrite = [compoundWrite addWrite:update2 atPath:[[FPath alloc] initWith:@"child-1/baz"]];
  195. compoundWrite = [compoundWrite addWrite:update3 atPath:[[FPath alloc] initWith:@"child-1"]];
  196. NSDictionary *expectedChild1 = @{@"foo" : @"foo-value", @"bar" : @"bar-value"};
  197. id<FNode> expectedNode =
  198. [self.baseNode updateImmediateChild:@"child-1"
  199. withNewChild:[FSnapshotUtilities nodeFrom:expectedChild1]];
  200. id<FNode> updatedNode = [compoundWrite applyToNode:self.baseNode];
  201. XCTAssertEqualObjects(updatedNode, expectedNode, @"Shallow update should remove deep udpates.");
  202. }
  203. - (void)testChildPriorityDoesntUpdateEmptyNodePriorityOnChildMerge {
  204. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  205. compoundWrite = [compoundWrite addWrite:self.priorityNode
  206. atPath:[[FPath alloc] initWith:@"child-1/.priority"]];
  207. compoundWrite = [compoundWrite childCompoundWriteAtPath:[[FPath alloc] initWith:@"child-1"]];
  208. [self assertAppliedCompoundWrite:compoundWrite
  209. equalsNode:[FEmptyNode emptyNode]
  210. withPriority:[FEmptyNode emptyNode]];
  211. }
  212. - (void)testChildPriorityUpdatesPriorityOnChildMerge {
  213. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  214. compoundWrite = [compoundWrite addWrite:self.priorityNode
  215. atPath:[[FPath alloc] initWith:@"child-1/.priority"]];
  216. id<FNode> node = [FSnapshotUtilities nodeFrom:@"value"];
  217. compoundWrite = [compoundWrite childCompoundWriteAtPath:[[FPath alloc] initWith:@"child-1"]];
  218. [self assertAppliedCompoundWrite:compoundWrite equalsNode:node withPriority:self.priorityNode];
  219. }
  220. - (void)testChildPriorityUpdatesEmptyPriorityOnChildMerge {
  221. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  222. compoundWrite = [compoundWrite addWrite:[FEmptyNode emptyNode]
  223. atPath:[[FPath alloc] initWith:@"child-1/.priority"]];
  224. id<FNode> node = [[FLeafNode alloc] initWithValue:@"foo" withPriority:self.priorityNode];
  225. compoundWrite = [compoundWrite childCompoundWriteAtPath:[[FPath alloc] initWith:@"child-1"]];
  226. [self assertAppliedCompoundWrite:compoundWrite
  227. equalsNode:node
  228. withPriority:[FEmptyNode emptyNode]];
  229. }
  230. - (void)testDeepPrioritySetWorksOnEmptyNodeWhenOtherSetIsAvailable {
  231. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  232. compoundWrite = [compoundWrite addWrite:self.priorityNode
  233. atPath:[[FPath alloc] initWith:@"foo/.priority"]];
  234. compoundWrite = [compoundWrite addWrite:self.leafNode
  235. atPath:[[FPath alloc] initWith:@"foo/child"]];
  236. id<FNode> updatedNode = [compoundWrite applyToNode:[FEmptyNode emptyNode]];
  237. id<FNode> updatedPriority = [updatedNode getChild:[[FPath alloc] initWith:@"foo"]].getPriority;
  238. XCTAssertEqualObjects(updatedPriority, self.priorityNode, @"Should get priority");
  239. }
  240. - (void)testChildMergeLooksIntoUpdateNode {
  241. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  242. id<FNode> update = [FSnapshotUtilities nodeFrom:@{@"foo" : @"foo-value", @"bar" : @"bar-value"}];
  243. compoundWrite = [compoundWrite addWrite:update atPath:[FPath empty]];
  244. compoundWrite = [compoundWrite childCompoundWriteAtPath:[[FPath alloc] initWith:@"foo"]];
  245. id<FNode> updatedNode = [compoundWrite applyToNode:[FEmptyNode emptyNode]];
  246. id<FNode> expectedNode = [FSnapshotUtilities nodeFrom:@"foo-value"];
  247. XCTAssertEqualObjects(updatedNode, expectedNode, @"Child merge should get updates.");
  248. }
  249. - (void)testChildMergeRemovesNodeOnDeeperPaths {
  250. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  251. id<FNode> update = [FSnapshotUtilities nodeFrom:@{@"foo" : @"foo-value", @"bar" : @"bar-value"}];
  252. compoundWrite = [compoundWrite addWrite:update atPath:[FPath empty]];
  253. compoundWrite =
  254. [compoundWrite childCompoundWriteAtPath:[[FPath alloc] initWith:@"foo/not/existing"]];
  255. id<FNode> updatedNode = [compoundWrite applyToNode:self.leafNode];
  256. id<FNode> expectedNode = [FEmptyNode emptyNode];
  257. XCTAssertEqualObjects(updatedNode, expectedNode, @"Should not have node.");
  258. }
  259. - (void)testChildMergeWithEmptyPathIsSameMerge {
  260. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  261. id<FNode> update = [FSnapshotUtilities nodeFrom:@{@"foo" : @"foo-value", @"bar" : @"bar-value"}];
  262. compoundWrite = [compoundWrite addWrite:update atPath:[FPath empty]];
  263. XCTAssertEqualObjects([compoundWrite childCompoundWriteAtPath:[FPath empty]], compoundWrite,
  264. @"Child merge with empty path should be the same merge.");
  265. }
  266. - (void)testRootUpdateRemovesRootPriority {
  267. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  268. compoundWrite = [compoundWrite addWrite:self.priorityNode
  269. atPath:[[FPath alloc] initWith:@".priority"]];
  270. id<FNode> update = [FSnapshotUtilities nodeFrom:@"foo"];
  271. compoundWrite = [compoundWrite addWrite:update atPath:[FPath empty]];
  272. id<FNode> updatedNode = [compoundWrite applyToNode:[FEmptyNode emptyNode]];
  273. XCTAssertEqualObjects(updatedNode, update, @"Root update should remove root priority");
  274. }
  275. - (void)testDeepUpdateRemovesPriorityThere {
  276. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  277. compoundWrite = [compoundWrite addWrite:self.priorityNode
  278. atPath:[[FPath alloc] initWith:@"foo/.priority"]];
  279. id<FNode> update = [FSnapshotUtilities nodeFrom:@"bar"];
  280. compoundWrite = [compoundWrite addWrite:update atPath:[[FPath alloc] initWith:@"foo"]];
  281. id<FNode> updatedNode = [compoundWrite applyToNode:[FEmptyNode emptyNode]];
  282. id<FNode> expectedNode = [FSnapshotUtilities nodeFrom:@{@"foo" : @"bar"}];
  283. XCTAssertEqualObjects(updatedNode, expectedNode, @"Deep update should remove priority there");
  284. }
  285. - (void)testAddingUpdatesAtPathWorks {
  286. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  287. NSMutableDictionary *updateDictionary = [[NSMutableDictionary alloc] init];
  288. [updateDictionary setObject:@"foo-value" forKey:@"foo"];
  289. [updateDictionary setObject:@"bar-value" forKey:@"bar"];
  290. FCompoundWrite *updates = [FCompoundWrite compoundWriteWithValueDictionary:updateDictionary];
  291. compoundWrite = [compoundWrite addCompoundWrite:updates
  292. atPath:[[FPath alloc] initWith:@"child-1"]];
  293. NSDictionary *expectedChild1 = @{@"foo" : @"foo-value", @"bar" : @"bar-value"};
  294. id<FNode> expectedNode =
  295. [self.baseNode updateImmediateChild:@"child-1"
  296. withNewChild:[FSnapshotUtilities nodeFrom:expectedChild1]];
  297. id<FNode> updatedNode = [compoundWrite applyToNode:self.baseNode];
  298. XCTAssertEqualObjects(updatedNode, expectedNode, @"Adding updates at a path should work.");
  299. }
  300. - (void)testAddingUpdatesAtRootWorks {
  301. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  302. NSMutableDictionary *updateDictionary = [[NSMutableDictionary alloc] init];
  303. [updateDictionary setObject:@"new-value-1" forKey:@"child-1"];
  304. [updateDictionary setObject:[NSNull null] forKey:@"child-2"];
  305. [updateDictionary setObject:@"value-3" forKey:@"child-3"];
  306. FCompoundWrite *updates = [FCompoundWrite compoundWriteWithValueDictionary:updateDictionary];
  307. compoundWrite = [compoundWrite addCompoundWrite:updates atPath:[FPath empty]];
  308. NSDictionary *expected = @{@"child-1" : @"new-value-1", @"child-3" : @"value-3"};
  309. id<FNode> updatedNode = [compoundWrite applyToNode:self.baseNode];
  310. id<FNode> expectedNode = [FSnapshotUtilities nodeFrom:expected];
  311. XCTAssertEqualObjects(updatedNode, expectedNode, @"Adding updates at root should work.");
  312. }
  313. - (void)testChildMergeOfRootPriorityWorks {
  314. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  315. compoundWrite = [compoundWrite addWrite:self.priorityNode
  316. atPath:[[FPath alloc] initWith:@".priority"]];
  317. compoundWrite = [compoundWrite childCompoundWriteAtPath:[[FPath alloc] initWith:@".priority"]];
  318. id<FNode> updatedNode = [compoundWrite applyToNode:[FEmptyNode emptyNode]];
  319. XCTAssertEqualObjects(updatedNode, self.priorityNode,
  320. @"Child merge of root priority should work.");
  321. }
  322. - (void)testCompleteChildrenOnlyReturnsCompleteOverwrites {
  323. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  324. compoundWrite = [compoundWrite addWrite:self.leafNode atPath:[[FPath alloc] initWith:@"child-1"]];
  325. NSArray *expectedChildren = @[ [[FNamedNode alloc] initWithName:@"child-1"
  326. andNode:self.leafNode] ];
  327. NSArray *completeChildren = [compoundWrite completeChildren];
  328. XCTAssertEqualObjects(completeChildren, expectedChildren,
  329. @"Complete children should only return on complete overwrites.");
  330. }
  331. - (void)testCompleteChildrenOnlyReturnsEmptyOverwrites {
  332. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  333. compoundWrite = [compoundWrite addWrite:[FEmptyNode emptyNode]
  334. atPath:[[FPath alloc] initWith:@"child-1"]];
  335. NSArray *expectedChildren = @[ [[FNamedNode alloc] initWithName:@"child-1"
  336. andNode:[FEmptyNode emptyNode]] ];
  337. NSArray *completeChildren = [compoundWrite completeChildren];
  338. XCTAssertEqualObjects(completeChildren, expectedChildren,
  339. @"Complete children should return list with empty on empty overwrites.");
  340. }
  341. - (void)testCompleteChildrenDoesntReturnDeepOverwrites {
  342. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  343. compoundWrite = [compoundWrite addWrite:self.leafNode
  344. atPath:[[FPath alloc] initWith:@"child-1/deep/path"]];
  345. NSArray *expectedChildren = @[];
  346. NSArray *completeChildren = [compoundWrite completeChildren];
  347. XCTAssertEqualObjects(completeChildren, expectedChildren,
  348. @"Should not get complete children on deep overwrites.");
  349. }
  350. - (void)testCompleteChildrenReturnAllCompleteChildrenButNoIncomplete {
  351. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  352. compoundWrite = [compoundWrite addWrite:self.leafNode
  353. atPath:[[FPath alloc] initWith:@"child-1/deep/path"]];
  354. compoundWrite = [compoundWrite addWrite:self.leafNode atPath:[[FPath alloc] initWith:@"child-2"]];
  355. compoundWrite = [compoundWrite addWrite:[FEmptyNode emptyNode]
  356. atPath:[[FPath alloc] initWith:@"child-3"]];
  357. NSDictionary *expected = @{@"child-2" : self.leafNode, @"child-3" : [FEmptyNode emptyNode]};
  358. NSMutableDictionary *actual = [[NSMutableDictionary alloc] init];
  359. for (FNamedNode *node in compoundWrite.completeChildren) {
  360. [actual setObject:node.node forKey:node.name];
  361. }
  362. XCTAssertEqualObjects(actual, expected,
  363. @"Complete children should get returned, but not incomplete ones.");
  364. }
  365. - (void)testCompleteChildrenReturnAllChildrenForRootSet {
  366. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  367. compoundWrite = [compoundWrite addWrite:self.baseNode atPath:[FPath empty]];
  368. NSDictionary *expected = @{
  369. @"child-1" : [FSnapshotUtilities nodeFrom:@"value-1"],
  370. @"child-2" : [FSnapshotUtilities nodeFrom:@"value-2"]
  371. };
  372. NSMutableDictionary *actual = [[NSMutableDictionary alloc] init];
  373. for (FNamedNode *node in compoundWrite.completeChildren) {
  374. [actual setObject:node.node forKey:node.name];
  375. }
  376. XCTAssertEqualObjects(actual, expected,
  377. @"Complete children should return all children on root set.");
  378. }
  379. - (void)testEmptyMergeHasNoShadowingWrite {
  380. XCTAssertFalse([[FCompoundWrite emptyWrite] hasCompleteWriteAtPath:[FPath empty]],
  381. @"Empty merge has no shadowing write.");
  382. }
  383. - (void)testCompoundWriteWithEmptyRootHasShadowingWrite {
  384. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  385. compoundWrite = [compoundWrite addWrite:[FEmptyNode emptyNode] atPath:[FPath empty]];
  386. XCTAssertTrue([compoundWrite hasCompleteWriteAtPath:[FPath empty]],
  387. @"Empty write should have shadowing write at root.");
  388. XCTAssertTrue([compoundWrite hasCompleteWriteAtPath:[[FPath alloc] initWith:@"child"]],
  389. @"Empty write should have complete write at child.");
  390. }
  391. - (void)testCompoundWriteWithRootHasShadowingWrite {
  392. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  393. compoundWrite = [compoundWrite addWrite:self.leafNode atPath:[FPath empty]];
  394. XCTAssertTrue([compoundWrite hasCompleteWriteAtPath:[FPath empty]],
  395. @"Root write should have shadowing write at root.");
  396. XCTAssertTrue([compoundWrite hasCompleteWriteAtPath:[[FPath alloc] initWith:@"child"]],
  397. @"Root write should have complete write at child.");
  398. }
  399. - (void)testCompoundWriteWithDeepUpdateHasShadowingWrite {
  400. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  401. compoundWrite = [compoundWrite addWrite:self.leafNode
  402. atPath:[[FPath alloc] initWith:@"deep/update"]];
  403. XCTAssertFalse([compoundWrite hasCompleteWriteAtPath:[FPath empty]],
  404. @"Deep write should not have complete write at root.");
  405. XCTAssertFalse([compoundWrite hasCompleteWriteAtPath:[[FPath alloc] initWith:@"deep"]],
  406. @"Deep write should not have should have complete write at child.");
  407. XCTAssertTrue([compoundWrite hasCompleteWriteAtPath:[[FPath alloc] initWith:@"deep/update"]],
  408. @"Deep write should have complete write at deep child.");
  409. }
  410. - (void)testCompoundWriteWithPriorityUpdateHasShadowingWrite {
  411. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  412. compoundWrite = [compoundWrite addWrite:self.priorityNode
  413. atPath:[[FPath alloc] initWith:@".priority"]];
  414. XCTAssertFalse([compoundWrite hasCompleteWriteAtPath:[FPath empty]],
  415. @"Write with priority at root should not have complete write at root.");
  416. XCTAssertTrue([compoundWrite hasCompleteWriteAtPath:[[FPath alloc] initWith:@".priority"]],
  417. @"Write with priority at root should have complete priority.");
  418. }
  419. - (void)testUpdatesCanBeRemoved {
  420. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  421. id<FNode> update = [FSnapshotUtilities nodeFrom:@{@"foo" : @"foo-value", @"bar" : @"bar-value"}];
  422. compoundWrite = [compoundWrite addWrite:update atPath:[[FPath alloc] initWith:@"child-1"]];
  423. compoundWrite = [compoundWrite removeWriteAtPath:[[FPath alloc] initWith:@"child-1"]];
  424. id<FNode> updatedNode = [compoundWrite applyToNode:self.baseNode];
  425. XCTAssertEqualObjects(updatedNode, self.baseNode, @"Updates should be removed.");
  426. }
  427. - (void)testDeepRemovesHasNoEffectOnOverlayingSet {
  428. // TODO I don't get this one.
  429. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  430. id<FNode> update1 = [FSnapshotUtilities nodeFrom:@{@"foo" : @"foo-value", @"bar" : @"bar-value"}];
  431. id<FNode> update2 = [FSnapshotUtilities nodeFrom:@"baz-value"];
  432. id<FNode> update3 = [FSnapshotUtilities nodeFrom:@"new-foo-value"];
  433. compoundWrite = [compoundWrite addWrite:update1 atPath:[[FPath alloc] initWith:@"child-1"]];
  434. compoundWrite = [compoundWrite addWrite:update2 atPath:[[FPath alloc] initWith:@"child-1/baz"]];
  435. compoundWrite = [compoundWrite addWrite:update3 atPath:[[FPath alloc] initWith:@"child-1/foo"]];
  436. compoundWrite = [compoundWrite removeWriteAtPath:[[FPath alloc] initWith:@"child-1/foo"]];
  437. NSDictionary *expected =
  438. @{@"foo" : @"new-foo-value", @"bar" : @"bar-value", @"baz" : @"baz-value"};
  439. id<FNode> updatedNode = [compoundWrite applyToNode:self.baseNode];
  440. id<FNode> expectedNode =
  441. [self.baseNode updateImmediateChild:@"child-1"
  442. withNewChild:[FSnapshotUtilities nodeFrom:expected]];
  443. XCTAssertEqualObjects(updatedNode, expectedNode,
  444. @"Deep removes should have no effect on overlaying set.");
  445. }
  446. - (void)testRemoveAtPathWithoutSetIsWithoutEffect {
  447. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  448. id<FNode> update1 = [FSnapshotUtilities nodeFrom:@{@"foo" : @"foo-value", @"bar" : @"bar-value"}];
  449. id<FNode> update2 = [FSnapshotUtilities nodeFrom:@"baz-value"];
  450. id<FNode> update3 = [FSnapshotUtilities nodeFrom:@"new-foo-value"];
  451. compoundWrite = [compoundWrite addWrite:update1 atPath:[[FPath alloc] initWith:@"child-1"]];
  452. compoundWrite = [compoundWrite addWrite:update2 atPath:[[FPath alloc] initWith:@"child-1/baz"]];
  453. compoundWrite = [compoundWrite addWrite:update3 atPath:[[FPath alloc] initWith:@"child-1/foo"]];
  454. compoundWrite = [compoundWrite removeWriteAtPath:[[FPath alloc] initWith:@"child-2"]];
  455. NSDictionary *expected =
  456. @{@"foo" : @"new-foo-value", @"bar" : @"bar-value", @"baz" : @"baz-value"};
  457. id<FNode> updatedNode = [compoundWrite applyToNode:self.baseNode];
  458. id<FNode> expectedNode =
  459. [self.baseNode updateImmediateChild:@"child-1"
  460. withNewChild:[FSnapshotUtilities nodeFrom:expected]];
  461. XCTAssertEqualObjects(updatedNode, expectedNode,
  462. @"Removing at path without a set should have no effect.");
  463. }
  464. - (void)testCanRemovePriority {
  465. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  466. compoundWrite = [compoundWrite addWrite:self.priorityNode
  467. atPath:[[FPath alloc] initWith:@".priority"]];
  468. compoundWrite = [compoundWrite removeWriteAtPath:[[FPath alloc] initWith:@".priority"]];
  469. [self assertAppliedCompoundWrite:compoundWrite
  470. equalsNode:self.leafNode
  471. withPriority:[FEmptyNode emptyNode]];
  472. }
  473. - (void)testRemovingOnlyAffectsRemovedPath {
  474. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  475. NSDictionary *updateDictionary =
  476. @{@"child-1" : @"new-value-1", @"child-2" : [NSNull null], @"child-3" : @"value-3"};
  477. FCompoundWrite *updates = [FCompoundWrite compoundWriteWithValueDictionary:updateDictionary];
  478. compoundWrite = [compoundWrite addCompoundWrite:updates atPath:[FPath empty]];
  479. compoundWrite = [compoundWrite removeWriteAtPath:[[FPath alloc] initWith:@"child-2"]];
  480. NSDictionary *expected =
  481. @{@"child-1" : @"new-value-1", @"child-2" : @"value-2", @"child-3" : @"value-3"};
  482. id<FNode> updatedNode = [compoundWrite applyToNode:self.baseNode];
  483. id<FNode> expectedNode = [FSnapshotUtilities nodeFrom:expected];
  484. XCTAssertEqualObjects(updatedNode, expectedNode, @"Removing should only affected removed paths");
  485. }
  486. - (void)testRemoveRemovesAllDeeperSets {
  487. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  488. id<FNode> update2 = [FSnapshotUtilities nodeFrom:@"baz-value"];
  489. id<FNode> update3 = [FSnapshotUtilities nodeFrom:@"new-foo-value"];
  490. compoundWrite = [compoundWrite addWrite:update2 atPath:[[FPath alloc] initWith:@"child-1/baz"]];
  491. compoundWrite = [compoundWrite addWrite:update3 atPath:[[FPath alloc] initWith:@"child-1/foo"]];
  492. compoundWrite = [compoundWrite removeWriteAtPath:[[FPath alloc] initWith:@"child-1"]];
  493. id<FNode> updatedNode = [compoundWrite applyToNode:self.baseNode];
  494. XCTAssertEqualObjects(updatedNode, self.baseNode, @"Remove should remove deeper sets.");
  495. }
  496. - (void)testRemoveAtRootAlsoRemovesPriority {
  497. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  498. compoundWrite = [compoundWrite addWrite:[[FLeafNode alloc] initWithValue:@"foo"
  499. withPriority:self.priorityNode]
  500. atPath:[FPath empty]];
  501. compoundWrite = [compoundWrite removeWriteAtPath:[FPath empty]];
  502. id<FNode> node = [FSnapshotUtilities nodeFrom:@"value"];
  503. [self assertAppliedCompoundWrite:compoundWrite
  504. equalsNode:node
  505. withPriority:[FEmptyNode emptyNode]];
  506. }
  507. - (void)testUpdatingPriorityDoesntOverwriteLeafNode {
  508. // TODO I don't get this one.
  509. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  510. compoundWrite = [compoundWrite addWrite:self.leafNode atPath:[FPath empty]];
  511. compoundWrite = [compoundWrite addWrite:self.priorityNode
  512. atPath:[[FPath alloc] initWith:@"child/.priority"]];
  513. id<FNode> updatedNode = [compoundWrite applyToNode:[FEmptyNode emptyNode]];
  514. XCTAssertEqualObjects(updatedNode, self.leafNode,
  515. @"Updating priority should not overwrite leaf node.");
  516. }
  517. - (void)testUpdatingEmptyChildNodeDoesntOverwriteLeafNode {
  518. FCompoundWrite *compoundWrite = [FCompoundWrite emptyWrite];
  519. compoundWrite = [compoundWrite addWrite:self.leafNode atPath:[FPath empty]];
  520. compoundWrite = [compoundWrite addWrite:[FEmptyNode emptyNode]
  521. atPath:[[FPath alloc] initWith:@"child"]];
  522. id<FNode> updatedNode = [compoundWrite applyToNode:[FEmptyNode emptyNode]];
  523. XCTAssertEqualObjects(updatedNode, self.leafNode,
  524. @"Updating empty node should not overwrite leaf node.");
  525. }
  526. @end