| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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. |
| */ |
| package org.apache.jackrabbit.oak.spi.state; |
| |
| import static com.google.common.base.Preconditions.checkNotNull; |
| import static org.apache.jackrabbit.JcrConstants.JCR_LASTMODIFIED; |
| import static org.apache.jackrabbit.oak.api.Type.LONG; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| import static org.junit.Assume.assumeTrue; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Calendar; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.concurrent.CountDownLatch; |
| import java.util.concurrent.TimeUnit; |
| import java.util.concurrent.atomic.AtomicReference; |
| |
| import org.apache.jackrabbit.oak.NodeStoreFixtures; |
| import org.apache.jackrabbit.oak.OakBaseTest; |
| import org.apache.jackrabbit.oak.api.CommitFailedException; |
| import org.apache.jackrabbit.oak.api.PropertyState; |
| import org.apache.jackrabbit.oak.fixture.NodeStoreFixture; |
| import org.apache.jackrabbit.oak.plugins.commit.ConflictHook; |
| import org.apache.jackrabbit.oak.plugins.commit.ConflictValidatorProvider; |
| import org.apache.jackrabbit.oak.plugins.commit.JcrConflictHandler; |
| import org.apache.jackrabbit.oak.plugins.memory.MultiStringPropertyState; |
| import org.apache.jackrabbit.oak.spi.commit.CommitHook; |
| import org.apache.jackrabbit.oak.spi.commit.CommitInfo; |
| import org.apache.jackrabbit.oak.spi.commit.CompositeHook; |
| import org.apache.jackrabbit.oak.spi.commit.EditorHook; |
| import org.apache.jackrabbit.oak.spi.commit.EmptyHook; |
| import org.apache.jackrabbit.oak.spi.commit.Observable; |
| import org.apache.jackrabbit.oak.spi.commit.Observer; |
| import org.jetbrains.annotations.NotNull; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| import com.google.common.collect.Iterables; |
| import com.google.common.collect.Maps; |
| import com.google.common.collect.Sets; |
| |
| public class NodeStoreTest extends OakBaseTest { |
| private NodeState root; |
| |
| public NodeStoreTest(NodeStoreFixture fixture) { |
| super(fixture); |
| } |
| |
| @Before |
| public void setUp() throws Exception { |
| NodeBuilder builder = store.getRoot().builder(); |
| NodeBuilder test = builder.child("test"); |
| test.setProperty("a", 1); |
| test.setProperty("b", 2); |
| test.setProperty("c", 3); |
| test.child("x"); |
| test.child("y"); |
| test.child("z"); |
| root = store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY); |
| } |
| |
| @After |
| public void tearDown() { |
| fixture.dispose(store); |
| } |
| |
| @Test |
| public void getRoot() { |
| assertEquals(root, store.getRoot()); |
| assertEquals(root.getChildNode("test"), store.getRoot().getChildNode("test")); |
| assertEquals(root.getChildNode("test").getChildNode("x"), |
| store.getRoot().getChildNode("test").getChildNode("x")); |
| assertEquals(root.getChildNode("test").getChildNode("any"), |
| store.getRoot().getChildNode("test").getChildNode("any")); |
| assertEquals(root.getChildNode("test").getProperty("a"), |
| store.getRoot().getChildNode("test").getProperty("a")); |
| assertEquals(root.getChildNode("test").getProperty("any"), |
| store.getRoot().getChildNode("test").getProperty("any")); |
| } |
| |
| @Test |
| public void addExistingNode() throws CommitFailedException { |
| // FIXME OAK-1550 Incorrect handling of addExistingNode conflict in NodeStore |
| assumeTrue(fixture != NodeStoreFixtures.DOCUMENT_MEM); |
| assumeTrue(fixture != NodeStoreFixtures.DOCUMENT_NS); |
| assumeTrue(fixture != NodeStoreFixtures.DOCUMENT_RDB); |
| |
| CommitHook hook = new CompositeHook( |
| new ConflictHook(JcrConflictHandler.createJcrConflictHandler()), |
| new EditorHook(new ConflictValidatorProvider()) |
| ); |
| |
| NodeBuilder b1 = store.getRoot().builder(); |
| NodeBuilder b2 = store.getRoot().builder(); |
| |
| // make sure we make it past DocumentRootBuilder.UPDATE_LIMIT |
| // in order to see the conflict handling of the stores involved |
| // rather the in memory one from AbstractRebaseDiff |
| for (int k = 0; k < 1002; k++) { |
| b1.setChildNode("n" + k); |
| b2.setChildNode("m" + k); |
| } |
| |
| b1.setChildNode("conflict"); |
| b2.setChildNode("conflict"); |
| |
| store.merge(b1, hook, CommitInfo.EMPTY); |
| store.merge(b2, hook, CommitInfo.EMPTY); |
| } |
| |
| @Test(expected = IllegalArgumentException.class) |
| public void mergeNodeRoot() throws CommitFailedException { |
| NodeBuilder x = store.getRoot().builder().getChildNode("x"); |
| store.merge(x, EmptyHook.INSTANCE, CommitInfo.EMPTY); |
| } |
| |
| @Test |
| public void addExistingNodeJCRLastModified() throws CommitFailedException { |
| CommitHook hook = new CompositeHook( |
| new ConflictHook(JcrConflictHandler.createJcrConflictHandler()), |
| new EditorHook(new ConflictValidatorProvider()) |
| ); |
| |
| NodeBuilder b1 = store.getRoot().builder(); |
| NodeBuilder b2 = store.getRoot().builder(); |
| |
| Calendar calendar = Calendar.getInstance(); |
| b1.setChildNode("addExistingNodeJCRLastModified").setProperty(JCR_LASTMODIFIED, calendar); |
| calendar.add(Calendar.MINUTE, 1); |
| b2.setChildNode("addExistingNodeJCRLastModified").setProperty(JCR_LASTMODIFIED, calendar); |
| |
| b1.setChildNode("conflict"); |
| b2.setChildNode("conflict"); |
| |
| store.merge(b1, hook, CommitInfo.EMPTY); |
| store.merge(b2, hook, CommitInfo.EMPTY); |
| } |
| |
| @Test |
| public void addChangeChangedJCRLastModified() throws CommitFailedException { |
| CommitHook hook = new CompositeHook( |
| new ConflictHook(JcrConflictHandler.createJcrConflictHandler()), |
| new EditorHook(new ConflictValidatorProvider()) |
| ); |
| |
| NodeBuilder b = store.getRoot().builder(); |
| Calendar calendar = Calendar.getInstance(); |
| b.setChildNode("addExistingNodeJCRLastModified").setProperty(JCR_LASTMODIFIED, calendar); |
| store.merge(b, hook, CommitInfo.EMPTY); |
| |
| NodeBuilder b1 = store.getRoot().builder(); |
| NodeBuilder b2 = store.getRoot().builder(); |
| |
| calendar.add(Calendar.MINUTE, 1); |
| b1.setChildNode("addExistingNodeJCRLastModified").setProperty(JCR_LASTMODIFIED, calendar); |
| calendar.add(Calendar.MINUTE, 1); |
| b2.setChildNode("addExistingNodeJCRLastModified").setProperty(JCR_LASTMODIFIED, calendar); |
| |
| b1.setChildNode("conflict"); |
| b2.setChildNode("conflict"); |
| |
| store.merge(b1, hook, CommitInfo.EMPTY); |
| store.merge(b2, hook, CommitInfo.EMPTY); |
| } |
| |
| @Test |
| public void simpleMerge() throws CommitFailedException { |
| NodeBuilder rootBuilder = store.getRoot().builder(); |
| NodeBuilder testBuilder = rootBuilder.child("test"); |
| NodeBuilder newNodeBuilder = testBuilder.child("newNode"); |
| |
| testBuilder.getChildNode("x").remove(); |
| |
| newNodeBuilder.setProperty("n", 42); |
| |
| // Assert changes are present in the builder |
| NodeState testState = rootBuilder.getNodeState().getChildNode("test"); |
| assertTrue(testState.getChildNode("newNode").exists()); |
| assertFalse(testState.getChildNode("x").exists()); |
| assertEquals(42, (long) testState.getChildNode("newNode").getProperty("n").getValue(LONG)); |
| |
| // Assert changes are not yet present in the trunk |
| testState = store.getRoot().getChildNode("test"); |
| assertFalse(testState.getChildNode("newNode").exists()); |
| assertTrue(testState.getChildNode("x").exists()); |
| |
| store.merge(rootBuilder, EmptyHook.INSTANCE, CommitInfo.EMPTY); |
| |
| // Assert changes are present in the trunk |
| testState = store.getRoot().getChildNode("test"); |
| assertTrue(testState.getChildNode("newNode").exists()); |
| assertFalse(testState.getChildNode("x").exists()); |
| assertEquals(42, (long) testState.getChildNode("newNode").getProperty("n").getValue(LONG)); |
| } |
| |
| @Test |
| public void afterCommitHook() throws CommitFailedException, InterruptedException { |
| assumeTrue(store instanceof Observable); |
| |
| final AtomicReference<NodeState> observedRoot = |
| new AtomicReference<NodeState>(null); |
| final CountDownLatch latch = new CountDownLatch(2); |
| |
| ((Observable) store).addObserver(new Observer() { |
| @Override |
| public void contentChanged( |
| @NotNull NodeState root, @NotNull CommitInfo info) { |
| if (root.getChildNode("test").hasChildNode("newNode")) { |
| observedRoot.set(checkNotNull(root)); |
| latch.countDown(); |
| } |
| } |
| }); |
| |
| NodeState root = store.getRoot(); |
| NodeBuilder rootBuilder= root.builder(); |
| NodeBuilder testBuilder = rootBuilder.child("test"); |
| NodeBuilder newNodeBuilder = testBuilder.child("newNode"); |
| |
| newNodeBuilder.setProperty("n", 42); |
| testBuilder.getChildNode("a").remove(); |
| |
| store.merge(rootBuilder, EmptyHook.INSTANCE, CommitInfo.EMPTY); |
| NodeState newRoot = store.getRoot(); // triggers the observer |
| latch.await(2, TimeUnit.SECONDS); |
| |
| NodeState after = observedRoot.get(); |
| assertNotNull(after); |
| assertTrue(after.getChildNode("test").getChildNode("newNode").exists()); |
| assertFalse(after.getChildNode("test").getChildNode("a").exists()); |
| assertEquals(42, (long) after.getChildNode("test").getChildNode("newNode").getProperty("n").getValue(LONG)); |
| assertEquals(newRoot, after); |
| } |
| |
| @Test |
| public void beforeCommitHook() throws CommitFailedException { |
| NodeState root = store.getRoot(); |
| NodeBuilder rootBuilder = root.builder(); |
| NodeBuilder testBuilder = rootBuilder.child("test"); |
| NodeBuilder newNodeBuilder = testBuilder.child("newNode"); |
| |
| newNodeBuilder.setProperty("n", 42); |
| |
| testBuilder.getChildNode("a").remove(); |
| |
| store.merge(rootBuilder, new CommitHook() { |
| @NotNull |
| @Override |
| public NodeState processCommit( |
| NodeState before, NodeState after, CommitInfo info) { |
| NodeBuilder rootBuilder = after.builder(); |
| NodeBuilder testBuilder = rootBuilder.child("test"); |
| testBuilder.child("fromHook"); |
| return rootBuilder.getNodeState(); |
| } |
| }, CommitInfo.EMPTY); |
| |
| NodeState test = store.getRoot().getChildNode("test"); |
| assertTrue(test.getChildNode("newNode").exists()); |
| assertTrue(test.getChildNode("fromHook").exists()); |
| assertFalse(test.getChildNode("a").exists()); |
| assertEquals(42, (long) test.getChildNode("newNode").getProperty("n").getValue(LONG)); |
| assertEquals(test, store.getRoot().getChildNode("test")); |
| } |
| |
| @Test |
| public void manyChildNodes() throws CommitFailedException { |
| NodeBuilder root = store.getRoot().builder(); |
| NodeBuilder parent = root.child("parent"); |
| for (int i = 0; i <= 100; i++) { |
| parent.child("child-" + i); |
| } |
| store.merge(root, EmptyHook.INSTANCE, CommitInfo.EMPTY); |
| |
| NodeState base = store.getRoot(); |
| root = base.builder(); |
| parent = root.child("parent"); |
| parent.child("child-new"); |
| store.merge(root, EmptyHook.INSTANCE, CommitInfo.EMPTY); |
| |
| Diff diff = new Diff(); |
| store.getRoot().compareAgainstBaseState(base, diff); |
| |
| assertEquals(0, diff.removed.size()); |
| assertEquals(1, diff.added.size()); |
| assertEquals("child-new", diff.added.get(0)); |
| |
| base = store.getRoot(); |
| root = base.builder(); |
| parent = root.getChildNode("parent"); |
| parent.getChildNode("child-new").moveTo(parent, "child-moved"); |
| store.merge(root, EmptyHook.INSTANCE, CommitInfo.EMPTY); |
| |
| diff = new Diff(); |
| store.getRoot().compareAgainstBaseState(base, diff); |
| |
| assertEquals(1, diff.removed.size()); |
| assertEquals("child-new", diff.removed.get(0)); |
| assertEquals(1, diff.added.size()); |
| assertEquals("child-moved", diff.added.get(0)); |
| |
| base = store.getRoot(); |
| root = base.builder(); |
| parent = root.child("parent"); |
| parent.child("child-moved").setProperty("foo", "value"); |
| parent.child("child-moved").setProperty( |
| new MultiStringPropertyState("bar", Arrays.asList("value"))); |
| store.merge(root, EmptyHook.INSTANCE, CommitInfo.EMPTY); |
| |
| diff = new Diff(); |
| store.getRoot().compareAgainstBaseState(base, diff); |
| |
| assertEquals(0, diff.removed.size()); |
| assertEquals(0, diff.added.size()); |
| assertEquals(2, diff.addedProperties.size()); |
| assertTrue(diff.addedProperties.contains("foo")); |
| assertTrue(diff.addedProperties.contains("bar")); |
| |
| base = store.getRoot(); |
| root = base.builder(); |
| parent = root.child("parent"); |
| parent.setProperty("foo", "value"); |
| parent.setProperty(new MultiStringPropertyState( |
| "bar", Arrays.asList("value"))); |
| store.merge(root, EmptyHook.INSTANCE, CommitInfo.EMPTY); |
| |
| diff = new Diff(); |
| store.getRoot().compareAgainstBaseState(base, diff); |
| |
| assertEquals(0, diff.removed.size()); |
| assertEquals(0, diff.added.size()); |
| assertEquals(2, diff.addedProperties.size()); |
| assertTrue(diff.addedProperties.contains("foo")); |
| assertTrue(diff.addedProperties.contains("bar")); |
| |
| base = store.getRoot(); |
| root = base.builder(); |
| parent = root.child("parent"); |
| parent.getChildNode("child-moved").remove(); |
| store.merge(root, EmptyHook.INSTANCE, CommitInfo.EMPTY); |
| |
| diff = new Diff(); |
| store.getRoot().compareAgainstBaseState(base, diff); |
| |
| assertEquals(1, diff.removed.size()); |
| assertEquals(0, diff.added.size()); |
| assertEquals("child-moved", diff.removed.get(0)); |
| } |
| |
| @Test |
| public void move() { |
| NodeBuilder test = store.getRoot().builder().getChildNode("test"); |
| NodeBuilder x = test.getChildNode("x"); |
| NodeBuilder y = test.getChildNode("y"); |
| assertTrue(x.moveTo(y, "xx")); |
| assertFalse(x.exists()); |
| assertTrue(y.exists()); |
| assertFalse(test.hasChildNode("x")); |
| assertTrue(y.hasChildNode("xx")); |
| } |
| |
| @Test |
| public void moveNonExisting() { |
| NodeBuilder test = store.getRoot().builder().getChildNode("test"); |
| NodeBuilder any = test.getChildNode("any"); |
| NodeBuilder y = test.getChildNode("y"); |
| assertFalse(any.moveTo(y, "xx")); |
| assertFalse(any.exists()); |
| assertTrue(y.exists()); |
| assertFalse(y.hasChildNode("xx")); |
| } |
| |
| @Test |
| public void moveToExisting() { |
| NodeBuilder test = store.getRoot().builder().getChildNode("test"); |
| NodeBuilder x = test.getChildNode("x"); |
| assertFalse(x.moveTo(test, "y")); |
| assertTrue(x.exists()); |
| assertTrue(test.hasChildNode("x")); |
| assertTrue(test.hasChildNode("y")); |
| } |
| |
| @Test |
| public void rename() { |
| NodeBuilder test = store.getRoot().builder().getChildNode("test"); |
| NodeBuilder x = test.getChildNode("x"); |
| assertTrue(x.moveTo(test, "xx")); |
| assertFalse(x.exists()); |
| assertFalse(test.hasChildNode("x")); |
| assertTrue(test.hasChildNode("xx")); |
| } |
| |
| @Test |
| public void renameNonExisting() { |
| NodeBuilder test = store.getRoot().builder().getChildNode("test"); |
| NodeBuilder any = test.getChildNode("any"); |
| assertFalse(any.moveTo(test, "xx")); |
| assertFalse(any.exists()); |
| assertFalse(test.hasChildNode("xx")); |
| } |
| |
| @Test |
| public void renameToExisting() { |
| NodeBuilder test = store.getRoot().builder().getChildNode("test"); |
| NodeBuilder x = test.getChildNode("x"); |
| assertFalse(x.moveTo(test, "y")); |
| assertTrue(x.exists()); |
| assertTrue(test.hasChildNode("x")); |
| assertTrue(test.hasChildNode("y")); |
| } |
| |
| @Test |
| public void moveToSelf() { |
| NodeBuilder test = store.getRoot().builder().getChildNode("test"); |
| NodeBuilder x = test.getChildNode("x"); |
| assertFalse(x.moveTo(test, "x")); |
| assertTrue(x.exists()); |
| assertTrue(test.hasChildNode("x")); |
| } |
| |
| @Test |
| public void moveToSelfNonExisting() { |
| NodeBuilder test = store.getRoot().builder().getChildNode("test"); |
| NodeBuilder any = test.getChildNode("any"); |
| assertFalse(any.moveTo(test, "any")); |
| assertFalse(any.exists()); |
| assertFalse(test.hasChildNode("any")); |
| } |
| |
| @Test |
| public void moveToDescendant() { |
| NodeBuilder test = store.getRoot().builder().getChildNode("test"); |
| NodeBuilder x = test.getChildNode("x"); |
| if (fixture == NodeStoreFixtures.SEGMENT_TAR || fixture == NodeStoreFixtures.MEMORY_NS |
| || fixture == NodeStoreFixtures.COMPOSITE_MEM || fixture == NodeStoreFixtures.COMPOSITE_SEGMENT |
| || fixture == NodeStoreFixtures.COW_DOCUMENT || fixture == NodeStoreFixtures.SEGMENT_AZURE) { |
| assertTrue(x.moveTo(x, "xx")); |
| assertFalse(x.exists()); |
| assertFalse(test.hasChildNode("x")); |
| } else { |
| assertFalse(x.moveTo(x, "xx")); |
| assertTrue(x.exists()); |
| assertTrue(test.hasChildNode("x")); |
| } |
| } |
| |
| @Test |
| public void oak965() throws CommitFailedException { |
| NodeStore store1 = init(fixture.createNodeStore()); |
| NodeStore store2 = init(fixture.createNodeStore()); |
| try { |
| NodeState tree1 = store1.getRoot(); |
| NodeState tree2 = store2.getRoot(); |
| tree1.equals(tree2); |
| } finally { |
| fixture.dispose(store1); |
| fixture.dispose(store2); |
| } |
| } |
| |
| private static NodeStore init(NodeStore store) throws CommitFailedException { |
| NodeBuilder builder = store.getRoot().builder(); |
| builder.setChildNode("root"); |
| store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY); |
| return store; |
| } |
| |
| @Test |
| public void merge() throws CommitFailedException { |
| NodeState base = store.getRoot(); |
| NodeBuilder builder1 = base.builder(); |
| |
| NodeBuilder builder2 = base.builder(); |
| |
| builder1.setChildNode("node1"); |
| builder2.setChildNode("node2"); |
| |
| store.merge(builder1, EmptyHook.INSTANCE, CommitInfo.EMPTY); |
| assertTrue(store.getRoot().hasChildNode("node1")); |
| assertFalse(store.getRoot().hasChildNode("node2")); |
| |
| store.merge(builder2, EmptyHook.INSTANCE, CommitInfo.EMPTY); |
| assertTrue(store.getRoot().hasChildNode("node1")); |
| assertTrue(store.getRoot().hasChildNode("node2")); |
| } |
| |
| @Test |
| public void compareAgainstBaseState0() throws CommitFailedException { |
| compareAgainstBaseState(0); |
| } |
| |
| @Test |
| public void compareAgainstBaseState20() throws CommitFailedException { |
| compareAgainstBaseState(20); |
| } |
| |
| @Test |
| public void compareAgainstBaseState100() throws CommitFailedException { |
| compareAgainstBaseState(100); |
| } |
| |
| @Test // OAK-1320 |
| public void rebaseWithFailedMerge() throws CommitFailedException { |
| NodeBuilder rootBuilder = store.getRoot().builder(); |
| rootBuilder.child("foo"); |
| |
| // commit something in between to force rebase |
| NodeBuilder b = store.getRoot().builder(); |
| b.child("bar"); |
| store.merge(b, EmptyHook.INSTANCE, CommitInfo.EMPTY); |
| |
| try { |
| store.merge(rootBuilder, new CommitHook() { |
| @NotNull |
| @Override |
| public NodeState processCommit( |
| NodeState before, NodeState after, CommitInfo info) |
| throws CommitFailedException { |
| throw new CommitFailedException("", 0, "commit rejected"); |
| } |
| }, CommitInfo.EMPTY); |
| fail("must throw CommitFailedException"); |
| } catch (CommitFailedException e) { |
| // expected |
| } |
| // merge again |
| NodeState root = store.merge( |
| rootBuilder, EmptyHook.INSTANCE, CommitInfo.EMPTY); |
| assertTrue(root.hasChildNode("bar")); |
| } |
| |
| @Test |
| public void checkpoints() throws Exception { |
| assumeTrue(fixture != NodeStoreFixtures.SEGMENT_TAR); |
| int numCps = 3; |
| Map<String, String> info = Maps.newHashMap(); |
| Set<String> cps = Sets.newHashSet(); |
| for (int i = 0; i < numCps; i++) { |
| info.put("key", "" + i); |
| cps.add(store.checkpoint(TimeUnit.HOURS.toMillis(1), info)); |
| } |
| assertEquals(numCps, cps.size()); |
| assertEquals(cps, Sets.newHashSet(store.checkpoints())); |
| Set<String> keys = Sets.newHashSet(); |
| for (String cp : cps) { |
| info = store.checkpointInfo(cp); |
| assertTrue(info.containsKey("key")); |
| keys.add(info.get("key")); |
| } |
| assertEquals(Sets.newHashSet("0", "1", "2"), keys); |
| while (!cps.isEmpty()) { |
| String cp = cps.iterator().next(); |
| cps.remove(cp); |
| store.release(cp); |
| assertEquals(cps.size(), Iterables.size(store.checkpoints())); |
| } |
| } |
| |
| private void compareAgainstBaseState(int childNodeCount) throws CommitFailedException { |
| NodeState before = store.getRoot(); |
| NodeBuilder builder = before.builder(); |
| for (int k = 0; k < childNodeCount; k++) { |
| builder.child("c" + k); |
| } |
| |
| builder.child("foo").child(":bar").child("quz").setProperty("p", "v"); |
| store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY); |
| |
| NodeState after = store.getRoot(); |
| Diff diff = new Diff(); |
| after.compareAgainstBaseState(before, diff); |
| |
| assertEquals(0, diff.removed.size()); |
| assertEquals(childNodeCount + 1, diff.added.size()); |
| assertEquals(0, diff.addedProperties.size()); |
| } |
| |
| private static class Diff extends DefaultNodeStateDiff { |
| final List<String> addedProperties = new ArrayList<String>(); |
| final List<String> added = new ArrayList<String>(); |
| final List<String> removed = new ArrayList<String>(); |
| |
| @Override |
| public boolean childNodeAdded(String name, NodeState after) { |
| added.add(name); |
| return true; |
| } |
| |
| @Override |
| public boolean childNodeDeleted(String name, NodeState before) { |
| removed.add(name); |
| return true; |
| } |
| |
| @Override |
| public boolean childNodeChanged( |
| String name, NodeState before, NodeState after) { |
| return after.compareAgainstBaseState(before, this); |
| } |
| |
| @Override |
| public boolean propertyAdded(PropertyState after) { |
| addedProperties.add(after.getName()); |
| return true; |
| } |
| } |
| |
| } |