/*
* 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.jcr.random;
import static org.apache.jackrabbit.oak.jcr.AbstractRepositoryTest.dispose;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.Closeable;
import java.io.IOException;
import java.util.Random;
import javax.jcr.Node;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
import org.apache.jackrabbit.oak.jcr.Jcr;
import org.apache.jackrabbit.oak.jcr.NodeStoreFixture;
import org.apache.jackrabbit.oak.plugins.document.DocumentMK;
import org.apache.jackrabbit.oak.plugins.document.util.MongoConnection;
import org.apache.jackrabbit.oak.spi.state.NodeStore;
import com.mongodb.DB;
/**
* A randomized test that writes to two repositories (using different storage
* backends), and compares the results. The test uses low cache sizes, and low
* thresholds / limits so that as much of the code as possible is used (high
* code coverage).
*/
public class RandomOpCompare {
private static final int SESSION_COUNT = 3;
protected NodeStoreFixture f1;
protected NodeStoreFixture f2;
protected NodeStore ns1, ns2;
protected Repository r1, r2;
protected Session session1, session2;
protected Session[] sessionList1 = new Session[SESSION_COUNT];
protected Session[] sessionList2 = new Session[SESSION_COUNT];
private Random random;
public static void main(String... args) throws Exception {
RandomOpCompare app = new RandomOpCompare();
app.login();
app.test();
app.logout();
}
static {
// TODO changes to system properties are not picked up if other tests
// are run first
// code coverage oak.plugins.document: 48.1%
// code coverage 46.3% (8829) - with
// DocumentRootBuilder
System.setProperty("update.limit", "2");
// System.setProperty("oak.documentMK.childrenCacheLimit", "1024");
// code coverage 39.8% (7583) - without
}
// @Before
public void login() throws RepositoryException {
f1 = NodeStoreFixture.SEGMENT_MK;
f2 = getMongo();
ns1 = f1.createNodeStore();
r1 = new Jcr(ns1).createRepository();
session1 = r1.login(new SimpleCredentials("admin", "admin".toCharArray()));
for (int i = 0; i < SESSION_COUNT; i++) {
sessionList1[i] = r1.login(new SimpleCredentials("admin", "admin"
.toCharArray()));
}
ns2 = f2.createNodeStore();
r2 = new Jcr(ns2).createRepository();
session2 = r2.login(new SimpleCredentials("admin", "admin".toCharArray()));
for (int i = 0; i < SESSION_COUNT; i++) {
sessionList2[i] = r2.login(new SimpleCredentials("admin", "admin"
.toCharArray()));
}
}
// @After
public void logout() {
if (session1 != null) {
session1.logout();
session1 = null;
}
for (Session s : sessionList1) {
s.logout();
}
if (session2 != null) {
session2.logout();
session2 = null;
}
for (Session s : sessionList2) {
s.logout();
}
r1 = dispose(r1);
r2 = dispose(r2);
if (ns1 != null) {
f1.dispose(ns1);
}
if (ns2 != null) {
f2.dispose(ns2);
}
}
private static NodeStoreFixture getMongo() {
return new NodeStoreFixture() {
@Override
public NodeStore createNodeStore() {
MongoConnection connection;
try {
connection = new MongoConnection("mongodb://localhost:27017/oak");
} catch (Exception e) {
throw new RuntimeException(e);
}
DB mongoDB = connection.getDB();
return new DocumentMK.Builder().
memoryCacheSize(0).
setMongoDB(mongoDB, 1, 16).
getNodeStore();
}
@Override
public NodeStore createNodeStore(int clusterNodeId) {
return null;
}
@Override
public void dispose(NodeStore nodeStore) {
if (nodeStore instanceof Closeable) {
try {
((Closeable) nodeStore).close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
};
}
// @Test
public void test() throws RepositoryException {
random = new Random(1);
String longName = longNodeName(0);
Node root1 = session1.getRootNode().addNode("testNodeRoot").
addNode(longName).addNode(longName).addNode(longName);
session1.save();
Node root2 = session2.getRootNode().addNode("testNodeRoot").
addNode(longName).addNode(longName).addNode(longName);
session2.save();
System.out.println("len: " + root1.getPath().length());
long start = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
long now = System.currentTimeMillis();
if (now - start > 1000) {
System.out.println("i: " + i);
start = now;
}
String nodeName = randomNodeName();
String propertyName = "p" + random.nextInt(2);
String value = "x" + random.nextInt(10);
int sessionId = random.nextInt(SESSION_COUNT);
Session s1 = sessionList1[sessionId];
Session s2 = sessionList1[sessionId];
switch (random.nextInt(3)) {
case 0:
if (root1.hasNode(nodeName)) {
assertTrue(root2.hasNode(nodeName));
root1.getNode(nodeName).remove();
root2.getNode(nodeName).remove();
} else {
assertFalse(root2.hasNode(nodeName));
root1.addNode(nodeName);
root2.addNode(nodeName);
}
break;
case 1:
if (root1.hasNode(nodeName)) {
assertTrue(root2.hasNode(nodeName));
Node n1 = root1.getNode(nodeName);
Node n2 = root1.getNode(nodeName);
if (n1.hasProperty(propertyName)) {
assertTrue(n2.hasProperty(propertyName));
assertEquals(
n1.getProperty(propertyName).
getValue().getString(),
n2.getProperty(propertyName).
getValue().getString());
} else {
assertFalse(n2.hasProperty(propertyName));
}
n1.setProperty(propertyName, value);
n2.setProperty(propertyName, value);
}
break;
case 2:
s1.save();
s2.save();
break;
}
}
}
private String randomNodeName() {
if (random.nextInt(50) == 0) {
return longNodeName(random.nextInt(5));
}
return "n" + random.nextInt(50);
}
private static String longNodeName(int x) {
StringBuilder buff = new StringBuilder("n");
for (int i = 0; i < 140; i++) {
buff.append('x');
}
buff.append(x);
return buff.toString();
}
}