You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by md...@apache.org on 2012/04/05 12:30:02 UTC

svn commit: r1309740 - /jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/kernel/KernelNodeStateEditorFuzzTest.java

Author: mduerig
Date: Thu Apr  5 10:30:02 2012
New Revision: 1309740

URL: http://svn.apache.org/viewvc?rev=1309740&view=rev
Log:
OAK-9: Internal tree builder
More thorough tests for KernelNodeStateEditor

Added:
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/kernel/KernelNodeStateEditorFuzzTest.java

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/kernel/KernelNodeStateEditorFuzzTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/kernel/KernelNodeStateEditorFuzzTest.java?rev=1309740&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/kernel/KernelNodeStateEditorFuzzTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/kernel/KernelNodeStateEditorFuzzTest.java Thu Apr  5 10:30:02 2012
@@ -0,0 +1,448 @@
+/*
+ * 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.kernel;
+
+import org.apache.jackrabbit.mk.api.MicroKernel;
+import org.apache.jackrabbit.mk.model.ChildNodeEntry;
+import org.apache.jackrabbit.mk.model.PropertyState;
+import org.apache.jackrabbit.mk.model.Scalar;
+import org.apache.jackrabbit.mk.model.ScalarImpl;
+import org.apache.jackrabbit.mk.simple.SimpleKernelImpl;
+import org.apache.jackrabbit.mk.util.PathUtils;
+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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Random;
+
+import static org.apache.jackrabbit.oak.kernel.KernelNodeStateEditorFuzzTest.Operation.AddNode;
+import static org.apache.jackrabbit.oak.kernel.KernelNodeStateEditorFuzzTest.Operation.MoveNode;
+import static org.apache.jackrabbit.oak.kernel.KernelNodeStateEditorFuzzTest.Operation.CopyNode;
+import static org.apache.jackrabbit.oak.kernel.KernelNodeStateEditorFuzzTest.Operation.RemoveNode;
+import static org.apache.jackrabbit.oak.kernel.KernelNodeStateEditorFuzzTest.Operation.RemoveProperty;
+import static org.apache.jackrabbit.oak.kernel.KernelNodeStateEditorFuzzTest.Operation.Save;
+import static org.apache.jackrabbit.oak.kernel.KernelNodeStateEditorFuzzTest.Operation.SetProperty;
+import static org.junit.Assert.assertEquals;
+
+@RunWith(Parameterized.class)
+public class KernelNodeStateEditorFuzzTest {
+    static final Logger log = LoggerFactory.getLogger(KernelNodeStateEditorFuzzTest.class);
+
+    private static final int OP_COUNT = 5000;
+
+    private final Random random;
+
+    private MicroKernel mk1;
+    private MicroKernel mk2;
+
+    @Parameters
+    public static List<Object[]> seeds() {
+        return Arrays.asList(new Object[][] {
+                {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9},
+                {10}, {11}, {12}, {13}, {14}, {15}, {16}, {17}, {18}, {19},
+                {20}, {21}, {22}, {23}, {24}, {25}, {26}, {27}, {28}, {29},
+                {30}, {31}, {32}, {33}, {34}, {35}, {36}, {37}, {38}, {39},
+        });
+    }
+
+    public KernelNodeStateEditorFuzzTest(int seed) {
+        log.info("Seed = {}", seed);
+        random = new Random(seed);
+    }
+
+    @Before
+    public void setup() {
+        mk1 = new SimpleKernelImpl("mem:mk1");
+        mk1.commit("", "+\"/root\":{}", mk1.getHeadRevision(), "");
+
+        mk2 = new SimpleKernelImpl("mem:mk2");
+        mk2.commit("", "+\"/root\":{}", mk2.getHeadRevision(), "");
+    }
+
+    @After
+    public void tearDown() {
+        mk1.dispose();
+        mk2.dispose();
+    }
+
+    @Test
+    public void fuzzTest() throws Exception {
+        KernelNodeState state1 = new KernelNodeState(mk1, "/", mk1.getHeadRevision());
+        KernelNodeStateEditor editor1 = new KernelNodeStateEditor(state1);
+
+        KernelNodeState state2 = new KernelNodeState(mk1, "/", mk2.getHeadRevision());
+        KernelNodeStateEditor editor2 = new KernelNodeStateEditor(state2);
+
+        for (Operation op : operations(OP_COUNT)) {
+            log.info("{}", op);
+            op.apply(editor1);
+            op.apply(editor2);
+            checkEqual(editor1.getTransientState(), editor2.getTransientState());
+
+            state1 = editor1.mergeInto(mk1, state1);
+            editor1 = new KernelNodeStateEditor(state1);
+            if (op instanceof Save) {
+                state2 = editor2.mergeInto(mk2, state2);
+                editor2 = new KernelNodeStateEditor(state1);
+                assertEquals(state1, state2);
+            }
+        }
+    }
+
+    private Iterable<Operation> operations(final int count) {
+        return new Iterable<Operation>() {
+            int k = count;
+
+            @Override
+            public Iterator<Operation> iterator() {
+                return new Iterator<Operation>() {
+                    @Override
+                    public boolean hasNext() {
+                        return k-- > 0;
+                    }
+
+                    @Override
+                    public Operation next() {
+                        return createOperation();
+                    }
+
+                    @Override
+                    public void remove() {
+                        throw new UnsupportedOperationException();
+                    }
+                };
+            }
+        };
+    }
+
+    abstract static class Operation {
+        abstract void apply(KernelNodeStateEditor editor);
+
+        static class AddNode extends Operation {
+            private final String parentPath;
+            private final String name;
+
+            AddNode(String parentPath, String name) {
+                this.parentPath = parentPath;
+                this.name = name;
+            }
+
+            @Override
+            void apply(KernelNodeStateEditor editor) {
+                for (String element : PathUtils.elements(parentPath)) {
+                    editor = editor.edit(element);
+                }
+                editor.addNode(name);
+            }
+
+            @Override
+            public String toString() {
+                return '+' + PathUtils.concat(parentPath, name) + ":{}";
+            }
+        }
+
+        static class RemoveNode extends Operation {
+            private final String path;
+
+            RemoveNode(String path) {
+                this.path = path;
+            }
+
+            @Override
+            void apply(KernelNodeStateEditor editor) {
+                for (String element : PathUtils.elements(PathUtils.getParentPath(path))) {
+                    editor = editor.edit(element);
+                }
+                editor.removeNode(PathUtils.getName(path));
+            }
+
+            @Override
+            public String toString() {
+                return '-' + path;
+            }
+        }
+
+        static class MoveNode extends Operation {
+            private final String source;
+            private final String destination;
+
+            MoveNode(String source, String destParent, String destName) {
+                this.source = source;
+                destination = PathUtils.concat(destParent, destName);
+            }
+
+            @Override
+            void apply(KernelNodeStateEditor editor) {
+                editor.move(source.substring(1), destination.substring(1));
+            }
+
+            @Override
+            public String toString() {
+                return '>' + source + ':' + destination;
+            }
+        }
+
+        static class CopyNode extends Operation {
+            private final String source;
+            private final String destination;
+
+            CopyNode(String source, String destParent, String destName) {
+                this.source = source;
+                destination = PathUtils.concat(destParent, destName);
+            }
+
+            @Override
+            void apply(KernelNodeStateEditor editor) {
+                editor.copy(source.substring(1), destination.substring(1));
+            }
+
+            @Override
+            public String toString() {
+                return '*' + source + ':' + destination;
+            }
+        }
+
+        static class SetProperty extends Operation {
+            private final String parentPath;
+            private KernelPropertyState propertyState;
+
+            SetProperty(String parentPath, String name, Scalar value) {
+                this.parentPath = parentPath;
+                this.propertyState = new KernelPropertyState(name, value);
+            }
+
+            @Override
+            void apply(KernelNodeStateEditor editor) {
+                for (String element : PathUtils.elements(parentPath)) {
+                    editor = editor.edit(element);
+                }
+                editor.setProperty(propertyState);
+            }
+
+            @Override
+            public String toString() {
+                return '^' + PathUtils.concat(parentPath, propertyState.getName()) + ':'
+                        + propertyState.getEncodedValue();
+            }
+        }
+
+        static class RemoveProperty extends Operation {
+            private final String parentPath;
+            private final String name;
+
+            RemoveProperty(String parentPath, String name) {
+                this.parentPath = parentPath;
+                this.name = name;
+            }
+
+            @Override
+            void apply(KernelNodeStateEditor editor) {
+                for (String element : PathUtils.elements(parentPath)) {
+                    editor = editor.edit(element);
+                }
+                editor.removeProperty(name);
+            }
+
+            @Override
+            public String toString() {
+                return '^' + PathUtils.concat(parentPath, name) + ":null";
+            }
+        }
+
+        static class Save extends Operation {
+            @Override
+            void apply(KernelNodeStateEditor editor) {
+                // empty
+            }
+
+            @Override
+            public String toString() {
+                return "save";
+            }
+        }
+    }
+
+    private Operation createOperation() {
+        Operation op;
+        do {
+            switch (random.nextInt(10)) {
+                case 0:
+                case 1:
+                case 2:
+                    op = createAddNode();
+                    break;
+                case 3:
+                    op = createRemoveNode();
+                    break;
+                case 4:
+                    op = createMoveNode();
+                    break;
+                case 5:
+                    // todo: copy is currently broken due to OAK-47
+                    // op = createCopyNode();
+                    op = null;
+                    break;
+                case 6:
+                    op = createAddProperty();
+                    break;
+                case 7:
+                    op = createSetProperty();
+                    break;
+                case 8:
+                    op = createRemoveProperty();
+                    break;
+                case 9:
+                    op = new Save();
+                    break;
+                default:
+                    throw new IllegalStateException();
+            }
+        } while (op == null);
+        return op;
+    }
+
+    private Operation createAddNode() {
+        String parentPath = chooseNodePath();
+        String name = createNodeName();
+        return new AddNode(parentPath, name);
+    }
+
+    private Operation createRemoveNode() {
+        String path = chooseNodePath();
+        return "/root".equals(path) ? null : new RemoveNode(path);
+    }
+
+    private Operation createMoveNode() {
+        String source = chooseNodePath();
+        String destParent = chooseNodePath();
+        String destName = createNodeName();
+        return "/root".equals(source) || destParent.startsWith(source)
+                ? null
+                : new MoveNode(source, destParent, destName);
+    }
+
+    private Operation createCopyNode() {
+        String source = chooseNodePath();
+        String destParent = chooseNodePath();
+        String destName = createNodeName();
+        return "/root".equals(source)
+                ? null
+                : new CopyNode(source, destParent, destName);
+    }
+
+    private Operation createAddProperty() {
+        String parent = chooseNodePath();
+        String name = createPropertyName();
+        Scalar value = createValue();
+        return new SetProperty(parent, name, value);
+    }
+
+    private Operation createSetProperty() {
+        String path = choosePropertyPath();
+        if (path == null) {
+            return null;
+        }
+        Scalar value = createValue();
+        return new SetProperty(PathUtils.getParentPath(path), PathUtils.getName(path), value);
+    }
+
+    private Operation createRemoveProperty() {
+        String path = choosePropertyPath();
+        if (path == null) {
+            return null;
+        }
+        return new RemoveProperty(PathUtils.getParentPath(path), PathUtils.getName(path));
+    }
+
+    private int counter;
+
+    private String createNodeName() {
+        return "N" + counter++;
+    }
+
+    private String createPropertyName() {
+        return "P" + counter++;
+    }
+
+    private String chooseNodePath() {
+        String path = "/root";
+
+        String next;
+        while ((next = chooseNode(path)) != null) {
+            path = next;
+        }
+
+        return path;
+    }
+
+    private String choosePropertyPath() {
+        return chooseProperty(chooseNodePath());
+    }
+
+    private String chooseNode(String parentPath) {
+        KernelNodeState state = new KernelNodeState(mk1, parentPath, mk1.getHeadRevision());
+        int k = random.nextInt((int) (state.getChildNodeCount() + 1));
+        int c = 0;
+        for (ChildNodeEntry entry : state.getChildNodeEntries(0, -1)) {
+            if (c++ == k) {
+                return PathUtils.concat(parentPath, entry.getName());
+            }
+        }
+        return null;
+    }
+
+    private String chooseProperty(String parentPath) {
+        KernelNodeState state = new KernelNodeState(mk1, parentPath, mk1.getHeadRevision());
+        int k = random.nextInt((int) (state.getPropertyCount() + 1));
+        int c = 0;
+        for (PropertyState entry : state.getProperties()) {
+            if (c++ == k) {
+                return PathUtils.concat(parentPath, entry.getName());
+            }
+        }
+        return null;
+    }
+
+    private Scalar createValue() {
+        return ScalarImpl.stringScalar("V" + counter++);
+    }
+
+    private static void checkEqual(TransientNodeState state1, TransientNodeState state2) {
+        assertEquals(state1.getPath(), state2.getPath());
+        assertEquals(state1.getChildNodeCount(), state2.getChildNodeCount());
+        assertEquals(state1.getPropertyCount(), state2.getPropertyCount());
+
+        for (PropertyState property1 : state1.getProperties()) {
+            assertEquals(property1, state2.getProperty(property1.getName()));
+        }
+
+        for (TransientNodeState node1 : state1.getChildNodes(0, -1)) {
+            checkEqual(node1, state2.getChildNode(node1.getName()));
+        }
+    }
+
+}



Re: svn commit: r1309740 - /jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/kernel/KernelNodeStateEditorFuzzTest.java

Posted by Michael Dürig <md...@apache.org>.

On 5.4.12 12:22, Michael Dürig wrote:
>
> On 5.4.12 11:56, Jukka Zitting wrote:
>> Hi,
>>
>> On Thu, Apr 5, 2012 at 12:30 PM,<md...@apache.org> wrote:
>>> More thorough tests for KernelNodeStateEditor
>>
>> Can we put this (and other long-running tests) into a separate
>> integration test component? The fuzz test doubled oak-core build time
>> for me.
>
> Yes that's was my thinking too. However, since this is clearly a unit
> test, it should not go into oak-it. I'd rather create a Maven profile
> "smoke-test" which excludes the long running tests. WDYT?
>

See https://issues.apache.org/jira/browse/OAK-52. To exclude long 
running tests do

mvn clean install -Psmoke-test

Michael

Re: svn commit: r1309740 - /jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/kernel/KernelNodeStateEditorFuzzTest.java

Posted by Michael Dürig <md...@apache.org>.

On 5.4.12 11:56, Jukka Zitting wrote:
> Hi,
>
> On Thu, Apr 5, 2012 at 12:30 PM,<md...@apache.org>  wrote:
>> More thorough tests for KernelNodeStateEditor
>
> Can we put this (and other long-running tests) into a separate
> integration test component? The fuzz test doubled oak-core build time
> for me.

Yes that's was my thinking too. However, since this is clearly a unit 
test, it should not go into oak-it. I'd rather create a Maven profile 
"smoke-test" which excludes the long running tests. WDYT?

Michael

>
> BR,
>
> Jukka Zitting

Re: svn commit: r1309740 - /jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/kernel/KernelNodeStateEditorFuzzTest.java

Posted by Jukka Zitting <ju...@gmail.com>.
Hi,

On Thu, Apr 5, 2012 at 12:30 PM,  <md...@apache.org> wrote:
> More thorough tests for KernelNodeStateEditor

Can we put this (and other long-running tests) into a separate
integration test component? The fuzz test doubled oak-core build time
for me.

BR,

Jukka Zitting