Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   /*
    * 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.mk.test;
  
  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.assertTrue;
  import static org.junit.Assert.fail;
  
  import java.util.HashSet;
  import java.util.List;
  import java.util.Random;
  import java.util.Set;
  
  import org.junit.Test;
Integration tests for verifying that a MicroKernel implementation obeys the contract of the MicroKernel API.
  
  public class MicroKernelIT extends AbstractMicroKernelIT {
  
      public MicroKernelIT(MicroKernelFixture fixture) {
          super(fixture, 1);
      }
  
      @Override
      protected void addInitialTestContent() {
          JSONObject obj = parseJSONObject(.getNodes("/"null, 0, 0, -1, null));
          Set<Stringnames = getNodeNames(obj);
          StringBuilder sb = new StringBuilder();
          for (String nm : names) {
              sb.append("-\"");
              sb.append(nm);
              sb.append("\"\n");
          }
          .commit("/"sb.toString(), null"clean test content");
  
          .commit("/""+\"test\" : {" +
                  "\"stringProp\":\"stringVal\"," +
                  "\"intProp\":42," +
                  "\"floatProp\":42.2," +
                  "\"booleanProp\": true," +
                  "\"multiIntProp\":[1,2,3]}"null"");
      }
  
      @Test
      public void revisionOps() {
          String head = .getHeadRevision();
          assertNotNull(head);
  
          try {
              Thread.sleep(100);
          } catch (InterruptedException ignore) {
          }
  
          long now = System.currentTimeMillis();
  
          // get history since 'now'
          JSONArray array = parseJSONArray(.getRevisionHistory(now, -1, null));
          // history should be empty since there was no commit since 'now'
          assertEquals(0, array.size());
  
          // get oldest available revision
          array = parseJSONArray(.getRevisionHistory(0, 1, null));
          // there should be exactly 1 revision
          assertEquals(1, array.size());
  
          long ts0 = System.currentTimeMillis();
  
          final int NUM_COMMITS = 100;
 
         // perform NUM_COMMITS commits
         for (int i = 0; i < NUM_COMMITSi++) {
             .commit("/test""+\"child" + i + "\":{}"null"commit#" + i);
         }
 
         // get oldest available revision
         array = parseJSONArray(.getRevisionHistory(ts0, -1, null));
         // there should be exactly NUM_COMMITS revisions
         assertEquals(NUM_COMMITSarray.size());
         long previousTS = ts0;
         for (int i = 0; i < NUM_COMMITSi++) {
             JSONObject rev = getObjectArrayEntry(arrayi);
             assertPropertyExists(rev"id"String.class);
             assertPropertyExists(rev"ts"Long.class);
             // verify commit msg
             assertPropertyValue(rev"msg""commit#" + i);
             // verify chronological order
             long ts = (LongresolveValue(rev"ts");
             assertTrue(previousTS <= ts);
             previousTS = ts;
         }
 
         // last revision should be the current head revision
         assertPropertyValue(getObjectArrayEntry(arrayarray.size() - 1), "id".getHeadRevision());
 
         String fromRev = (StringresolveValue(getObjectArrayEntry(array, 0), "id");
         String toRev = (StringresolveValue(getObjectArrayEntry(arrayarray.size() - 1), "id");
 
         // verify journal
         array = parseJSONArray(.getJournal(fromRevtoRev""));
         // there should be exactly NUM_COMMITS entries
         assertEquals(NUM_COMMITSarray.size());
         // verify that 1st and last rev match fromRev and toRev
         assertPropertyValue(getObjectArrayEntry(array, 0), "id"fromRev);
         assertPropertyValue(getObjectArrayEntry(arrayarray.size() - 1), "id"toRev);
 
         previousTS = ts0;
         for (int i = 0; i < NUM_COMMITSi++) {
             JSONObject rev = getObjectArrayEntry(arrayi);
             assertPropertyExists(rev"id"String.class);
             assertPropertyExists(rev"ts"Long.class);
             assertPropertyExists(rev"changes"String.class);
             // TODO verify json diff
             // verify commit msg
             assertPropertyValue(rev"msg""commit#" + i);
             // verify chronological order
             long ts = (LongresolveValue(rev"ts");
             assertTrue(previousTS <= ts);
             previousTS = ts;
         }
 
         // test with 'negative' range (from and to swapped)
         array = parseJSONArray(.getJournal(toRevfromRev""));
         // there should be exactly 0 entries
         assertEquals(0, array.size());
     }
 
     @Test
     public void revisionOpsFiltered() {
         // test getRevisionHistory/getJournal path filter
         .commit("/test""+\"foo\":{} +\"bar\":{}"null"");
 
         try {
             Thread.sleep(100);
         } catch (InterruptedException ignore) {
         }
 
         long ts = System.currentTimeMillis();
 
         String revFoo = .commit("/test/foo""^\"p1\":123"null"");
         String revBar = .commit("/test/bar""^\"p2\":456"null"");
         String rev0 = revFoo;
 
         // get history since ts (no filter)
         JSONArray array = parseJSONArray(.getRevisionHistory(ts, -1, null));
         // history should contain 2 commits: revFoo and revBar
         assertEquals(2, array.size());
         assertPropertyValue(getObjectArrayEntry(array, 0), "id"revFoo);
         assertPropertyValue(getObjectArrayEntry(array, 1), "id"revBar);
 
         // get history since ts (non-matching filter)
         array = parseJSONArray(.getRevisionHistory(ts, -1, "/blah"));
         // history should contain 0 commits since filter doesn't match
         assertEquals(0, array.size());
 
         // get history since ts (filter on /test/bar)
         array = parseJSONArray(.getRevisionHistory(ts, -1, "/test/bar"));
         // history should contain 1 commit: revBar
         assertEquals(1, array.size());
         assertPropertyValue(getObjectArrayEntry(array, 0), "id"revBar);
 
         // get journal (no filter)
         array = parseJSONArray(.getJournal(rev0null""));
         // journal should contain 2 commits: revFoo and revBar
         assertEquals(2, array.size());
         assertPropertyValue(getObjectArrayEntry(array, 0), "id"revFoo);
         assertPropertyValue(getObjectArrayEntry(array, 1), "id"revBar);
 
         // get journal (non-matching filter)
         array = parseJSONArray(.getJournal(rev0null"/blah"));
         // journal should contain 0 commits since filter doesn't match
         assertEquals(0, array.size());
 
         // get journal (filter on /test/bar)
         array = parseJSONArray(.getJournal(rev0null"/test/bar"));
         // journal should contain 1 commit: revBar
         assertEquals(1, array.size());
         assertPropertyValue(getObjectArrayEntry(array, 0), "id"revBar);
         String diff = (StringresolveValue(getObjectArrayEntry(array, 0), "changes");
         assertNotNull(diff);
         assertTrue(diff.contains("456"));
         assertFalse(diff.contains("123"));
     }
 
     @Test
     public void diff() {
         String rev0 = .getHeadRevision();
 
         String rev1 = .commit("/test""+\"target\":{}"nullnull);
         assertTrue(.nodeExists("/test/target"null));
 
         // get reverse diff
         String reverseDiff = .diff(rev1rev0null, -1);
         assertNotNull(reverseDiff);
         assertTrue(reverseDiff.length() > 0);
 
         // commit reverse diff
         String rev2 = .commit(""reverseDiffnullnull);
         assertFalse(.nodeExists("/test/target"null));
 
         // diff of rev0->rev2 should be empty
         assertEquals("".diff(rev0rev2null, -1));
     }
 
     @Test
     public void diffFiltered() {
         String rev0 = .commit("/test""+\"foo\":{} +\"bar\":{}"null"");
 
         String rev1 = .commit("/test/foo""^\"p1\":123"null"");
         String rev2 = .commit("/test/bar""^\"p2\":456"null"");
 
         // test with path filter
         String diff = .diff(rev0rev2"/test", -1);
         assertNotNull(diff);
         assertFalse(diff.isEmpty());
         assertTrue(diff.contains("foo"));
         assertTrue(diff.contains("bar"));
         assertTrue(diff.contains("123"));
         assertTrue(diff.contains("456"));
 
         diff = .diff(rev0rev2"/test/foo", -1);
         assertNotNull(diff);
         assertFalse(diff.isEmpty());
         assertTrue(diff.contains("foo"));
         assertFalse(diff.contains("bar"));
         assertTrue(diff.contains("123"));
         assertFalse(diff.contains("456"));
 
         // non-matching filter
         diff = .diff(rev0rev2"/blah", -1);
         assertNotNull(diff);
         assertTrue(diff.isEmpty());
     }
 
     @Test
     public void diffDepthLimited() {
         // initial content (/test/foo)
         String rev0 = .commit("/test""+\"foo\":{}"null"");
 
         // add /test/foo/bar
         String rev1 = .commit("/test/foo""+\"bar\":{\"p1\":123}"null"");
 
         // modify property /test/foo/bar/p1
         String rev2 = .commit("/test/foo/bar""^\"p1\":456"null"");
 
         // diff with depth -1
         assertEquals(.diff(rev0rev2"/".), .diff(rev0rev2"/", -1));
 
         // diff with depth 5
         String diff = .diff(rev0rev2"/", 5);
         // returned +"/test/foo/bar":{"p1":456}
         assertNotNull(diff);
         assertFalse(diff.isEmpty());
         assertTrue(diff.contains("/test/foo/bar"));
         assertTrue(diff.contains("456"));
 
         // diff with depth 0
         diff = .diff(rev0rev2"/", 0);
         // returned ^"/test", indicating that there are changes below /test
         assertNotNull(diff);
         assertFalse(diff.isEmpty());
         assertFalse(diff.contains("/test/foo"));
         assertFalse(diff.contains("456"));
         assertTrue(diff.contains("/test"));
         assertTrue(diff.startsWith("^"));
 
         // diff with depth 1
         diff = .diff(rev0rev2"/", 1);
         // returned ^"/test/foo", indicating that there are changes below /test/foo
         assertNotNull(diff);
         assertFalse(diff.isEmpty());
         assertFalse(diff.contains("/test/foo/bar"));
         assertFalse(diff.contains("456"));
         assertTrue(diff.contains("/test/foo"));
         assertTrue(diff.startsWith("^"));
     }
 
     @Test
     public void diffWithMove() {
         String base = .commit("""+\"/a\":{\"b\":{}}".getHeadRevision(), null);
         String head = .commit("""+\"/a/c\":{}+\"/a/d\":{}-\"/a/b\""basenull);
         String diff = .diff(basehead"/a", 0);
         assertTrue(diff.contains("c"));
         assertTrue(diff.contains("d"));
     }
 
     @Test
     public void snapshotIsolation() {
         final int NUM_COMMITS = 100;
 
         String[] revs = new String[NUM_COMMITS];
 
         // perform NUM_COMMITS commits
         for (int i = 0; i < NUM_COMMITSi++) {
             revs[i] = .commit("/test""^\"cnt\":" + inullnull);
         }
         // verify that each revision contains the expected distinct property value
         for (int i = 0; i < NUM_COMMITSi++) {
             JSONObject obj = parseJSONObject(.getNodes("/test"revs[i], 1, 0, -1, null));
             assertPropertyValue(obj"cnt", (longi);
         }
     }
 
     @Test
     public void waitForCommit() throws InterruptedException {
         final long SHORT_TIMEOUT = 1000;
         final long LONG_TIMEOUT = 1000;
 
         // concurrent commit
         String oldHead = .getHeadRevision();
 
         Thread t = new Thread("") {
             @Override
             public void run() {
                 String newHead = .commit("/""+\"foo\":{}"null"");
                 setName(newHead);
             }
         };
         t.start();
         String newHead = .waitForCommit(oldHeadLONG_TIMEOUT);
         t.join();
 
         assertFalse(oldHead.equals(newHead));
         assertEquals(newHeadt.getName());
         assertEquals(newHead.getHeadRevision());
 
         // the current head is already more recent than oldRevision;
         // the method should return immediately (TIMEOUT not applied)
         String currentHead = .getHeadRevision();
         long t0 = System.currentTimeMillis();
         newHead = .waitForCommit(oldHeadLONG_TIMEOUT);
         long t1 = System.currentTimeMillis();
         assertTrue((t1 - t0) < LONG_TIMEOUT);
         assertEquals(currentHeadnewHead);
 
         // there's no more recent head available;
         // the method should wait for the given short timeout
         currentHead = .getHeadRevision();
         long t2 = System.currentTimeMillis();
         newHead = .waitForCommit(currentHeadSHORT_TIMEOUT);
         long t3 = System.currentTimeMillis();
         assertTrue((t3 - t2) >= SHORT_TIMEOUT);
         assertEquals(currentHeadnewHead);
     }
 
     @Test
     public void addAndMove() {
         String head = .getHeadRevision();
         head = .commit("",
                 "+\"/root\":{}\n" +
                         "+\"/root/a\":{}\n",
                 head"");
 
         head = .commit("",
                 "+\"/root/a/b\":{}\n" +
                         ">\"/root/a\":\"/root/c\"\n",
                 head"");
 
         assertFalse(.nodeExists("/root/a"head));
         assertTrue(.nodeExists("/root/c/b"head));
     }
 
     @Test
     public void copy() {
         .commit("/""*\"test\":\"testCopy\""null"");
 
         assertTrue(.nodeExists("/testCopy"null));
         assertTrue(.nodeExists("/test"null));
 
         JSONObject obj = parseJSONObject(.getNodes("/test"null, 99, 0, -1, null));
         JSONObject obj1 = parseJSONObject(.getNodes("/testCopy"null, 99, 0, -1, null));
         assertEquals(objobj1);
     }
 
     @Test
     public void addAndCopy() {
         .commit("/",
                 "+\"x\":{}\n" +
                         "+\"y\":{}\n",
                 null"");
 
         .commit("/",
                 "+\"x/a\":{}\n" +
                         "*\"x\":\"y/x1\"\n",
                 null"");
 
         assertTrue(.nodeExists("/x/a"null));
         assertTrue(.nodeExists("/y/x1/a"null));
     }
 
     @Test
     public void copyToDescendant() {
         .commit("/",
                 "+\"test/child\":{}\n" +
                         "*\"test\":\"test/copy\"\n",
                 null"");
 
         assertTrue(.nodeExists("/test/child"null));
         assertTrue(.nodeExists("/test/copy/child"null));
         JSONObject obj = parseJSONObject(.getNodes("/test"null, 99, 0, -1, null));
         assertPropertyValue(obj":childNodeCount", 2l);
         assertPropertyValue(obj"copy/:childNodeCount", 1l);
         assertPropertyValue(obj"copy/child/:childNodeCount", 0l);
 
         .commit("""+\"/root\":{} +\"/root/N4\":{} *\"/root/N4\":\"/root/N4/N5\""nullnull);
         assertTrue(.nodeExists("/root"null));
         assertTrue(.nodeExists("/root/N4"null));
         assertTrue(.nodeExists("/root/N4/N5"null));
         obj = parseJSONObject(.getNodes("/root"null, 99, 0, -1, null));
         assertPropertyValue(obj":childNodeCount", 1l);
         assertPropertyValue(obj"N4/:childNodeCount", 1l);
         assertPropertyValue(obj"N4/N5/:childNodeCount", 0l);
     }
 
     @Test
     public void getNodes() {
         String head = .getHeadRevision();
 
         // verify initial content
         JSONObject obj = parseJSONObject(.getNodes("/"head, 1, 0, -1, null));
         assertPropertyValue(obj"test/stringProp""stringVal");
         assertPropertyValue(obj"test/intProp", 42L);
         assertPropertyValue(obj"test/floatProp", 42.2);
         assertPropertyValue(obj"test/booleanProp"true);
         assertPropertyValue(obj"test/multiIntProp"new Object[]{1, 2, 3});
     }
 
     @Test
     public void getNodesHash() {
         // :hash must be explicitly specified in the filter
         JSONObject obj = parseJSONObject(.getNodes("/"null, 1, 0, -1, null));
         assertPropertyNotExists(obj":hash");
         obj = parseJSONObject(.getNodes("/"null, 1, 0, -1, "{\"properties\":[\"*\"]}"));
         assertPropertyNotExists(obj":hash");
 
         // verify initial content with :hash property
         obj = parseJSONObject(.getNodes("/"null, 1, 0, -1, "{\"properties\":[\"*\",\":hash\"]}"));
         assertPropertyValue(obj"test/booleanProp"true);
 
         if (obj.get(":hash") == null) {
             // :hash is optional, an implementation might not support it
             return;
         }
 
         assertPropertyExists(obj":hash"String.class);
         String hash0 = (StringresolveValue(obj":hash");
 
         // get node by identifier (:hash)
         JSONObject obj1 = parseJSONObject(.getNodes(hash0null, 1, 0, -1, "{\"properties\":[\"*\",\":hash\"]}"));
         assertEquals(objobj1);
 
         // modify a property and verify that the hash of the root node changed
         .commit("/test""^\"booleanProp\":false"nullnull);
         obj = parseJSONObject(.getNodes("/"null, 1, 0, -1, "{\"properties\":[\"*\",\":hash\"]}"));
         assertPropertyValue(obj"test/booleanProp"false);
 
         assertPropertyExists(obj":hash"String.class);
         String hash1 = (StringresolveValue(obj":hash");
 
         assertFalse(hash0.equals(hash1));
 
         // undo property modification and verify that the hash
         // of the root node is now the same as before the modification
         .commit("/test""^\"booleanProp\":true"nullnull);
         obj = parseJSONObject(.getNodes("/"null, 1, 0, -1, "{\"properties\":[\"*\",\":hash\"]}"));
         assertPropertyValue(obj"test/booleanProp"true);
 
         assertPropertyExists(obj":hash"String.class);
         String hash2 = (StringresolveValue(obj":hash");
 
         assertFalse(hash1.equals(hash2));
         assertTrue(hash0.equals(hash2));
     }
 
     @Test
     public void getNodesId() {
         // :id must be explicitly specified in the filter
         JSONObject obj = parseJSONObject(.getNodes("/"null, 1, 0, -1, null));
         assertPropertyNotExists(obj":id");
         obj = parseJSONObject(.getNodes("/"null, 1, 0, -1, "{\"properties\":[\"*\"]}"));
         assertPropertyNotExists(obj":id");
 
         // verify initial content with :id property
         obj = parseJSONObject(.getNodes("/"null, 1, 0, -1, "{\"properties\":[\"*\",\":id\"]}"));
         assertPropertyValue(obj"test/booleanProp"true);
 
         if (obj.get(":id") == null) {
             // :id is optional, an implementation might not support it
             return;
         }
 
         assertPropertyExists(obj":id"String.class);
         String id0 = (StringresolveValue(obj":id");
 
         // get node by identifier (:id)
         JSONObject obj1 = parseJSONObject(.getNodes(id0null, 1, 0, -1, "{\"properties\":[\"*\",\":id\"]}"));
         assertEquals(objobj1);
 
         // modify a property and verify that the id of the root node changed
         .commit("/test""^\"booleanProp\":false"nullnull);
         obj = parseJSONObject(.getNodes("/"null, 1, 0, -1, "{\"properties\":[\"*\",\":id\"]}"));
         assertPropertyValue(obj"test/booleanProp"false);
 
         assertPropertyExists(obj":id"String.class);
         String id1 = (StringresolveValue(obj":id");
 
         assertFalse(id0.equals(id1));
     }
 
     @Test
     public void getNodesChildNodeCount() {
         // :childNodeCount is included per default
         JSONObject obj = parseJSONObject(.getNodes("/"null, 1, 0, -1, null));
         assertPropertyExists(obj":childNodeCount"Long.class);
         assertPropertyExists(obj"test/:childNodeCount"Long.class);
         long count = (LongresolveValue(obj":childNodeCount") ;
         assertEquals(count.getChildNodeCount("/"null));
 
         obj = parseJSONObject(.getNodes("/"null, 1, 0, -1, "{\"properties\":[\"*\"]}"));
         assertPropertyExists(obj":childNodeCount"Long.class);
         assertPropertyExists(obj"test/:childNodeCount"Long.class);
         count = (LongresolveValue(obj":childNodeCount") ;
         assertEquals(count.getChildNodeCount("/"null));
 
         // explicitly exclude :childNodeCount
         obj = parseJSONObject(.getNodes("/"null, 1, 0, -1, "{\"properties\":[\"*\",\"-:childNodeCount\"]}"));
         assertPropertyNotExists(obj":childNodeCount");
         assertPropertyNotExists(obj"test/:childNodeCount");
     }
 
     @Test
     public void getNodesFiltered() {
         String head = .getHeadRevision();
 
         // verify initial content using filter
         String filter = "{ \"properties\" : [ \"*ntProp\", \"-mult*\" ] } ";
         JSONObject obj = parseJSONObject(.getNodes("/"head, 1, 0, -1, filter));
         assertPropertyExists(obj"test/intProp");
         assertPropertyNotExists(obj"test/multiIntProp");
         assertPropertyNotExists(obj"test/stringProp");
         assertPropertyNotExists(obj"test/floatProp");
         assertPropertyNotExists(obj"test/booleanProp");
     }
 
     @Test
     public void getNodesWithDefaultFilter() {
         String head = .getHeadRevision();
 
         String[] filters = { null"" };
 
         for (String filter : filters) {
             // verify initial content with implicit default filter
             JSONObject obj = parseJSONObject(.getNodes("/"head, 1, 0, -1, filter));
             assertPropertyExists(obj"test/:childNodeCount");
             assertPropertyNotExists(obj"test/:hash");
             assertPropertyNotExists(obj"test/:id");
             assertPropertyValue(obj"test/stringProp""stringVal");
             assertPropertyValue(obj"test/intProp", 42L);
             assertPropertyValue(obj"test/floatProp", 42.2);
             assertPropertyValue(obj"test/booleanProp"true);
             assertPropertyValue(obj"test/multiIntProp"new Object[]{1, 2, 3});
         }
     }
 
     @Test
     public void getNodesNonExistingPath() {
         String head = .getHeadRevision();
 
         String nonExistingPath = "/test/" + System.currentTimeMillis();
         assertFalse(.nodeExists(nonExistingPathhead));
 
         assertNull(.getNodes(nonExistingPathhead, 0, 0, -1, null));
     }
 
     @Test
     public void getNodesNonExistingRevision() {
         String nonExistingRev = "12345678";
 
         try {
             .nodeExists("/test"nonExistingRev);
             fail("Success with non-existing revision: " + nonExistingRev);
         } catch (MicroKernelException e) {
             // expected
         }
 
         try {
             .getNodes("/test"nonExistingRev, 0, 0, -1, null);
             fail("Success with non-existing revision: " + nonExistingRev);
         } catch (MicroKernelException e) {
             // expected
         }
     }
 
     @Test
     public void getNodesDepth() {
         .commit("""+\"/testRoot\":{\"depth\":0}"null"");
         .commit("/testRoot""+\"a\":{\"depth\":1}\n" +
                 "+\"a/b\":{\"depth\":2}\n" +
                 "+\"a/b/c\":{\"depth\":3}\n" +
                 "+\"a/b/c/d\":{\"depth\":4}\n" +
                 "+\"a/b/c/d/e\":{\"depth\":5}\n",
                 null"");
 
         // depth = 0: properties, including :childNodeCount and empty child node objects
         JSONObject obj = parseJSONObject(.getNodes("/testRoot"null, 0, 0, -1, null));
         assertPropertyValue(obj"depth", 0l);
         assertPropertyValue(obj":childNodeCount", 1l);
         JSONObject child = resolveObjectValue(obj"a");
         assertNotNull(child);
         assertEquals(0, child.size());
 
         // depth = 1: properties, child nodes, their properties (including :childNodeCount)
         // and their empty child node objects
         obj = parseJSONObject(.getNodes("/testRoot"null, 1, 0, -1, null));
         assertPropertyValue(obj"depth", 0l);
         assertPropertyValue(obj":childNodeCount", 1l);
         assertPropertyValue(obj"a/depth", 1l);
         assertPropertyValue(obj"a/:childNodeCount", 1l);
         child = resolveObjectValue(obj"a/b");
         assertNotNull(child);
         assertEquals(0, child.size());
 
         // depth = 2: [and so on...]
         obj = parseJSONObject(.getNodes("/testRoot"null, 2, 0, -1, null));
         assertPropertyValue(obj"depth", 0l);
         assertPropertyValue(obj":childNodeCount", 1l);
         assertPropertyValue(obj"a/depth", 1l);
         assertPropertyValue(obj"a/:childNodeCount", 1l);
         assertPropertyValue(obj"a/b/depth", 2l);
         assertPropertyValue(obj"a/b/:childNodeCount", 1l);
         child = resolveObjectValue(obj"a/b/c");
         assertNotNull(child);
         assertEquals(0, child.size());
 
         // depth = 3: [and so on...]
         obj = parseJSONObject(.getNodes("/testRoot"null, 3, 0, -1, null));
         assertPropertyValue(obj"depth", 0l);
         assertPropertyValue(obj":childNodeCount", 1l);
         assertPropertyValue(obj"a/depth", 1l);
         assertPropertyValue(obj"a/:childNodeCount", 1l);
         assertPropertyValue(obj"a/b/depth", 2l);
         assertPropertyValue(obj"a/b/:childNodeCount", 1l);
         assertPropertyValue(obj"a/b/c/depth", 3l);
         assertPropertyValue(obj"a/b/c/:childNodeCount", 1l);
         child = resolveObjectValue(obj"a/b/c/d");
         assertNotNull(child);
         assertEquals(0, child.size());
 
         // getNodes(path, revId) must return same result as getNodes(path, revId, 1, 0, -1, null)
         obj = parseJSONObject(.getNodes("/testRoot"null, 1, 0, -1, null));
         JSONObject obj1 = parseJSONObject(.getNodes("/testRoot"null, 1, 0, -1, null));
         assertEquals(objobj1);
     }
 
     @Test
     public void getNodesOffset() {
         // number of siblings (multiple of 10)
         final int NUM_SIBLINGS = 1000;
         // set of all sibling names
         final Set<StringsiblingNames = new HashSet<String>(NUM_SIBLINGS);
 
         // populate siblings
         Random rand = new Random();
         StringBuilder sb = new StringBuilder("+\"/testRoot\":{");
         for (int i = 0; i < NUM_SIBLINGSi++) {
             String name = "n" + rand.nextLong();
             siblingNames.add(name);
             sb.append("\n\"");
             sb.append(name);
             sb.append("\":{}");
             if (i < NUM_SIBLINGS - 1) {
                 sb.append(',');
             }
         }
         sb.append("\n}");
         String head = .commit(""sb.toString(), null"");
 
         // get all siblings in one call
         JSONObject obj = parseJSONObject(.getNodes("/testRoot"head, 0, 0, -1, null));
         assertPropertyValue(obj":childNodeCount", (longNUM_SIBLINGS);
         assertEquals((longNUM_SIBLINGS.getChildNodeCount("/testRoot"head));
         assertEquals(siblingNamesgetNodeNames(obj));
 
         // list of sibling names in iteration order
         final List<StringorderedSiblingNames = new ArrayList<String>(NUM_SIBLINGS);
 
         // get siblings one by one
         for (int i = 0; i < NUM_SIBLINGSi++) {
             obj = parseJSONObject(.getNodes("/testRoot"head, 0, i, 1, null));
             assertPropertyValue(obj":childNodeCount", (longNUM_SIBLINGS);
             Set<Stringset = getNodeNames(obj);
             assertEquals(1, set.size());
             orderedSiblingNames.add(set.iterator().next());
         }
 
         // check completeness
         Set<Stringnames = new HashSet<String>(siblingNames);
         names.removeAll(orderedSiblingNames);
         assertTrue(names.isEmpty());
 
         // we've now established the expected iteration order
 
         // get siblings in 10 chunks
         for (int i = 0; i < 10; i++) {
             obj = parseJSONObject(.getNodes("/testRoot"head, 0, i * 10, NUM_SIBLINGS / 10, null));
             assertPropertyValue(obj":childNodeCount", (longNUM_SIBLINGS);
             names = getNodeNames(obj);
             assertEquals(NUM_SIBLINGS / 10, names.size());
             List<StringsubList = orderedSiblingNames.subList(i * 10, (i * 10) + (NUM_SIBLINGS / 10));
             names.removeAll(subList);
             assertTrue(names.isEmpty());
         }
 
         // test offset with filter
         try {
             parseJSONObject(.getNodes("/testRoot"head, 0, 10, NUM_SIBLINGS / 10, "{\"nodes\":[\"n0*\"]}"));
             fail();
         } catch (Throwable e) {
             // expected
         }
     }
 
     @Test
     public void getNodesMaxNodeCount() {
         // number of siblings
         final int NUM_SIBLINGS = 100;
 
         // populate siblings
         StringBuffer sb = new StringBuffer("+\"/testRoot\":{");
         for (int i = 0; i < NUM_SIBLINGSi++) {
             String name = "n" + i;
             sb.append("\n\"");
             sb.append(name);
             sb.append("\":{");
 
             for (int n = 0; n < NUM_SIBLINGSn++) {
                 String childName = "n" + n;
                 sb.append("\n\"");
                 sb.append(childName);
                 sb.append("\":{}");
                 if (n < NUM_SIBLINGS - 1) {
                     sb.append(',');
                 }
             }
             sb.append("}");
             if (i < NUM_SIBLINGS - 1) {
                 sb.append(',');
             }
         }
         sb.append("\n}");
         String head = .commit(""sb.toString(), null"");
 
         // get all siblings
         JSONObject obj = parseJSONObject(.getNodes("/testRoot"head, 1, 0, -1, null));
         assertPropertyValue(obj":childNodeCount", (longNUM_SIBLINGS);
         assertEquals((longNUM_SIBLINGS.getChildNodeCount("/testRoot"head));
         Set<Stringnames = getNodeNames(obj);
         assertTrue(names.size() == NUM_SIBLINGS);
         String childName = names.iterator().next();
         JSONObject childObj = resolveObjectValue(objchildName);
         assertPropertyValue(childObj":childNodeCount", (longNUM_SIBLINGS);
         assertTrue(getNodeNames(childObj).size() == NUM_SIBLINGS);
 
         // get max 10 siblings
         int maxSiblings = 10;
         obj = parseJSONObject(.getNodes("/testRoot"head, 1, 0, maxSiblingsnull));
         assertPropertyValue(obj":childNodeCount", (longNUM_SIBLINGS);
         assertEquals((longNUM_SIBLINGS.getChildNodeCount("/testRoot"head));
         names = getNodeNames(obj);
         assertEquals(maxSiblingsnames.size());
         childName = names.iterator().next();
         childObj = resolveObjectValue(objchildName);
         assertPropertyValue(childObj":childNodeCount", (longNUM_SIBLINGS);
         assertTrue(getNodeNames(childObj).size() == maxSiblings);
 
         // get max 5 siblings using filter
         maxSiblings = 5;
         obj = parseJSONObject(.getNodes("/testRoot"head, 1, 0, maxSiblings"{\"nodes\":[\"n1*\"]}"));
         assertPropertyValue(obj":childNodeCount", (longNUM_SIBLINGS);
         assertEquals((longNUM_SIBLINGS.getChildNodeCount("/testRoot"head));
         names = getNodeNames(obj);
         assertTrue(names.size() == maxSiblings);
         childName = names.iterator().next();
         childObj = resolveObjectValue(objchildName);
         assertPropertyValue(childObj":childNodeCount", (longNUM_SIBLINGS);
         assertTrue(getNodeNames(childObj).size() == maxSiblings);
     }
 
     @Test
     public void getNodesRevision() {
         // 1st pass
         long tst = System.currentTimeMillis();
         String head = .commit("/test""^\"tst\":" + tstnullnull);
         assertEquals(head.getHeadRevision());
         // test getNodes with 'null' revision
         assertPropertyValue(parseJSONObject(.getNodes("/test"null, 1, 0, -1, null)), "tst"tst);
         // test getNodes with specific revision
         assertPropertyValue(parseJSONObject(.getNodes("/test"head, 1, 0, -1, null)), "tst"tst);
 
         // 2nd pass
         ++tst;
         String oldHead = head;
         head = .commit("/test""^\"tst\":" + tstnullnull);
         assertFalse(head.equals(oldHead));
         assertEquals(head.getHeadRevision());
         // test getNodes with 'null' revision
         assertPropertyValue(parseJSONObject(.getNodes("/test"null, 1, 0, -1, null)), "tst"tst);
         // test getNodes with specific revision
         assertPropertyValue(parseJSONObject(.getNodes("/test"head, 1, 0, -1, null)), "tst"tst);
     }
 
     @Test
     public void missingName() {
         String head = .getHeadRevision();
 
         assertTrue(.nodeExists("/test"head));
         try {
             String path = "/test/";
             .getNodes(pathhead, 1, 0, -1, null);
             fail("Success with invalid path: " + path);
         } catch (AssertionError e) {
             // expected
         } catch (MicroKernelException e) {
             // expected
         }
     }
 
     @Test
     public void addNodeWithRelativePath() {
         String head = .getHeadRevision();
 
         head = .commit("/""+\"foo\" : {} \n+\"foo/bar\" : {}"head"");
         assertTrue(.nodeExists("/foo"head));
         assertTrue(.nodeExists("/foo/bar"head));
     }
 
     @Test
     public void commitWithEmptyPath() {
         String head = .getHeadRevision();
 
         head = .commit("""+\"/ene\" : {}\n+\"/ene/mene\" : {}\n+\"/ene/mene/muh\" : {}"head"");
         assertTrue(.nodeExists("/ene/mene/muh"head));
     }
 
     @Test
     public void addPropertyWithRelativePath() {
         String head = .getHeadRevision();
 
         head = .commit("/",
                 "+\"fuu\" : {} \n" +
                         "^\"fuu/bar\" : 42"head"");
         JSONObject obj = parseJSONObject(.getNodes("/fuu"head, 1, 0, -1, null));
         assertPropertyValue(obj"bar", 42L);
     }
 
     @Test
     public void addMultipleNodes() {
         String head = .getHeadRevision();
 
         long millis = System.currentTimeMillis();
         String node1 = "n1_" + millis;
         String node2 = "n2_" + millis;
         head = .commit("/""+\"" + node1 + "\" : {} \n+\"" + node2 + "\" : {}\n"head"");
         assertTrue(.nodeExists('/' + node1head));
         assertTrue(.nodeExists('/' + node2head));
     }
 
     @Test
     public void addDeepNodes() {
         String head = .getHeadRevision();
 
         head = .commit("/",
                 "+\"a\" : {} \n" +
                         "+\"a/b\" : {} \n" +
                         "+\"a/b/c\" : {} \n" +
                         "+\"a/b/c/d\" : {} \n",
                 head"");
 
         assertTrue(.nodeExists("/a"head));
         assertTrue(.nodeExists("/a/b"head));
         assertTrue(.nodeExists("/a/b/c"head));
         assertTrue(.nodeExists("/a/b/c/d"head));
     }
 
     @Test
     public void addItemsIncrementally() {
         String head = .getHeadRevision();
 
         String node = "n_" + System.currentTimeMillis();
 
         head = .commit("/",
                 "+\"" + node + "\" : {} \n" +
                         "+\"" + node + "/child1\" : {} \n" +
                         "+\"" + node + "/child2\" : {} \n" +
                         "+\"" + node + "/child1/grandchild11\" : {} \n" +
                         "^\"" + node + "/prop1\" : 41\n" +
                         "^\"" + node + "/child1/prop2\" : 42\n" +
                         "^\"" + node + "/child1/grandchild11/prop3\" : 43",
                 head"");
 
         JSONObject obj = parseJSONObject(.getNodes('/' + nodehead, 3, 0, -1, null));
         assertPropertyValue(obj"prop1", 41L);
         assertPropertyValue(obj":childNodeCount", 2L);
         assertPropertyValue(obj"child1/prop2", 42L);
         assertPropertyValue(obj"child1/:childNodeCount", 1L);
         assertPropertyValue(obj"child1/grandchild11/prop3", 43L);
         assertPropertyValue(obj"child1/grandchild11/:childNodeCount", 0L);
         assertPropertyValue(obj"child2/:childNodeCount", 0L);
     }
 
     @Test
     public void removeNode() {
         String head = .getHeadRevision();
         String node = "removeNode_" + System.currentTimeMillis();
 
         head = .commit("/""+\"" + node + "\" : {\"child\":{}}"head"");
 
         head = .commit('/' + node"-\"child\""head"");
         JSONObject obj = parseJSONObject(.getNodes('/' + nodehead, 1, 0, -1, null));
         assertPropertyValue(obj":childNodeCount", 0L);
     }
 
     @Test
     public void moveNode() {
         String rev0 = .getHeadRevision();
         JSONObject obj = parseJSONObject(.getNodes("/test"null, 99, 0, -1, null));
         .