Added: 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/multiplex/MultiplexingChildrenCountTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/multiplex/MultiplexingChildrenCountTest.java?rev=1767652&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/multiplex/MultiplexingChildrenCountTest.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/multiplex/MultiplexingChildrenCountTest.java
 Wed Nov  2 12:16:32 2016
@@ -0,0 +1,259 @@
+/*
+ * 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.plugins.multiplex;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState;
+import org.apache.jackrabbit.oak.plugins.memory.MemoryChildNodeEntry;
+import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
+import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
+import org.apache.jackrabbit.oak.spi.state.AbstractNodeState;
+import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.spi.state.ReadOnlyBuilder;
+import org.junit.Test;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import java.util.List;
+import java.util.Map;
+
+import static com.google.common.collect.Iterables.cycle;
+import static com.google.common.collect.Iterables.limit;
+import static com.google.common.collect.Iterables.transform;
+import static com.google.common.collect.Maps.newHashMap;
+import static java.lang.Long.MAX_VALUE;
+import static java.util.Arrays.asList;
+import static java.util.Collections.emptyList;
+import static 
org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
+import static org.junit.Assert.assertEquals;
+
+public class MultiplexingChildrenCountTest {
+
+    @Test
+    public void singleContributingStore() {
+        MountInfoProvider mip = new SimpleMountInfoProvider.Builder().build();
+        NodeStore globalStore = new MemoryNodeStore();
+        MultiplexingNodeStore multiplexingNodeStore = new 
MultiplexingNodeStore.Builder(mip, globalStore).build();
+
+        MultiplexingNodeStateBuilder b = new 
MultiplexingNodeStateBuilder(multiplexingNodeStore.ctx);
+        b.configureMount("/", MAX_VALUE);
+        assertEquals(MAX_VALUE, b.getNodeState().getChildNodeCount(123));
+
+        b.clear().configureMount("/", 10);
+        assertEquals(10, b.getNodeState().getChildNodeCount(200));
+    }
+
+    @Test
+    public void multipleContributingStores() {
+        MountInfoProvider mip = new 
SimpleMountInfoProvider.Builder().mount("libs", "/libs", "/libs1", "/libs2", 
"/libs3", "/libs4").build();
+        NodeStore globalStore = new MemoryNodeStore();
+        NodeStore libsStore = new MemoryNodeStore();
+
+        List<MountedNodeStore> mounts = Lists.newArrayList(); 
+        mounts.add(new MountedNodeStore(mip.getMountByName("libs"), 
libsStore));
+        MultiplexingNodeStore multiplexingNodeStore = new 
MultiplexingNodeStore(mip, globalStore, mounts);
+
+        MultiplexingNodeStateBuilder b = new 
MultiplexingNodeStateBuilder(multiplexingNodeStore.ctx);
+        TestingNodeState globalTestingNS = b.configureMount("/", 5);
+        TestingNodeState libsTestingNS = b.configureMount("/libs", "libs", 
"libs1", "libs2");
+
+        MultiplexingNodeState mns = b.getNodeState();
+
+        assertEquals(8, mns.getChildNodeCount(9));
+        assertEquals(5, globalTestingNS.fetchedChildren);
+        assertEquals(3, libsTestingNS.fetchedChildren);
+        globalTestingNS.fetchedChildren = 0;
+        libsTestingNS.fetchedChildren = 0;
+
+        assertEquals(MAX_VALUE, mns.getChildNodeCount(8));
+        assertEquals(5, globalTestingNS.fetchedChildren);
+        assertEquals(3, libsTestingNS.fetchedChildren);
+        globalTestingNS.fetchedChildren = 0;
+        libsTestingNS.fetchedChildren = 0;
+
+        assertEquals(MAX_VALUE, mns.getChildNodeCount(7));
+        assertEquals(5, globalTestingNS.fetchedChildren);
+        assertEquals(2, libsTestingNS.fetchedChildren);
+        globalTestingNS.fetchedChildren = 0;
+        libsTestingNS.fetchedChildren = 0;
+
+        assertEquals(8, mns.builder().getChildNodeCount(9));
+        assertEquals(5, globalTestingNS.fetchedChildren);
+        assertEquals(3, libsTestingNS.fetchedChildren);
+        globalTestingNS.fetchedChildren = 0;
+        libsTestingNS.fetchedChildren = 0;
+
+        assertEquals(MAX_VALUE, mns.builder().getChildNodeCount(8));
+        assertEquals(5, globalTestingNS.fetchedChildren);
+        assertEquals(3, libsTestingNS.fetchedChildren);
+        globalTestingNS.fetchedChildren = 0;
+        libsTestingNS.fetchedChildren = 0;
+
+        assertEquals(MAX_VALUE, mns.builder().getChildNodeCount(7));
+        assertEquals(5, globalTestingNS.fetchedChildren);
+        assertEquals(2, libsTestingNS.fetchedChildren);
+    }
+
+    @Test
+    public void contributingStoreReturnsInfinity() {
+        MountInfoProvider mip = new 
SimpleMountInfoProvider.Builder().mount("libs", "/libs", "/libs1", "/libs2", 
"/libs3", "/libs4").build();
+        NodeStore globalStore = new MemoryNodeStore();
+        NodeStore libsStore = new MemoryNodeStore();
+
+        List<MountedNodeStore> mounts = Lists.newArrayList(); 
+        mounts.add(new MountedNodeStore(mip.getMountByName("libs"), 
libsStore));
+        MultiplexingNodeStore multiplexingNodeStore = new 
MultiplexingNodeStore(mip, globalStore, mounts);
+
+        MultiplexingNodeStateBuilder b = new 
MultiplexingNodeStateBuilder(multiplexingNodeStore.ctx);
+        TestingNodeState globalTestingNS = b.configureMount("/", 5);
+        TestingNodeState libsTestingNS = b.configureMount("/libs", MAX_VALUE);
+
+        MultiplexingNodeState mns = b.getNodeState();
+
+        assertEquals(MAX_VALUE, mns.getChildNodeCount(100));
+        assertEquals(5, globalTestingNS.fetchedChildren);
+        assertEquals(0, libsTestingNS.fetchedChildren);
+        globalTestingNS.fetchedChildren = 0;
+        libsTestingNS.fetchedChildren = 0;
+
+        assertEquals(MAX_VALUE, mns.builder().getChildNodeCount(100));
+        assertEquals(5, globalTestingNS.fetchedChildren);
+        assertEquals(0, libsTestingNS.fetchedChildren);
+    }
+
+    private static class MultiplexingNodeStateBuilder {
+
+        private final Map<MountedNodeStore, NodeState> rootStates = 
newHashMap();
+
+        private final MultiplexingContext ctx;
+
+        public MultiplexingNodeStateBuilder(MultiplexingContext ctx) {
+            this.ctx = ctx;
+        }
+
+        public TestingNodeState configureMount(String mountPath, long 
children) {
+            TestingNodeState nodeState = new TestingNodeState(children);
+            rootStates.put(ctx.getOwningStore(mountPath), nodeState);
+            return nodeState;
+        }
+
+        public TestingNodeState configureMount(String mountPath, String... 
children) {
+            TestingNodeState nodeState = new TestingNodeState(children);
+            rootStates.put(ctx.getOwningStore(mountPath), nodeState);
+            return nodeState;
+        }
+
+        public MultiplexingNodeState getNodeState() {
+            return new MultiplexingNodeState("/", rootStates, ctx);
+        }
+
+        public MultiplexingNodeStateBuilder clear() {
+            rootStates.clear();
+            return this;
+        }
+    }
+
+    private static class TestingNodeState extends AbstractNodeState {
+
+        private final long childrenCount;
+
+        private final String[] children;
+
+        private long fetchedChildren = 0;
+
+        private TestingNodeState(long childrenCount) {
+            this.children = null;
+            this.childrenCount = childrenCount;
+        }
+
+        private TestingNodeState(String... children) {
+            this.children = children;
+            this.childrenCount = children.length;
+        }
+
+        @Override
+        public boolean exists() {
+            return true;
+        }
+
+        @Nonnull
+        @Override
+        public Iterable<? extends PropertyState> getProperties() {
+            return emptyList();
+        }
+
+        @Override
+        public boolean hasChildNode(@Nonnull String name) {
+            return false;
+        }
+
+        @Nonnull
+        @Override
+        public NodeState getChildNode(@Nonnull String name) throws 
IllegalArgumentException {
+            return EmptyNodeState.MISSING_NODE;
+        }
+
+        @Nonnull
+        @Override
+        public Iterable<? extends ChildNodeEntry> getChildNodeEntries() {
+            if (children == null) {
+                Iterable<? extends ChildNodeEntry> childrenIterable = 
cycle(new MemoryChildNodeEntry("child", EMPTY_NODE));
+                return asCountingIterable(limit(childrenIterable, 
childrenCount == MAX_VALUE ? 1000 : (int) childrenCount));
+            } else {
+                return asCountingIterable(transform(asList(children), new 
Function<String, ChildNodeEntry>() {
+                    @Nullable
+                    @Override
+                    public ChildNodeEntry apply(@Nullable String input) {
+                        return new MemoryChildNodeEntry(input, EMPTY_NODE);
+                    }
+                }));
+            }
+        }
+
+        @Override
+        public long getChildNodeCount(long max) {
+            return childrenCount;
+        }
+
+        @Nonnull
+        @Override
+        public NodeBuilder builder() {
+            return new ReadOnlyBuilder(this);
+        }
+
+        private <T> Iterable<T> asCountingIterable(Iterable<T> input) {
+            return Iterables.transform(input, new Function<T, T>() {
+                @Nullable
+                @Override
+                public T apply(@Nullable T input) {
+                    fetchedChildren++;
+                    return input;
+                }
+            });
+        }
+    }
+}

Added: 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/multiplex/MultiplexingCompareTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/multiplex/MultiplexingCompareTest.java?rev=1767652&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/multiplex/MultiplexingCompareTest.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/multiplex/MultiplexingCompareTest.java
 Wed Nov  2 12:16:32 2016
@@ -0,0 +1,163 @@
+/*
+ * 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.plugins.multiplex;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+import org.apache.jackrabbit.oak.spi.commit.EmptyHook;
+import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
+import org.apache.jackrabbit.oak.spi.state.DefaultNodeStateDiff;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.junit.Test;
+
+import java.util.List;
+import java.util.Set;
+
+import static com.google.common.collect.Sets.newHashSet;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class MultiplexingCompareTest {
+
+    @Test
+    public void reportedNodesAreWrapped() {
+        MountInfoProvider mip = new SimpleMountInfoProvider.Builder().build();
+        NodeStore globalStore = new MemoryNodeStore();
+        MultiplexingNodeStore multiplexingNodeStore = new 
MultiplexingNodeStore.Builder(mip, globalStore).build();
+
+        NodeBuilder builder = multiplexingNodeStore.getRoot().builder();
+        builder.child("changed");
+        builder.child("deleted");
+        NodeState base = builder.getNodeState();
+
+        builder.getChildNode("changed").setProperty("newProp", "xyz", 
Type.STRING);
+        builder.getChildNode("deleted").remove();
+        builder.child("added");
+        final NodeState modified = builder.getNodeState();
+
+        final Set<String> modifiedNodes = newHashSet();
+        modified.compareAgainstBaseState(base, new DefaultNodeStateDiff() {
+            @Override
+            public boolean childNodeAdded(String name, NodeState after) {
+                assertTrue(after instanceof MultiplexingNodeState);
+                assertEquals(name, "added");
+                modifiedNodes.add(name);
+                return true;
+            }
+
+            @Override
+            public boolean childNodeChanged(String name, NodeState before, 
NodeState after) {
+                assertTrue(before instanceof MultiplexingNodeState);
+                assertTrue(after instanceof MultiplexingNodeState);
+                assertEquals(name, "changed");
+                modifiedNodes.add(name);
+                return true;
+            }
+
+            @Override
+            public boolean childNodeDeleted(String name, NodeState before) {
+                assertTrue(before instanceof MultiplexingNodeState);
+                assertEquals(name, "deleted");
+                modifiedNodes.add(name);
+                return true;
+            }
+        });
+        assertEquals(ImmutableSet.of("added", "changed", "deleted"), 
modifiedNodes);
+    }
+
+    @Test
+    public void onlyPropertiesOnMainNodesAreCompared() throws 
CommitFailedException {
+        MountInfoProvider mip = new 
SimpleMountInfoProvider.Builder().mount("libs", "/libs").build();
+        NodeStore globalStore = new MemoryNodeStore();
+        NodeStore libsStore = new MemoryNodeStore();
+
+        List<MountedNodeStore> mounts = Lists.newArrayList(); 
+        mounts.add(new MountedNodeStore(mip.getMountByName("libs"), 
libsStore));
+        MultiplexingNodeStore multiplexingNodeStore = new 
MultiplexingNodeStore(mip, globalStore, mounts);
+
+        NodeState empty = multiplexingNodeStore.getRoot();
+
+        NodeBuilder builder = globalStore.getRoot().builder();
+        builder.setProperty("global-prop-1", "val");
+        builder.setProperty("global-prop-2", "val");
+        globalStore.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        NodeBuilder libsBuilder = libsStore.getRoot().builder();
+        libsBuilder.setProperty("libs-prop-1", "val");
+        libsBuilder.setProperty("libs-prop-2", "val");
+        libsStore.merge(libsBuilder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        NodeState modified = multiplexingNodeStore.getRoot();
+
+        final Set<String> addedProperties = newHashSet();
+        modified.compareAgainstBaseState(empty, new DefaultNodeStateDiff() {
+            @Override
+            public boolean propertyAdded(PropertyState after) {
+                addedProperties.add(after.getName());
+                return true;
+            }
+        });
+        assertEquals(ImmutableSet.of("global-prop-1", "global-prop-2"), 
addedProperties);
+    }
+
+    @Test
+    public void nodesOutsideTheMountsAreIgnored() throws CommitFailedException 
{
+        MountInfoProvider mip = new 
SimpleMountInfoProvider.Builder().mount("libs", "/libs").build();
+        NodeStore globalStore = new MemoryNodeStore();
+        NodeStore libsStore = new MemoryNodeStore();
+
+        List<MountedNodeStore> mounts = Lists.newArrayList();
+        mounts.add(new MountedNodeStore(mip.getMountByName("libs"), 
libsStore));
+        MultiplexingNodeStore multiplexingNodeStore = new 
MultiplexingNodeStore(mip, globalStore, mounts);
+
+        NodeState empty = multiplexingNodeStore.getRoot();
+
+        NodeBuilder builder = globalStore.getRoot().builder();
+        builder.child("global-child-1");
+        builder.child("global-child-2");
+        globalStore.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        NodeBuilder libsBuilder = libsStore.getRoot().builder();
+        libsBuilder.child("libs");
+        libsBuilder.child("libs-child-1");
+        libsBuilder.child("libs-child-2");
+        libsStore.merge(libsBuilder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        NodeState modified = multiplexingNodeStore.getRoot();
+
+        final Set<String> addedChildren = newHashSet();
+        modified.compareAgainstBaseState(empty, new DefaultNodeStateDiff() {
+            @Override
+            public boolean childNodeAdded(String name, NodeState after) {
+                addedChildren.add(name);
+                return true;
+            }
+        });
+        assertEquals(ImmutableSet.of("global-child-1", "global-child-2", 
"libs"), addedChildren);
+
+    }
+}

Modified: 
jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/NodeStoreFixtures.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/NodeStoreFixtures.java?rev=1767652&r1=1767651&r2=1767652&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/NodeStoreFixtures.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/NodeStoreFixtures.java
 Wed Nov  2 12:16:32 2016
@@ -28,6 +28,8 @@ import org.apache.jackrabbit.oak.fixture
 import org.apache.jackrabbit.oak.fixture.DocumentRdbFixture;
 import org.apache.jackrabbit.oak.fixture.MemoryFixture;
 import org.apache.jackrabbit.oak.fixture.NodeStoreFixture;
+import org.apache.jackrabbit.oak.plugins.multiplex.MultiplexingMemoryFixture;
+import org.apache.jackrabbit.oak.plugins.multiplex.MultiplexingSegmentFixture;
 import org.apache.jackrabbit.oak.plugins.segment.fixture.SegmentFixture;
 import org.apache.jackrabbit.oak.segment.fixture.SegmentTarFixture;
 
@@ -45,6 +47,10 @@ public class NodeStoreFixtures {
 
     public static final NodeStoreFixture DOCUMENT_MEM = new 
DocumentMemoryFixture();
 
+    public static final NodeStoreFixture MULTIPLEXED_SEGMENT = new 
MultiplexingSegmentFixture();
+
+    public static final NodeStoreFixture MULTIPLEXED_MEM = new 
MultiplexingMemoryFixture();
+
     public static Collection<Object[]> 
asJunitParameters(Set<FixturesHelper.Fixture> fixtures) {
         List<NodeStoreFixture> configuredFixtures = new 
ArrayList<NodeStoreFixture>();
         if (fixtures.contains(FixturesHelper.Fixture.DOCUMENT_NS)) {
@@ -65,6 +71,12 @@ public class NodeStoreFixtures {
         if (fixtures.contains(FixturesHelper.Fixture.SEGMENT_TAR)) {
             configuredFixtures.add(SEGMENT_TAR);
         }
+        if (fixtures.contains(FixturesHelper.Fixture.MULTIPLEXED_SEGMENT)) {
+            configuredFixtures.add(MULTIPLEXED_SEGMENT);
+        }
+        if (fixtures.contains(FixturesHelper.Fixture.MULTIPLEXED_MEM)) {
+            configuredFixtures.add(MULTIPLEXED_MEM);
+        }
 
         Collection<Object[]> result = new ArrayList<Object[]>();
         for (NodeStoreFixture f : configuredFixtures) {

Added: 
jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/plugins/multiplex/MultiplexingMemoryFixture.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/plugins/multiplex/MultiplexingMemoryFixture.java?rev=1767652&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/plugins/multiplex/MultiplexingMemoryFixture.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/plugins/multiplex/MultiplexingMemoryFixture.java
 Wed Nov  2 12:16:32 2016
@@ -0,0 +1,47 @@
+/*
+ * 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.plugins.multiplex;
+
+import org.apache.jackrabbit.oak.fixture.NodeStoreFixture;
+import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
+import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+
+public class MultiplexingMemoryFixture extends NodeStoreFixture {
+
+    private static final String MOUNT_PATH = "/tmp";
+
+    @Override
+    public NodeStore createNodeStore() {
+        MountInfoProvider mip = new SimpleMountInfoProvider.Builder()
+                .readOnlyMount("temp", MOUNT_PATH)
+                .build();
+
+        NodeStore globalStore = new MemoryNodeStore();
+        NodeStore tempMount = new MemoryNodeStore();
+
+        return new MultiplexingNodeStore.Builder(mip, 
globalStore).addMount("temp", tempMount).build();
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + " with a mount under " + 
MOUNT_PATH;
+    }
+
+}
\ No newline at end of file

Added: 
jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/plugins/multiplex/MultiplexingNodeStoreBuilderTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/plugins/multiplex/MultiplexingNodeStoreBuilderTest.java?rev=1767652&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/plugins/multiplex/MultiplexingNodeStoreBuilderTest.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/plugins/multiplex/MultiplexingNodeStoreBuilderTest.java
 Wed Nov  2 12:16:32 2016
@@ -0,0 +1,84 @@
+/*
+ * 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.plugins.multiplex;
+
+import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
+import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
+import org.junit.Test;
+
+public class MultiplexingNodeStoreBuilderTest {
+
+    @Test(expected = IllegalArgumentException.class)
+    public void builderRejectsTooManyReadWriteStores_oneExtra() {
+        MountInfoProvider mip = new SimpleMountInfoProvider.Builder()
+                .mount("temp", "/tmp")
+                .build();
+
+        new MultiplexingNodeStore.Builder(mip, new MemoryNodeStore())
+            .addMount("temp", new MemoryNodeStore())
+            .build();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void builderRejectsTooManyReadWriteStores_mixed() {
+        MountInfoProvider mip = new SimpleMountInfoProvider.Builder()
+                .mount("temp", "/tmp")
+                .readOnlyMount("readOnly", "/readOnly")
+                .build();
+
+        new MultiplexingNodeStore.Builder(mip, new MemoryNodeStore())
+            .addMount("temp", new MemoryNodeStore())
+            .addMount("readOnly", new MemoryNodeStore())
+            .build();
+    }
+
+    @Test
+    public void builderAcceptsMultipleReadOnlyStores() {
+        MountInfoProvider mip = new SimpleMountInfoProvider.Builder()
+                .readOnlyMount("readOnly", "/readOnly")
+                .readOnlyMount("readOnly2", "/readOnly2")
+                .build();
+
+        new MultiplexingNodeStore.Builder(mip, new MemoryNodeStore())
+            .addMount("readOnly", new MemoryNodeStore())
+            .addMount("readOnly2", new MemoryNodeStore())
+            .build();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void mismatchBetweenMountsAndStoresIsRejected() {
+        MountInfoProvider mip = new SimpleMountInfoProvider.Builder()
+                .mount("temp", "/tmp")
+                .build();
+
+        new MultiplexingNodeStore.Builder(mip, new MemoryNodeStore())
+                .build();
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void mismatchBetweenMountNameAndStoreName() {
+        MountInfoProvider mip = new SimpleMountInfoProvider.Builder()
+                .mount("temp", "/tmp")
+                .build();
+
+        new MultiplexingNodeStore.Builder(mip, new MemoryNodeStore())
+            .addMount("not-temp", new MemoryNodeStore())
+            .build();
+    }
+}
\ No newline at end of file

Added: 
jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/plugins/multiplex/MultiplexingNodeStoreTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/plugins/multiplex/MultiplexingNodeStoreTest.java?rev=1767652&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/plugins/multiplex/MultiplexingNodeStoreTest.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/plugins/multiplex/MultiplexingNodeStoreTest.java
 Wed Nov  2 12:16:32 2016
@@ -0,0 +1,948 @@
+/*
+ * 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.plugins.multiplex;
+
+import static com.google.common.base.Predicates.compose;
+import static com.google.common.collect.Iterables.filter;
+import static com.google.common.collect.Lists.newArrayList;
+import static org.apache.jackrabbit.oak.spi.state.ChildNodeEntry.GET_NAME;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.hasItems;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.Nullable;
+import javax.sql.DataSource;
+
+import com.google.common.base.Predicates;
+import org.apache.commons.io.FileUtils;
+import org.apache.jackrabbit.oak.api.Blob;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.plugins.document.DocumentMK;
+import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStore;
+import org.apache.jackrabbit.oak.plugins.document.rdb.RDBDataSourceFactory;
+import org.apache.jackrabbit.oak.plugins.document.rdb.RDBOptions;
+import org.apache.jackrabbit.oak.plugins.document.util.CountingDiff;
+import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
+import org.apache.jackrabbit.oak.plugins.segment.Segment;
+import org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStore;
+import org.apache.jackrabbit.oak.plugins.segment.file.FileStore;
+import org.apache.jackrabbit.oak.spi.blob.BlobStore;
+import org.apache.jackrabbit.oak.spi.blob.FileBlobStore;
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+import org.apache.jackrabbit.oak.spi.commit.EmptyHook;
+import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
+import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+@RunWith(Parameterized.class)
+public class MultiplexingNodeStoreTest {
+
+    private final NodeStoreKind root;
+    private final NodeStoreKind mounts;
+
+    private final List<NodeStoreRegistration> registrations = newArrayList();
+
+    private MultiplexingNodeStore store;
+    private NodeStore globalStore;
+    private NodeStore mountedStore;
+    private NodeStore deepMountedStore;
+    private NodeStore readOnlyStore;
+
+    @Parameters(name="Root: {0}, Mounts: {1}")
+    public static Collection<Object[]> data() {
+        return Arrays.asList(new Object[][] {
+            { NodeStoreKind.MEMORY, NodeStoreKind.MEMORY },
+            { NodeStoreKind.SEGMENT, NodeStoreKind.SEGMENT},
+            { NodeStoreKind.DOCUMENT_H2, NodeStoreKind.DOCUMENT_H2},
+            { NodeStoreKind.DOCUMENT_H2, NodeStoreKind.SEGMENT}
+        });
+    }
+
+    public MultiplexingNodeStoreTest(NodeStoreKind root, NodeStoreKind mounts) 
{
+        this.root = root;
+        this.mounts = mounts;
+    }
+
+    @Before
+    public void initStore() throws Exception {
+        MountInfoProvider mip = new SimpleMountInfoProvider.Builder()
+                .mount("temp", "/tmp")
+                .mount("deep", "/libs/mount")
+                .mount("empty", "/nowhere")
+                .readOnlyMount("readOnly", "/readOnly")
+                .build();
+
+        globalStore = register(root.create(null));
+        mountedStore = register(mounts.create("temp"));
+        deepMountedStore = register(mounts.create("deep"));
+        readOnlyStore = register(mounts.create("readOnly"));
+        NodeStore emptyStore = register(mounts.create("empty")); // this 
NodeStore will always be empty
+
+        // create a property on the root node
+        NodeBuilder builder = globalStore.getRoot().builder();
+        builder.setProperty("prop", "val");
+        globalStore.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        assertTrue(globalStore.getRoot().hasProperty("prop"));
+
+        // create a different sub-tree on the root store
+        builder = globalStore.getRoot().builder();
+        NodeBuilder libsBuilder = builder.child("libs");
+        libsBuilder.child("first");
+        libsBuilder.child("second");
+
+        // create an empty /apps node with a property
+        builder.child("apps").setProperty("prop", "val");
+
+        globalStore.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        assertThat(globalStore.getRoot().getChildNodeCount(10), equalTo(2l));
+
+        // create a /tmp child on the mounted store and set a property
+        builder = mountedStore.getRoot().builder();
+        NodeBuilder tmpBuilder = builder.child("tmp");
+        tmpBuilder.setProperty("prop1", "val1");
+        tmpBuilder.child("child1").setProperty("prop1", "val1");
+        tmpBuilder.child("child2");
+
+        mountedStore.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        assertTrue(mountedStore.getRoot().hasChildNode("tmp"));
+        
assertThat(mountedStore.getRoot().getChildNode("tmp").getChildNodeCount(10), 
equalTo(2l));
+
+        // populate /libs/mount/third in the deep mount, and include a property
+
+        builder = deepMountedStore.getRoot().builder();
+        
builder.child("libs").child("mount").child("third").setProperty("mounted", 
"true");
+
+        deepMountedStore.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        
assertTrue(deepMountedStore.getRoot().getChildNode("libs").getChildNode("mount").getChildNode("third").hasProperty("mounted"));
+
+        // populate /readonly with a single node
+        builder = readOnlyStore.getRoot().builder();
+        builder.child("readOnly");
+
+        readOnlyStore.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        // don't use the builder since it would fail due to too many 
read-write stores
+        // but for the purposes of testing the general correctness it's fine
+        List<MountedNodeStore> nonDefaultStores = Lists.newArrayList();
+        nonDefaultStores.add(new MountedNodeStore(mip.getMountByName("temp"), 
mountedStore));
+        nonDefaultStores.add(new MountedNodeStore(mip.getMountByName("deep"), 
deepMountedStore));
+        nonDefaultStores.add(new MountedNodeStore(mip.getMountByName("empty"), 
emptyStore));
+        nonDefaultStores.add(new 
MountedNodeStore(mip.getMountByName("readOnly"), readOnlyStore));
+        store = new MultiplexingNodeStore(mip, globalStore, nonDefaultStores);
+    }
+
+    @After
+    public void closeRepositories() throws Exception {
+        for ( NodeStoreRegistration reg : registrations ) {
+            reg.close();
+        }
+    }
+
+    @Test
+    public void rootExists() {
+        assertThat("root exists", store.getRoot().exists(), equalTo(true));
+    }
+
+    @Test
+    public void rootPropertyIsSet() {
+        assertThat("root[prop]", store.getRoot().hasProperty("prop"), 
equalTo(true));
+        assertThat("root[prop] = val", 
store.getRoot().getProperty("prop").getValue(Type.STRING), equalTo("val"));
+    }
+
+    @Test
+    public void nonMountedChildIsFound() {
+        assertThat("root.libs", store.getRoot().hasChildNode("libs"), 
equalTo(true));
+    }
+
+    @Test
+    public void nestedMountNodeIsVisible() {
+        assertThat("root.libs(childCount)", 
store.getRoot().getChildNode("libs").getChildNodeCount(10), equalTo(3l));
+    }
+
+    @Test
+    public void mixedMountsChildNodes() {
+        assertThat("root(childCount)", store.getRoot().getChildNodeCount(100), 
equalTo(4l));
+    }
+
+    @Test
+    public void mountedChildIsFound() {
+
+        assertThat("root.tmp", store.getRoot().hasChildNode("tmp"), 
equalTo(true));
+    }
+
+    @Test
+    public void childrenUnderMountAreFound() {
+        assertThat("root.tmp(childCount)", 
store.getRoot().getChildNode("tmp").getChildNodeCount(10), equalTo(2l));
+    }
+
+    @Test
+    public void childNodeEntryForMountIsMultiplexed() {
+        ChildNodeEntry libsNode = 
Iterables.find(store.getRoot().getChildNodeEntries(), new 
Predicate<ChildNodeEntry>() {
+
+            @Override
+            public boolean apply(ChildNodeEntry input) {
+                return input.getName().equals("libs");
+            }
+        });
+
+        assertThat("root.libs(childCount)", 
libsNode.getNodeState().getChildNodeCount(10), equalTo(3l));
+    }
+
+    @Test
+    public void contentBelongingToAnotherMountIsIgnored() throws Exception {
+        // create a /tmp/oops child on the root store
+        // these two nodes must be ignored
+        NodeBuilder builder = globalStore.getRoot().builder();
+        builder.child("tmp").child("oops");
+
+        globalStore.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        
assertTrue(globalStore.getRoot().getChildNode("tmp").hasChildNode("oops"));
+
+        assertFalse(store.getRoot().getChildNode("tmp").hasChildNode("oops"));
+    }
+
+    @Test
+    public void checkpoint() throws Exception {
+        String checkpoint = store.checkpoint(TimeUnit.DAYS.toMillis(1));
+
+        assertNotNull("checkpoint reference is null", checkpoint);
+
+        // create a new child /new in the root store
+        NodeBuilder globalBuilder = globalStore.getRoot().builder();
+        globalBuilder.child("new");
+        globalStore.merge(globalBuilder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        // create a new child /tmp/new in the mounted store
+        NodeBuilder mountedBuilder = mountedStore.getRoot().builder();
+        mountedBuilder.getChildNode("tmp").child("new");
+        mountedStore.merge(mountedBuilder, EmptyHook.INSTANCE, 
CommitInfo.EMPTY);
+
+        // create a new child /libs/mount/new in the deeply mounted store
+        NodeBuilder deepMountBuilder = deepMountedStore.getRoot().builder();
+        
deepMountBuilder.getChildNode("libs").getChildNode("mount").child("new");
+        deepMountedStore.merge(deepMountBuilder, EmptyHook.INSTANCE, 
CommitInfo.EMPTY);
+
+        assertFalse("store incorrectly exposes child at /new", 
store.retrieve(checkpoint).hasChildNode("new"));
+        assertFalse("store incorrectly exposes child at /tmp/new", 
store.retrieve(checkpoint).getChildNode("tmp").hasChildNode("new"));
+        assertFalse("store incorrectly exposes child at /libs/mount/new", 
store.retrieve(checkpoint).getChildNode("libs").getChildNode("mount").hasChildNode("new"));
+    }
+
+    @Test
+    public void checkpointInfo() throws Exception {
+        Map<String, String> info = Collections.singletonMap("key", "value");
+        String checkpoint = store.checkpoint(TimeUnit.DAYS.toMillis(1), info);
+
+        assertThat(store.checkpointInfo(checkpoint), equalTo(info));
+    }
+
+    @Test
+    public void release() {
+        String checkpoint = store.checkpoint(TimeUnit.DAYS.toMillis(1));
+
+        assertTrue(store.release(checkpoint));
+    }
+
+    @Test
+    public void existingBlobsInRootStoreAreRetrieved() throws Exception {
+        assumeTrue(root.supportsBlobCreation());
+
+        Blob createdBlob = globalStore.createBlob(createLargeBlob());
+        Blob retrievedBlob = store.getBlob(createdBlob.getReference());
+
+        assertThat(retrievedBlob.getContentIdentity(), 
equalTo(createdBlob.getContentIdentity()));
+    }
+
+
+    @Test
+    public void existingBlobsInMountedStoreAreRetrieved() throws Exception {
+
+        assumeTrue(mounts.supportsBlobCreation());
+
+        Blob createdBlob = mountedStore.createBlob(createLargeBlob());
+        Blob retrievedBlob = store.getBlob(createdBlob.getReference());
+
+        assertThat(retrievedBlob.getContentIdentity(), 
equalTo(createdBlob.getContentIdentity()));
+    }
+
+    @Test
+    public void blobCreation() throws Exception {
+        assumeTrue(root.supportsBlobCreation());
+
+        Blob createdBlob = store.createBlob(createLargeBlob());
+        Blob retrievedBlob = store.getBlob(createdBlob.getReference());
+
+        assertThat(retrievedBlob.getContentIdentity(), 
equalTo(createdBlob.getContentIdentity()));
+    }
+
+    @Test
+    public void setPropertyOnRootStore() throws Exception {
+        NodeBuilder builder = store.getRoot().builder();
+
+        builder.setProperty("newProp", "newValue");
+
+        store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        assertThat("Property must be visible in multiplexed store",
+                store.getRoot().getProperty("newProp").getValue(Type.STRING), 
equalTo("newValue"));
+
+        assertThat("Property must be visible in owning (root) store",
+                
globalStore.getRoot().getProperty("newProp").getValue(Type.STRING), 
equalTo("newValue"));
+    }
+
+    @Test
+    public void removePropertyFromRootStore() throws Exception {
+        NodeBuilder builder = store.getRoot().builder();
+
+        builder.removeProperty("prop");
+
+        store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        assertFalse("Property must be removed from multiplexed store", 
store.getRoot().hasProperty("prop"));
+        assertFalse("Property must be removed from owning (root) store", 
globalStore.getRoot().hasProperty("prop"));
+    }
+
+    @Test
+    public void createNodeInRootStore() throws Exception {
+        NodeBuilder builder = store.getRoot().builder();
+
+        builder.child("newNode");
+
+        store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        assertTrue("Node must be added to multiplexed store", 
store.getRoot().hasChildNode("newNode"));
+        assertTrue("Node must be added to owning (root) store", 
globalStore.getRoot().hasChildNode("newNode"));
+    }
+
+    @Test
+    public void createNodeInMountedStore() throws Exception {
+
+        NodeBuilder builder = store.getRoot().builder();
+
+        builder.getChildNode("tmp").child("newNode");
+
+        store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        assertTrue("Node must be added to multiplexed store", 
store.getRoot().getChildNode("tmp").hasChildNode("newNode"));
+        assertTrue("Node must be added to owning (mounted) store", 
mountedStore.getRoot().getChildNode("tmp").hasChildNode("newNode"));
+    }
+
+    @Test
+    public void removeNodeInRootStore() throws Exception {
+        NodeBuilder builder = store.getRoot().builder();
+
+        builder.getChildNode("apps").remove();
+
+        store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        assertFalse("Node must be removed from the multiplexed store", 
store.getRoot().hasChildNode("apps"));
+        assertFalse("Node must be removed from the owning (root) store", 
globalStore.getRoot().hasChildNode("apps"));
+    }
+
+
+    @Test
+    public void removeNodeInMountedStore() throws Exception {
+        NodeBuilder builder = store.getRoot().builder();
+
+        builder.getChildNode("tmp").child("newNode");
+
+        store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        assertTrue("Node must be added to multiplexed store", 
store.getRoot().getChildNode("tmp").hasChildNode("newNode"));
+
+        builder = store.getRoot().builder();
+
+        builder.getChildNode("tmp").getChildNode("newNode").remove();
+
+        store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        assertFalse("Node must be removed from the multiplexed store", 
store.getRoot().getChildNode("tmp").hasChildNode("newNode"));
+        assertFalse("Node must be removed from the owning (multiplexed) 
store", globalStore.getRoot().getChildNode("tmp").hasChildNode("newNode"));
+    }
+
+    @Test
+    public void builderChildrenCountInRootStore() throws Exception {
+        assertThat("root(childCount)", 
store.getRoot().builder().getChildNodeCount(100), equalTo(4l));
+    }
+
+    @Test
+    public void builderChildrenCountInMountedStore() {
+        assertThat("root.tmp(childCount)", 
store.getRoot().builder().getChildNode("tmp").getChildNodeCount(10), 
equalTo(2l));
+    }
+
+    @Test
+    public void builderChildNodeNamesInRootStore() throws Exception {
+        assertChildNodeNames(store.getRoot().builder(), "libs", "apps", "tmp", 
"readOnly");
+    }
+
+    @Test
+    public void builderChildNodeNamesInMountedStore() throws Exception {
+        assertChildNodeNames(store.getRoot().builder().getChildNode("tmp"), 
"child1", "child2");
+    }
+
+    @Test
+    public void builderStateIsUpdatedBeforeMergeinGlobalStore() throws 
Exception {
+        NodeBuilder builder = store.getRoot().builder();
+        builder.child("newChild");
+
+        assertTrue("Newly created node should be visible in the builder's node 
state", builder.hasChildNode("newChild"));
+    }
+
+    @Test
+    public void builderStateIsUpdatedBeforeMergeinMountedStore() throws 
Exception {
+        NodeBuilder builder = store.getRoot().getChildNode("tmp").builder();
+        builder.child("newChild");
+
+        assertTrue("Newly created node should be visible in the builder's node 
state", builder.hasChildNode("newChild"));
+    }
+
+
+    @Test
+    public void builderHasPropertyNameInRootStore() {
+        assertFalse("Node 'nope' does not exist", 
store.getRoot().builder().hasChildNode("nope"));
+        assertTrue("Node 'tmp' should exist (contributed by mount)", 
store.getRoot().builder().hasChildNode("tmp"));
+        assertTrue("Node 'libs' should exist (contributed by root)", 
store.getRoot().builder().hasChildNode("libs"));
+    }
+
+    @Test
+    public void builderHasPropertyNameInMountedStore() {
+        assertFalse("Node 'nope' does not exist", 
store.getRoot().builder().getChildNode("tmp").hasChildNode("nope"));
+        assertTrue("Node 'child1' should exist", 
store.getRoot().builder().getChildNode("tmp").hasChildNode("child1"));
+    }
+
+    @Test
+    public void setChildNodeInRootStore() throws Exception {
+        NodeBuilder builder = store.getRoot().builder();
+
+        builder.setChildNode("apps");
+
+        store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        assertTrue("Node apps must still exist", 
store.getRoot().hasChildNode("apps"));
+        assertThat("Node apps must not have any properties", 
store.getRoot().getChildNode("apps").getPropertyCount(), equalTo(0l));
+    }
+
+
+    @Test
+    public void setChildNodeInMountStore() throws Exception {
+        NodeBuilder builder = store.getRoot().builder();
+
+        builder.getChildNode("tmp").setChildNode("child1");
+
+        store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        assertTrue("Node child1 must still exist", 
store.getRoot().getChildNode("tmp").hasChildNode("child1"));
+        assertThat("Node child1 must not have any properties", 
store.getRoot().getChildNode("tmp").getChildNode("child1").getPropertyCount(), 
equalTo(0l));
+    }
+
+
+    @Test
+    public void builderBasedOnRootStoreChildNode() throws Exception {
+        NodeBuilder builder = store.getRoot().builder();
+        NodeBuilder appsBuilder = builder.getChildNode("apps");
+
+        appsBuilder.removeProperty("prop");
+        appsBuilder.setChildNode("child1");
+
+        store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        assertFalse("Node apps must have no properties (multiplexed store)", 
store.getRoot().getChildNode("apps").hasProperty("prop"));
+        assertFalse("Node apps must have no properties (root store)", 
globalStore.getRoot().getChildNode("apps").hasProperty("prop"));
+
+        assertTrue("Node /apps/child1 must exist (multiplexed store)", 
store.getRoot().getChildNode("apps").hasChildNode("child1"));
+        assertTrue("Node /apps/child1 must exist (root store)", 
globalStore.getRoot().getChildNode("apps").hasChildNode("child1"));
+    }
+
+    @Test
+    public void builderBasedOnMountStoreChildNode() throws Exception {
+        NodeBuilder builder = store.getRoot().builder();
+        NodeBuilder tmpBuilder = builder.getChildNode("tmp");
+
+        tmpBuilder.removeProperty("prop1");
+        tmpBuilder.setChildNode("child3");
+
+        store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        assertFalse("Node tmp must have no properties (multiplexed store)", 
store.getRoot().getChildNode("tmp").hasProperty("prop1"));
+        assertFalse("Node tmp must have no properties (mounted store)", 
mountedStore.getRoot().getChildNode("tmp").hasProperty("prop1"));
+
+        assertTrue("Node /tmp/build3 must exist (multiplexed store)", 
store.getRoot().getChildNode("tmp").hasChildNode("child3"));
+        assertTrue("Node /tmp/child3 must exist (mounted store)", 
mountedStore.getRoot().getChildNode("tmp").hasChildNode("child3"));
+
+    }
+
+    @Test
+    public void freshBuilderForGlobalStore() {
+        NodeBuilder builder = store.getRoot().builder();
+
+        assertFalse("builder.isNew", builder.isNew());
+        assertFalse("builder.isModified", builder.isModified());
+        assertFalse("builder.isReplaced", builder.isReplaced());
+    }
+
+    @Test
+    public void freshBuilderForMountedStore() {
+        NodeBuilder builder = store.getRoot().getChildNode("tmp").builder();
+
+        assertFalse("builder.isNew", builder.isNew());
+        assertFalse("builder.isModified", builder.isModified());
+        assertFalse("builder.isReplaced", builder.isReplaced());
+    }
+
+    @Test
+    public void newBuilderForGlobalStore() {
+        NodeBuilder builder = store.getRoot().builder();
+
+        builder = builder.child("newChild");
+
+        assertTrue("builder.isNew", builder.isNew());
+        assertFalse("builder.isModified", builder.isModified());
+        assertFalse("builder.isReplaced", builder.isReplaced());
+    }
+
+    @Test
+    public void newBuilderForMountedStore() {
+        NodeBuilder builder = store.getRoot().getChildNode("tmp").builder();
+
+        builder = builder.child("newChild");
+
+        assertTrue("builder.isNew", builder.isNew());
+        assertFalse("builder.isModified", builder.isModified());
+        assertFalse("builder.isReplaced", builder.isReplaced());
+    }
+
+    @Test
+    public void replacedBuilderForGlobalStore() {
+        NodeBuilder builder = store.getRoot().builder();
+
+        NodeBuilder libsBuilder = builder.setChildNode("libs");
+
+        assertTrue("libsBuilder.isReplaced", libsBuilder.isReplaced());
+        assertTrue("builder.getChild('libs').isReplaced", 
builder.getChildNode("libs").isReplaced());
+    }
+
+    @Test
+    public void replacedBuilderForMountedStore() {
+        NodeBuilder builder = store.getRoot().getChildNode("tmp").builder();
+
+        builder = builder.setChildNode("child1");
+
+        assertTrue("builder.isReplaced", builder.isReplaced());
+    }
+
+    @Test
+    public void readChildNodeBasedOnPathFragment() throws Exception {
+        NodeBuilder builder = globalStore.getRoot().builder();
+
+        builder.child("multi-holder");
+
+        globalStore.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        builder = mountedStore.getRoot().builder();
+
+        
builder.child("multi-holder").child("oak:mount-temp").setProperty("prop", 
"val");
+
+        mountedStore.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        NodeState holderNode = store.getRoot().getChildNode("multi-holder");
+        assertTrue("/multi-holder/oak:mount-temp should be visible from the 
multiplexed store",
+                holderNode.hasChildNode("oak:mount-temp"));
+
+        assertChildNodeNames(holderNode, "oak:mount-temp");
+
+        assertThat("/multi-holder/ must have 1 child entry", 
holderNode.getChildNodeCount(10), equalTo(1l));
+    }
+
+    @Test
+    public void moveNodeInSameStore() throws Exception {
+        NodeBuilder builder = store.getRoot().builder();
+
+        NodeBuilder src = builder.child("src");
+        NodeBuilder dst = builder.child("dst");
+
+        boolean result = src.moveTo(dst, "src");
+        assertTrue("move result should be success", result);
+
+        store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        assertFalse("/src must no longer exist", 
store.getRoot().hasChildNode("src"));
+        assertTrue("/dst/src must exist (multiplexed store)", 
store.getRoot().getChildNode("dst").hasChildNode("src"));
+    }
+
+    @Test
+    public void moveNodeBetweenStores() throws Exception {
+        NodeBuilder builder = store.getRoot().builder();
+
+        NodeBuilder src = builder.child("src");
+        NodeBuilder dst = builder.child("tmp");
+
+        boolean result = src.moveTo(dst, "src");
+        assertTrue("move result should be success", result);
+
+        store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        assertFalse("/src must no longer exist", 
store.getRoot().hasChildNode("src"));
+        assertTrue("/tmp/src must exist (multiplexed store)", 
store.getRoot().getChildNode("tmp").hasChildNode("src"));
+
+    }
+
+    @Test
+    public void resetOnGlobalStore() {
+        NodeBuilder builder = store.getRoot().builder();
+        builder.child("newChild");
+
+        store.reset(builder);
+
+        assertFalse("Newly added child should no longer be visible after 
reset", builder.hasChildNode("newChild"));
+    }
+
+    @Test
+    public void resetOnMountedStore() {
+        NodeBuilder rootBuilder = store.getRoot().builder();
+        NodeBuilder builder = rootBuilder.getChildNode("tmp");
+        builder.child("newChild");
+
+        store.reset(rootBuilder);
+
+        assertFalse("Newly added child should no longer be visible after 
reset", builder.getChildNode("tmp").hasChildNode("newChild"));
+    }
+
+    @Test
+    public void oldNodeStateDoesNotRefreshOnGlobalStore() throws Exception {
+        NodeState old = store.getRoot();
+
+        NodeBuilder builder = store.getRoot().builder();
+        builder.child("newNode");
+
+        assertFalse("old NodeState should not see newly added child node 
before merge ", old.hasChildNode("newNode"));
+
+        store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        assertFalse("old NodeState should not see newly added child node after 
merge ", old.hasChildNode("newNode"));
+    }
+
+    @Test
+    public void oldNodeStateDoesNotRefreshOnMountedStore() throws Exception {
+        NodeState old = store.getRoot();
+
+        NodeBuilder builder = store.getRoot().builder();
+
+        builder.getChildNode("tmp").child("newNode");
+
+        assertFalse("old NodeState should not see newly added child node 
before merge ", old.getChildNode("tmp").hasChildNode("newNode"));
+
+        store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        assertFalse("old NodeState should not see newly added child node after 
merge ", old.getChildNode("tmp").hasChildNode("newNode"));
+    }
+
+    // this test ensures that when going from State -> Builder -> State -> 
Builder the state is properly maintained
+    @Test
+    public void nestedBuilderFromState() throws Exception {
+        NodeState rootState = store.getRoot();
+        NodeBuilder rootBuilder = rootState.builder();
+        rootBuilder.child("newNode");
+
+        NodeState baseState = rootBuilder.getNodeState();
+        NodeBuilder builderFromState = baseState.builder();
+
+        assertTrue(builderFromState.hasChildNode("newNode"));
+    }
+
+    @Test
+    public void nestedBuilderWithNewPropertyFromState() throws Exception {
+        NodeState rootState = store.getRoot();
+        NodeBuilder rootBuilder = rootState.builder();
+        rootBuilder.setProperty("newProperty", true, Type.BOOLEAN);
+
+        NodeState baseState = rootBuilder.getNodeState();
+        assertTrue(baseState.getBoolean("newProperty"));
+
+        NodeBuilder builderFromState = baseState.builder();
+        assertTrue(builderFromState.getBoolean("newProperty"));
+        assertTrue(builderFromState.getNodeState().getBoolean("newProperty"));
+        
//assertTrue(builderFromState.getBaseState().getBoolean("newProperty")); // 
FIXME
+    }
+
+    @Test
+    public void readOnlyMountRejectsChanges() throws Exception {
+        NodeState oldState = store.getRoot();
+        try {
+            NodeBuilder builder = store.getRoot().builder();
+            builder.getChildNode("readOnly").child("newChild");
+            builder.getChildNode("libs").child("otherChild");
+            store.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+        } catch (CommitFailedException e) {
+            // expected
+            // validate that changes were not applied
+            CountingDiff countingDiff = new CountingDiff();
+            store.getRoot().compareAgainstBaseState(oldState, countingDiff);
+            assertThat("Unexpected number of changes", 
countingDiff.getNumChanges(), equalTo(0));
+        } catch ( Exception e ) {
+            throw e;
+        }
+    }
+
+    @Test
+    public void builderBasedOnCheckpoint() throws CommitFailedException {
+        String checkpoint = store.checkpoint(TimeUnit.DAYS.toMillis(1));
+
+        assertNotNull("checkpoint reference is null", checkpoint);
+
+        // create a new child /new in the root store
+        NodeBuilder globalBuilder = globalStore.getRoot().builder();
+        globalBuilder.child("new");
+        globalStore.merge(globalBuilder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        // create a new child /tmp/new in the mounted store
+        NodeBuilder mountedBuilder = mountedStore.getRoot().builder();
+        mountedBuilder.getChildNode("tmp").child("new");
+        mountedStore.merge(mountedBuilder, EmptyHook.INSTANCE, 
CommitInfo.EMPTY);
+
+        // create a new child /libs/mount/new in the deeply mounted store
+        NodeBuilder deepMountBuilder = deepMountedStore.getRoot().builder();
+        
deepMountBuilder.getChildNode("libs").getChildNode("mount").child("new");
+        deepMountedStore.merge(deepMountBuilder, EmptyHook.INSTANCE, 
CommitInfo.EMPTY);
+
+        NodeBuilder rootCheckpointBuilder = 
store.retrieve(checkpoint).builder();
+        assertFalse("store incorrectly exposes child at /new", 
rootCheckpointBuilder.hasChildNode("new"));
+        assertFalse("store incorrectly exposes child at /tmp/new", 
rootCheckpointBuilder.getChildNode("tmp").hasChildNode("new"));
+        assertFalse("store incorrectly exposes child at /libs/mount/new", 
rootCheckpointBuilder.getChildNode("libs").getChildNode("mount").hasChildNode("new"));
+    }
+
+    @Test
+    public void duplicatedChildren() throws CommitFailedException {
+        // create a new child /new in the root store
+        NodeBuilder globalBuilder = globalStore.getRoot().builder();
+        globalBuilder.child("new").setProperty("store", "global", Type.STRING);
+        globalStore.merge(globalBuilder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        // create a new child /tmp/new in the mounted store
+        NodeBuilder mountedBuilder = mountedStore.getRoot().builder();
+        mountedBuilder.child("new").setProperty("store", "mounted", 
Type.STRING);
+        mountedStore.merge(mountedBuilder, EmptyHook.INSTANCE, 
CommitInfo.EMPTY);
+
+        // create a new child /libs/mount/new in the deeply mounted store
+        NodeBuilder deepMountBuilder = deepMountedStore.getRoot().builder();
+        deepMountBuilder.child("new").setProperty("store", "deepMounted", 
Type.STRING);
+        deepMountedStore.merge(deepMountBuilder, EmptyHook.INSTANCE, 
CommitInfo.EMPTY);
+
+        List<ChildNodeEntry> children = 
newArrayList(filter(store.getRoot().getChildNodeEntries(), 
compose(Predicates.equalTo("new"), GET_NAME)));
+        assertEquals(1, children.size());
+        assertEquals("global", 
children.get(0).getNodeState().getString("store"));
+
+        NodeBuilder rootBuilder = store.getRoot().builder();
+        List<String> childNames = 
newArrayList(filter(rootBuilder.getChildNodeNames(), 
Predicates.equalTo("new")));
+        assertEquals(1, childNames.size());
+        assertEquals("global", 
rootBuilder.getChildNode("new").getString("store"));
+    }
+
+    private static enum NodeStoreKind {
+        MEMORY {
+            @Override
+            public NodeStoreRegistration create(String name) {
+                return new NodeStoreRegistration() {
+
+                    private MemoryNodeStore instance;
+
+                    @Override
+                    public NodeStore get() {
+
+                        if (instance != null) {
+                            throw new IllegalStateException("instance already 
created");
+                        }
+
+                        instance = new MemoryNodeStore();
+
+                        return instance;
+                    }
+
+                    @Override
+                    public void close() throws Exception {
+                        // does nothing
+
+                    }
+                };
+            }
+
+            public boolean supportsBlobCreation() {
+                return false;
+            }
+        }, SEGMENT {
+            @Override
+            public NodeStoreRegistration create(final String name) {
+                return new NodeStoreRegistration() {
+
+                    private SegmentNodeStore instance;
+                    private FileStore store;
+                    private File storePath;
+                    private String blobStorePath;
+
+                    @Override
+                    public NodeStore get() throws Exception {
+
+                        if (instance != null) {
+                            throw new IllegalStateException("instance already 
created");
+                        }
+
+                        // TODO - don't use Unix directory separators
+                        String directoryName = name != null ? "segment-" + 
name : "segment";
+                        storePath = new File("target/classes/" + 
directoryName);
+
+                        String blobStoreDirectoryName = name != null ? "blob-" 
+ name : "blob";
+                        blobStorePath = "target/classes/" + 
blobStoreDirectoryName;
+
+                        BlobStore blobStore = new FileBlobStore(blobStorePath);
+
+                        store = 
FileStore.builder(storePath).withBlobStore(blobStore).build();
+                        instance = SegmentNodeStore.builder(store).build();
+
+                        return instance;
+                    }
+
+                    @Override
+                    public void close() throws Exception {
+                        store.close();
+
+                        FileUtils.deleteQuietly(storePath);
+                        FileUtils.deleteQuietly(new File(blobStorePath));
+                    }
+                };
+            }
+        }, DOCUMENT_H2 {
+
+            // TODO - copied from DocumentRdbFixture
+
+            private DataSource ds;
+
+            @Override
+            public NodeStoreRegistration create(final String name) {
+
+                return new NodeStoreRegistration() {
+
+                    private DocumentNodeStore instance;
+
+                    @Override
+                    public NodeStore get() throws Exception {
+                        RDBOptions options = new 
RDBOptions().dropTablesOnClose(true);
+                        String jdbcUrl = 
"jdbc:h2:file:./target/classes/document";
+                        if ( name != null ) {
+                            jdbcUrl += "-" + name;
+                        }
+                        ds = RDBDataSourceFactory.forJdbcUrl(jdbcUrl, "sa", 
"");
+
+                        instance = new 
DocumentMK.Builder().setRDBConnection(ds, options).getNodeStore();
+
+                        return instance;
+
+                    }
+
+                    @Override
+                    public void close() throws Exception {
+                        instance.dispose();
+                        if ( ds instanceof Closeable ) {
+                            ((Closeable) ds).close();
+                        }
+                    }
+
+                };
+
+            }
+        };
+
+        public abstract NodeStoreRegistration create(@Nullable String name);
+
+        public boolean supportsBlobCreation() {
+            return true;
+        }
+    }
+
+    private interface NodeStoreRegistration {
+        NodeStore get() throws Exception;
+
+        void close() throws Exception;
+    }
+
+    private NodeStore register(NodeStoreRegistration reg) throws Exception {
+        registrations.add(reg);
+
+        return reg.get();
+    }
+
+    // ensure blobs don't get inlined by the SegmentBlobStore
+    private ByteArrayInputStream createLargeBlob() {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+        for ( int i = 0 ; i <= Segment.MEDIUM_LIMIT; i++) {
+            out.write('a');
+        }
+
+        return new ByteArrayInputStream(out.toByteArray());
+    }
+
+    private void assertChildNodeNames(NodeBuilder builder, String... names) {
+        Iterable<String> childNodeNames = builder.getChildNodeNames();
+
+        assertNotNull("childNodeNames must not be empty", childNodeNames);
+        assertThat("Incorrect number of elements", 
Iterables.size(childNodeNames), equalTo(names.length));
+        assertThat("Mismatched elements", childNodeNames, hasItems(names));
+    }
+
+    private void assertChildNodeNames(NodeState state, String... names) {
+        Iterable<String> childNodeNames = state.getChildNodeNames();
+
+        assertNotNull("childNodeNames must not be empty", childNodeNames);
+        assertThat("Incorrect number of elements", 
Iterables.size(childNodeNames), equalTo(names.length));
+        assertThat("Mismatched elements", childNodeNames, hasItems(names));
+    }
+}
\ No newline at end of file

Added: 
jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/plugins/multiplex/MultiplexingSegmentFixture.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/plugins/multiplex/MultiplexingSegmentFixture.java?rev=1767652&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/plugins/multiplex/MultiplexingSegmentFixture.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/plugins/multiplex/MultiplexingSegmentFixture.java
 Wed Nov  2 12:16:32 2016
@@ -0,0 +1,54 @@
+/*
+ * 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.plugins.multiplex;
+
+import java.io.IOException;
+
+import org.apache.jackrabbit.oak.fixture.NodeStoreFixture;
+import org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStore;
+import org.apache.jackrabbit.oak.plugins.segment.memory.MemoryStore;
+import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+
+public class MultiplexingSegmentFixture extends NodeStoreFixture {
+
+    private static final String MOUNT_PATH = "/tmp";
+
+    @Override
+    public NodeStore createNodeStore() {
+        try {
+            MountInfoProvider mip = new SimpleMountInfoProvider.Builder()
+                    .readOnlyMount("temp", MOUNT_PATH)
+                    .build();
+
+            NodeStore globalStore = SegmentNodeStore.builder(new 
MemoryStore()).build();
+            NodeStore tempMount = SegmentNodeStore.builder(new 
MemoryStore()).build();
+
+            return new MultiplexingNodeStore.Builder(mip, 
globalStore).addMount("temp", tempMount).build();
+        } catch (IOException e) {
+            throw new RuntimeException();
+        }
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + " with a mount under " + 
MOUNT_PATH;
+    }
+
+}
\ No newline at end of file

Modified: 
jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/spi/state/NodeStoreTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/spi/state/NodeStoreTest.java?rev=1767652&r1=1767651&r2=1767652&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/spi/state/NodeStoreTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-it/src/test/java/org/apache/jackrabbit/oak/spi/state/NodeStoreTest.java
 Wed Nov  2 12:16:32 2016
@@ -457,7 +457,8 @@ public class NodeStoreTest extends OakBa
     public void moveToDescendant() {
         NodeBuilder test = store.getRoot().builder().getChildNode("test");
         NodeBuilder x = test.getChildNode("x");
-        if (fixture == NodeStoreFixtures.SEGMENT_TAR || fixture == 
NodeStoreFixtures.SEGMENT_MK || fixture == NodeStoreFixtures.MEMORY_NS) {
+        if (fixture == NodeStoreFixtures.SEGMENT_TAR || fixture == 
NodeStoreFixtures.SEGMENT_MK || fixture == NodeStoreFixtures.MEMORY_NS 
+                || fixture == NodeStoreFixtures.MULTIPLEXED_SEGMENT || fixture 
== NodeStoreFixtures.MULTIPLEXED_MEM) {
             assertTrue(x.moveTo(x, "xx"));
             assertFalse(x.exists());
             assertFalse(test.hasChildNode("x"));


Reply via email to