blob: 5015564ac49c77e1645137482d59efd4e330ac6b [file] [log] [blame]
/*
* 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.document;
import java.util.HashSet;
import java.util.Set;
import com.google.common.collect.Sets;
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.plugins.document.Branch.BranchCommit;
import org.apache.jackrabbit.oak.plugins.document.memory.MemoryDocumentStore;
import org.apache.jackrabbit.oak.plugins.document.util.Utils;
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.NodeStateDiff;
import org.junit.Rule;
import org.junit.Test;
import static org.apache.jackrabbit.oak.plugins.document.Collection.NODES;
import static org.apache.jackrabbit.oak.plugins.document.TestUtils.asDocumentState;
import static org.apache.jackrabbit.oak.plugins.document.TestUtils.merge;
import static org.apache.jackrabbit.oak.plugins.document.TestUtils.persistToBranch;
import static org.apache.jackrabbit.oak.plugins.document.util.Utils.getRootDocument;
import static org.hamcrest.Matchers.contains;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class BranchTest {
@Rule
public DocumentMKBuilderProvider builderProvider = new DocumentMKBuilderProvider();
@Test
public void getModifiedPathsUntil() {
UnmergedBranches branches = new UnmergedBranches();
RevisionVector base = new RevisionVector(Revision.newRevision(1));
Revision c1 = Revision.newRevision(1).asBranchRevision();
Branch b = branches.create(base, c1, null);
BranchCommit bc1 = b.getCommit(c1);
bc1.track(Path.fromString("/foo"));
Revision c2 = Revision.newRevision(1).asBranchRevision();
b.addCommit(c2);
BranchCommit bc2 = b.getCommit(c2);
bc2.track(Path.fromString("/bar"));
Revision c3 = Revision.newRevision(1).asBranchRevision();
b.rebase(c3, new RevisionVector(Revision.newRevision(1)));
Revision c4 = Revision.newRevision(1).asBranchRevision();
b.addCommit(c4);
BranchCommit bc4 = b.getCommit(c4);
bc4.track(Path.fromString("/baz"));
Revision c5 = Revision.newRevision(1).asBranchRevision();
try {
b.getModifiedPathsUntil(Revision.newRevision(1));
fail("Must fail with IllegalArgumentException");
} catch (IllegalArgumentException e) {
// expected
}
assertModifiedPaths(b.getModifiedPathsUntil(c1), "/foo");
assertModifiedPaths(b.getModifiedPathsUntil(c2), "/foo", "/bar");
assertModifiedPaths(b.getModifiedPathsUntil(c3), "/foo", "/bar");
assertModifiedPaths(b.getModifiedPathsUntil(c4), "/foo", "/bar", "/baz");
assertModifiedPaths(b.getModifiedPathsUntil(c5));
}
@Test
public void rootBranchCommitChildTest() throws Exception {
MemoryDocumentStore store = new MemoryDocumentStore();
DocumentNodeStore ns = builderProvider.newBuilder()
.setClusterId(1)
.setDocumentStore(store).build();
NodeBuilder builder = ns.getRoot().builder();
builder.child("a");
persistToBranch(builder);
merge(ns, builder);
Sweep2TestHelper.testPre18UpgradeSimulations(ns, builderProvider);
}
@Test
public void childBranchCommitChildTest() throws Exception {
MemoryDocumentStore store = new MemoryDocumentStore();
DocumentNodeStore ns = builderProvider.newBuilder()
.setClusterId(1)
.setDocumentStore(store).build();
NodeBuilder builder = ns.getRoot().builder();
builder.child("a");
merge(ns, builder);
builder = ns.getRoot().builder();
builder.child("a").child("b");
persistToBranch(builder);
merge(ns, builder);
Sweep2TestHelper.testPre18UpgradeSimulations(ns, builderProvider);
}
@Test
public void manyBranchCommitsDepthTest() throws Exception {
MemoryDocumentStore store = new MemoryDocumentStore();
DocumentNodeStore ns = builderProvider.newBuilder()
.setClusterId(1)
.setDocumentStore(store).build();
NodeBuilder builder = ns.getRoot().builder();
builder.child("a");
persistToBranch(builder);
builder.getChildNode("a").child("b");
persistToBranch(builder);
builder.getChildNode("a").getChildNode("b").child("c");
persistToBranch(builder);
builder.getChildNode("a").getChildNode("b").getChildNode("c").child("d");
persistToBranch(builder);
builder.getChildNode("a").getChildNode("b").getChildNode("c").getChildNode("d").child("e");
persistToBranch(builder);
merge(ns, builder);
Sweep2TestHelper.testPre18UpgradeSimulations(ns, builderProvider);
}
@Test
public void manyBranchCommitsWidthTest() throws Exception {
MemoryDocumentStore store = new MemoryDocumentStore();
DocumentNodeStore ns = builderProvider.newBuilder()
.setClusterId(1)
.setDocumentStore(store).build();
NodeBuilder builder = ns.getRoot().builder();
builder.child("a");
persistToBranch(builder);
builder.child("b");
persistToBranch(builder);
builder.child("c");
persistToBranch(builder);
builder.child("d");
persistToBranch(builder);
builder.child("e");
persistToBranch(builder);
merge(ns, builder);
Sweep2TestHelper.testPre18UpgradeSimulations(ns, builderProvider);
}
@Test
public void mixedPre18BranchTest() throws Exception {
MemoryDocumentStore store = new MemoryDocumentStore();
DocumentNodeStore ns = builderProvider.newBuilder()
.setClusterId(1)
.setDocumentStore(store).build();
// step 1 : create an initial structure /a/b/c
NodeBuilder builder = ns.getRoot().builder();
builder.child("a").child("b").child("c").setProperty("commit", "1");
merge(ns, builder);
// step 2 : create a branch commit incl. with a child under an existing node, ie /a/b/c/d..
builder = ns.getRoot().builder();
builder.child("a").setProperty("commit", "2");
builder.child("a").child("b").child("c").child("d").child("e").setProperty("commit", "2");
persistToBranch(builder);
merge(ns, builder);
// step 3 : verify that "_bc" are set correctly : on all except /a/b (where nothing was changed)
assertNotNull(store.find(NODES, Utils.getIdFromPath("/")).get("_bc"));
assertNotNull(store.find(NODES, Utils.getIdFromPath("/a")).get("_bc"));
assertNull(store.find(NODES, Utils.getIdFromPath("/a/b")).get("_bc"));
assertNotNull(store.find(NODES, Utils.getIdFromPath("/a/b/c")).get("_bc"));
assertNotNull(store.find(NODES, Utils.getIdFromPath("/a/b/c/d")).get("_bc"));
assertNotNull(store.find(NODES, Utils.getIdFromPath("/a/b/c/d/e")).get("_bc"));
// step 4 : verify the "_bc" are set correctly by backgroundSweep()/forceBackgroundSweep()
// step 4b: /a/b/c did not get a "_bc" because it only contained "_commitRoot" but no other changes, and
// https://github.com/apache/jackrabbit-oak/blob/d35346d4d446908c7019e931cb54d88824c1a637/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocumentSweeper.java#L179
// only went through document that had changes in properties in PROPERTY_OR_DELETED
// step 4c: / also failed with a similar reason as the above, except the root
// only has changes in "_revisions" (not "_commitRoot")
Sweep2TestHelper.testPre18UpgradeSimulations(ns, builderProvider);
}
@Test
public void orphanedBranchTest() {
MemoryDocumentStore store = new MemoryDocumentStore();
DocumentNodeStore ns = builderProvider.newBuilder()
.setDocumentStore(store).build();
NodeBuilder builder = ns.getRoot().builder();
builder.setProperty("p", "v");
persistToBranch(builder);
ns.dispose();
// start again
ns = builderProvider.newBuilder()
.setDocumentStore(store).build();
assertFalse(ns.getRoot().hasProperty("p"));
Sweep2TestHelper.testPre18UpgradeSimulations(ns, builderProvider);
}
@Test
public void compareBranchStates() {
DocumentNodeStore ns = builderProvider.newBuilder()
.setAsyncDelay(0).build();
ns.runBackgroundOperations();
DocumentStore store = ns.getDocumentStore();
NodeBuilder builder = ns.getRoot().builder();
builder.setProperty("p", "a");
persistToBranch(builder);
NodeState s1 = builder.getNodeState();
builder.setProperty("p", "b");
persistToBranch(builder);
NodeState s2 = builder.getNodeState();
Set<String> changes = new HashSet<>();
NodeStateDiff diff = new DefaultNodeStateDiff() {
@Override
public boolean propertyChanged(PropertyState before,
PropertyState after) {
changes.add(before.getName());
return super.propertyChanged(before, after);
}
};
s2.compareAgainstBaseState(s1, diff);
assertThat(changes, contains("p"));
NodeDocument root = getRootDocument(store);
RevisionVector br = asDocumentState(s1).getRootRevision();
assertTrue(br.isBranch());
DocumentNodeState state = root.getNodeAtRevision(ns, br, null);
assertNotNull(state);
assertEquals("a", state.getString("p"));
Sweep2TestHelper.testPre18UpgradeSimulations(ns, builderProvider);
}
private void assertModifiedPaths(Iterable<Path> actual, String... expected) {
Set<Path> expectedSet = Sets.newHashSet();
for (String p : expected) {
expectedSet.add(Path.fromString(p));
}
assertEquals(expectedSet, Sets.newHashSet(actual));
}
}