FCompoundWriteTest.m 29 KB

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