You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/11/07 10:17:28 UTC
[sling-org-apache-sling-testing-jcr-mock] 01/10: SLING-4042 Donate
sling-mock, jcr-mock, osgi-mock implementation
This is an automated email from the ASF dual-hosted git repository.
rombert pushed a commit to annotated tag org.apache.sling.testing.jcr-mock-1.0.0
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-testing-jcr-mock.git
commit 39572249976327b490288b8f876908d1e49cbd1f
Author: sseifert <ss...@unknown>
AuthorDate: Mon Oct 13 11:54:39 2014 +0000
SLING-4042 Donate sling-mock, jcr-mock, osgi-mock implementation
git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/testing/jcr-mock@1631356 13f79535-47bb-0310-9956-ffa450edef68
---
pom.xml | 91 ++++
.../sling/testing/mock/jcr/AbstractItem.java | 129 ++++++
.../apache/sling/testing/mock/jcr/ItemFilter.java | 31 ++
.../org/apache/sling/testing/mock/jcr/MockJcr.java | 62 +++
.../testing/mock/jcr/MockNamespaceRegistry.java | 72 +++
.../apache/sling/testing/mock/jcr/MockNode.java | 499 +++++++++++++++++++++
.../sling/testing/mock/jcr/MockNodeType.java | 155 +++++++
.../testing/mock/jcr/MockNodeTypeManager.java | 103 +++++
.../sling/testing/mock/jcr/MockNodeTypes.java | 49 ++
.../testing/mock/jcr/MockObservationManager.java | 65 +++
.../sling/testing/mock/jcr/MockProperty.java | 306 +++++++++++++
.../sling/testing/mock/jcr/MockRepository.java | 85 ++++
.../apache/sling/testing/mock/jcr/MockSession.java | 354 +++++++++++++++
.../sling/testing/mock/jcr/MockWorkspace.java | 149 ++++++
.../sling/testing/mock/jcr/ResourceUtil.java | 219 +++++++++
.../sling/testing/mock/jcr/package-info.java | 23 +
src/site/markdown/index.md | 38 ++
src/site/markdown/usage.md | 17 +
.../sling/testing/mock/jcr/AbstractItemTest.java | 95 ++++
.../sling/testing/mock/jcr/MockNodeTest.java | 129 ++++++
.../sling/testing/mock/jcr/MockPropertyTest.java | 240 ++++++++++
.../sling/testing/mock/jcr/MockRepositoryTest.java | 63 +++
.../sling/testing/mock/jcr/MockSessionTest.java | 228 ++++++++++
.../sling/testing/mock/jcr/MockWorkspaceTest.java | 64 +++
.../sling/testing/mock/jcr/ResourceUtilTest.java | 221 +++++++++
25 files changed, 3487 insertions(+)
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..6bd600b
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>sling</artifactId>
+ <version>22</version>
+ <relativePath>../../parent/pom.xml</relativePath>
+ </parent>
+
+ <artifactId>org.apache.sling.testing.jcr-mock</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <packaging>jar</packaging>
+
+ <name>Apache Sling Testing JCR Mock</name>
+ <description>Mock implementation of selected JCR APIs.</description>
+
+ <properties>
+ <sling.java.version>6</sling.java.version>
+ </properties>
+
+ <dependencies>
+
+ <dependency>
+ <groupId>javax.jcr</groupId>
+ <artifactId>jcr</artifactId>
+ <version>2.0</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.jackrabbit</groupId>
+ <artifactId>jackrabbit-jcr-commons</artifactId>
+ <version>2.8.0</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <version>15.0</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <version>3.0.1</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>2.4</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+
+</project>
diff --git a/src/main/java/org/apache/sling/testing/mock/jcr/AbstractItem.java b/src/main/java/org/apache/sling/testing/mock/jcr/AbstractItem.java
new file mode 100644
index 0000000..0eb92f3
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/mock/jcr/AbstractItem.java
@@ -0,0 +1,129 @@
+/*
+ * 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.sling.testing.mock.jcr;
+
+import javax.jcr.Item;
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.ItemVisitor;
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * Mock {@link Item} implementation.
+ */
+abstract class AbstractItem implements Item {
+
+ private final String path;
+ private final Session session;
+
+ public AbstractItem(final String path, final Session session) {
+ this.path = path;
+ this.session = session;
+ }
+
+ @Override
+ public String getName() {
+ return ResourceUtil.getName(this.path);
+ }
+
+ @Override
+ public String getPath() {
+ return this.path;
+ }
+
+ @Override
+ public Node getParent() throws RepositoryException {
+ return (Node) getSession().getItem(ResourceUtil.getParent(this.path));
+ }
+
+ @Override
+ public Session getSession() {
+ return this.session;
+ }
+
+ @Override
+ public boolean isModified() {
+ return false;
+ }
+
+ @Override
+ public boolean isNew() {
+ return false;
+ }
+
+ @Override
+ public Item getAncestor(final int depth) throws RepositoryException {
+ if (depth < 0 || depth > getDepth()) {
+ throw new ItemNotFoundException();
+ }
+ return this.session.getItem(ResourceUtil.getParent(this.path, depth));
+ }
+
+ protected String makeAbsolutePath(final String relativePath) {
+ String absolutePath = relativePath;
+ // ensure the path is absolute and normalized
+ if (!StringUtils.startsWith(absolutePath, "/")) {
+ absolutePath = this.path + "/" + absolutePath; // NOPMD
+ }
+ return ResourceUtil.normalize(absolutePath);
+ }
+
+ protected MockSession getMockedSession() {
+ return (MockSession) this.session;
+ }
+
+ @Override
+ public void remove() throws RepositoryException {
+ getSession().removeItem(getPath());
+ }
+
+ @Override
+ public int getDepth() throws RepositoryException {
+ if (StringUtils.equals("/", this.path)) {
+ return 0;
+ } else {
+ return StringUtils.countMatches(this.path, "/");
+ }
+ }
+
+ // --- unsupported operations ---
+ @Override
+ public void accept(final ItemVisitor visitor) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isSame(final Item otherItem) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void refresh(final boolean keepChanges) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void save() {
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/testing/mock/jcr/ItemFilter.java b/src/main/java/org/apache/sling/testing/mock/jcr/ItemFilter.java
new file mode 100644
index 0000000..2e144a1
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/mock/jcr/ItemFilter.java
@@ -0,0 +1,31 @@
+/*
+ * 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.sling.testing.mock.jcr;
+
+import javax.jcr.Item;
+import javax.jcr.RepositoryException;
+
+/**
+ * Used internally for filtering items in JCR data map.
+ */
+interface ItemFilter {
+
+ boolean accept(Item item) throws RepositoryException;
+
+}
diff --git a/src/main/java/org/apache/sling/testing/mock/jcr/MockJcr.java b/src/main/java/org/apache/sling/testing/mock/jcr/MockJcr.java
new file mode 100644
index 0000000..11c3c1d
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/mock/jcr/MockJcr.java
@@ -0,0 +1,62 @@
+/*
+ * 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.sling.testing.mock.jcr;
+
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+/**
+ * Factory for mock JCR objects.
+ */
+public final class MockJcr {
+
+ /**
+ * Default workspace name
+ */
+ public static final String DEFAULT_WORKSPACE = "mockedWorkspace";
+
+ private MockJcr() {
+ // static methods only
+ }
+
+ /**
+ * Create a new mocked in-memory JCR repository. Beware: each session has
+ * its own data store.
+ * @return JCR repository
+ */
+ public static Repository newRepository() {
+ return new MockRepository();
+ }
+
+ /**
+ * Create a new mocked in-memory JCR session. It contains only the root
+ * node. All data of the session is thrown away if it gets garbage
+ * collected.
+ * @return JCR session
+ */
+ public static Session newSession() {
+ try {
+ return newRepository().login();
+ } catch (RepositoryException ex) {
+ throw new RuntimeException("Creating mocked JCR session failed.", ex);
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/testing/mock/jcr/MockNamespaceRegistry.java b/src/main/java/org/apache/sling/testing/mock/jcr/MockNamespaceRegistry.java
new file mode 100644
index 0000000..91b194f
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/mock/jcr/MockNamespaceRegistry.java
@@ -0,0 +1,72 @@
+/*
+ * 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.sling.testing.mock.jcr;
+
+import java.util.Set;
+
+import javax.jcr.NamespaceRegistry;
+import javax.jcr.RepositoryException;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+
+/**
+ * Mock {@link NamespaceRegistry} implementation.
+ */
+class MockNamespaceRegistry implements NamespaceRegistry {
+
+ private final BiMap<String, String> namespacePrefixMapping = HashBiMap.create();
+
+ public MockNamespaceRegistry() {
+ this.namespacePrefixMapping.put("jcr", "http://www.jcp.org/jcr/1.0");
+ }
+
+ @Override
+ public String getURI(final String prefix) {
+ return this.namespacePrefixMapping.get(prefix);
+ }
+
+ @Override
+ public String getPrefix(final String uri) {
+ return this.namespacePrefixMapping.inverse().get(uri);
+ }
+
+ @Override
+ public void registerNamespace(final String prefix, final String uri) {
+ this.namespacePrefixMapping.put(prefix, uri);
+ }
+
+ @Override
+ public void unregisterNamespace(final String prefix) {
+ this.namespacePrefixMapping.remove(prefix);
+ }
+
+ @Override
+ public String[] getPrefixes() throws RepositoryException {
+ Set<String> keys = this.namespacePrefixMapping.keySet();
+ return keys.toArray(new String[keys.size()]);
+ }
+
+ @Override
+ public String[] getURIs() throws RepositoryException {
+ Set<String> values = this.namespacePrefixMapping.values();
+ return values.toArray(new String[values.size()]);
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/testing/mock/jcr/MockNode.java b/src/main/java/org/apache/sling/testing/mock/jcr/MockNode.java
new file mode 100644
index 0000000..41350d6
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/mock/jcr/MockNode.java
@@ -0,0 +1,499 @@
+/*
+ * 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.sling.testing.mock.jcr;
+
+import java.io.InputStream;
+import java.math.BigDecimal;
+import java.util.Calendar;
+import java.util.UUID;
+import java.util.regex.Pattern;
+
+import javax.jcr.Binary;
+import javax.jcr.Item;
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.RangeIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+import javax.jcr.lock.Lock;
+import javax.jcr.nodetype.NodeDefinition;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.version.Version;
+import javax.jcr.version.VersionHistory;
+
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.commons.iterator.NodeIteratorAdapter;
+import org.apache.jackrabbit.commons.iterator.PropertyIteratorAdapter;
+
+/**
+ * Mock {@link Node} implementation
+ */
+class MockNode extends AbstractItem implements Node {
+
+ private final UUID uuid = UUID.randomUUID();
+ private final NodeType nodeType;
+
+ public MockNode(final String path, final Session session, final NodeType nodeType) {
+ super(path, session);
+ this.nodeType = nodeType;
+ }
+
+ @Override
+ public Node addNode(final String relPath) throws RepositoryException {
+ return addNode(relPath, JcrConstants.NT_UNSTRUCTURED);
+ }
+
+ @Override
+ public Node addNode(final String relPath, final String primaryNodeTypeName) throws RepositoryException {
+ String path = makeAbsolutePath(relPath);
+ Node node = new MockNode(path, getSession(), new MockNodeType(primaryNodeTypeName));
+ getMockedSession().addItem(node);
+ return node;
+ }
+
+ @Override
+ public Node getNode(final String relPath) throws RepositoryException {
+ String path = makeAbsolutePath(relPath);
+ return getSession().getNode(path);
+ }
+
+ @Override
+ public NodeIterator getNodes() throws RepositoryException {
+ RangeIterator items = getMockedSession().listChildren(getPath(), new ItemFilter() {
+ @Override
+ public boolean accept(final Item item) {
+ return item instanceof Node;
+ }
+ });
+ return new NodeIteratorAdapter(items, items.getSize());
+ }
+
+ @Override
+ public NodeIterator getNodes(final String namePattern) throws RepositoryException {
+ final Pattern pattern = Pattern.compile(namePattern);
+ RangeIterator items = getMockedSession().listChildren(getPath(), new ItemFilter() {
+ @Override
+ public boolean accept(final Item item) throws RepositoryException {
+ return (item instanceof Node) && pattern.matcher(item.getName()).matches();
+ }
+ });
+ return new NodeIteratorAdapter(items, items.getSize());
+ }
+
+ @Override
+ public PropertyIterator getProperties() throws RepositoryException {
+ RangeIterator items = getMockedSession().listChildren(getPath(), new ItemFilter() {
+ @Override
+ public boolean accept(final Item item) {
+ return item instanceof Property;
+ }
+ });
+ return new PropertyIteratorAdapter(items, items.getSize());
+ }
+
+ @Override
+ public PropertyIterator getProperties(final String namePattern) throws RepositoryException {
+ final Pattern pattern = Pattern.compile(namePattern);
+ RangeIterator items = getMockedSession().listChildren(getPath(), new ItemFilter() {
+ @Override
+ public boolean accept(final Item item) throws RepositoryException {
+ return (item instanceof Property) && pattern.matcher(item.getName()).matches();
+ }
+ });
+ return new PropertyIteratorAdapter(items, items.getSize());
+ }
+
+ @Override
+ public Property getProperty(final String relPath) throws RepositoryException {
+ String path = makeAbsolutePath(relPath);
+ return getSession().getProperty(path);
+ }
+
+ @Override
+ public String getIdentifier() {
+ return this.uuid.toString();
+ }
+
+ @Override
+ public String getUUID() {
+ return getIdentifier();
+ }
+
+ @Override
+ public boolean hasNode(final String relPath) throws RepositoryException {
+ String path = makeAbsolutePath(relPath);
+ return getSession().nodeExists(path);
+ }
+
+ @Override
+ public boolean hasNodes() throws RepositoryException {
+ return getNodes().hasNext();
+ }
+
+ @Override
+ public boolean hasProperties() throws RepositoryException {
+ return getProperties().hasNext();
+ }
+
+ @Override
+ public boolean hasProperty(final String relPath) throws RepositoryException {
+ String path = makeAbsolutePath(relPath);
+ return getSession().propertyExists(path);
+ }
+
+ @Override
+ public Property setProperty(final String name, final Value value) throws RepositoryException {
+ Property property = new MockProperty(getPath() + "/" + name, getSession());
+ property.setValue(value);
+ getMockedSession().addItem(property);
+ return property;
+ }
+
+ @Override
+ public Property setProperty(final String name, final Value[] values) throws RepositoryException {
+ Property property = new MockProperty(getPath() + "/" + name, getSession());
+ property.setValue(values);
+ getMockedSession().addItem(property);
+ return property;
+ }
+
+ @Override
+ public Property setProperty(final String name, final String[] values) throws RepositoryException {
+ Property property = new MockProperty(getPath() + "/" + name, getSession());
+ property.setValue(values);
+ getMockedSession().addItem(property);
+ return property;
+ }
+
+ @Override
+ public Property setProperty(final String name, final String value) throws RepositoryException {
+ Property property = new MockProperty(getPath() + "/" + name, getSession());
+ property.setValue(value);
+ getMockedSession().addItem(property);
+ return property;
+ }
+
+ @Override
+ @SuppressWarnings("deprecation")
+ public Property setProperty(final String name, final InputStream value) throws RepositoryException {
+ Property property = new MockProperty(getPath() + "/" + name, getSession());
+ property.setValue(value);
+ getMockedSession().addItem(property);
+ return property;
+ }
+
+ @Override
+ public Property setProperty(final String name, final boolean value) throws RepositoryException {
+ Property property = new MockProperty(getPath() + "/" + name, getSession());
+ property.setValue(value);
+ getMockedSession().addItem(property);
+ return property;
+ }
+
+ @Override
+ public Property setProperty(final String name, final double value) throws RepositoryException {
+ Property property = new MockProperty(getPath() + "/" + name, getSession());
+ property.setValue(value);
+ getMockedSession().addItem(property);
+ return property;
+ }
+
+ @Override
+ public Property setProperty(final String name, final long value) throws RepositoryException {
+ Property property = new MockProperty(getPath() + "/" + name, getSession());
+ property.setValue(value);
+ getMockedSession().addItem(property);
+ return property;
+ }
+
+ @Override
+ public Property setProperty(final String name, final Calendar value) throws RepositoryException {
+ Property property = new MockProperty(getPath() + "/" + name, getSession());
+ property.setValue(value);
+ getMockedSession().addItem(property);
+ return property;
+ }
+
+ @Override
+ public Property setProperty(final String name, final Node value) throws RepositoryException {
+ Property property = new MockProperty(getPath() + "/" + name, getSession());
+ property.setValue(value);
+ getMockedSession().addItem(property);
+ return property;
+ }
+
+ @Override
+ public Property setProperty(final String name, final Binary value) throws RepositoryException {
+ Property property = new MockProperty(getPath() + "/" + name, getSession());
+ property.setValue(value);
+ getMockedSession().addItem(property);
+ return property;
+ }
+
+ @Override
+ public Property setProperty(final String name, final BigDecimal value) throws RepositoryException {
+ Property property = new MockProperty(getPath() + "/" + name, getSession());
+ property.setValue(value);
+ getMockedSession().addItem(property);
+ return property;
+ }
+
+ @Override
+ public boolean isNode() {
+ return true;
+ }
+
+ @Override
+ public boolean isNodeType(final String nodeTypeName) throws RepositoryException {
+ return this.nodeType.isNodeType(nodeTypeName);
+ }
+
+ @Override
+ public NodeType getPrimaryNodeType() {
+ return this.nodeType;
+ }
+
+ @Override
+ public Item getPrimaryItem() throws RepositoryException {
+ // support "jcr:content" node and "jcr:data" property as primary items
+ if (hasProperty(JcrConstants.JCR_DATA)) {
+ return getProperty(JcrConstants.JCR_DATA);
+ } else if (hasNode(JcrConstants.JCR_CONTENT)) {
+ return getNode(JcrConstants.JCR_CONTENT);
+ } else {
+ throw new ItemNotFoundException();
+ }
+ }
+
+ // --- unsupported operations ---
+ @Override
+ public Property setProperty(final String name, final Value value, final int type) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Property setProperty(final String name, final Value[] values, final int type) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Property setProperty(final String name, final String[] values, final int type) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Property setProperty(final String name, final String value, final int type) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void addMixin(final String pMixinName) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean canAddMixin(final String pMixinName) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void cancelMerge(final Version pVersion) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Version checkin() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void checkout() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void doneMerge(final Version pVersion) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Version getBaseVersion() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getCorrespondingNodePath(final String workspaceName) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public NodeDefinition getDefinition() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getIndex() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Lock getLock() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public NodeType[] getMixinNodeTypes() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PropertyIterator getReferences() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public VersionHistory getVersionHistory() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean holdsLock() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isCheckedOut() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isLocked() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Lock lock(final boolean isDeep, final boolean isSessionScoped) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public NodeIterator merge(final String srcWorkspace, final boolean bestEffort) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void orderBefore(final String srcChildRelPath, final String destChildRelPath) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void removeMixin(final String mixinName) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void restore(final String versionName, final boolean removeExisting) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void restore(final Version version, final boolean removeExisting) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void restore(final Version version, final String relPath, final boolean removeExisting) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void restoreByLabel(final String versionLabel, final boolean removeExisting) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void unlock() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void update(final String srcWorkspaceName) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void followLifecycleTransition(final String transition) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String[] getAllowedLifecycleTransistions() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public NodeIterator getNodes(final String[] nameGlobs) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PropertyIterator getProperties(final String[] nameGlobs) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PropertyIterator getReferences(final String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public NodeIterator getSharedSet() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PropertyIterator getWeakReferences() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PropertyIterator getWeakReferences(final String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void removeShare() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void removeSharedSet() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setPrimaryType(final String pNodeTypeName) {
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/testing/mock/jcr/MockNodeType.java b/src/main/java/org/apache/sling/testing/mock/jcr/MockNodeType.java
new file mode 100644
index 0000000..7033df5
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/mock/jcr/MockNodeType.java
@@ -0,0 +1,155 @@
+/*
+ * 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.sling.testing.mock.jcr;
+
+import javax.jcr.Value;
+import javax.jcr.nodetype.NodeDefinition;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.nodetype.NodeTypeIterator;
+import javax.jcr.nodetype.PropertyDefinition;
+
+/**
+ * Mock {@link NodeType} implementation.
+ */
+class MockNodeType implements NodeType {
+
+ private final String name;
+
+ public MockNodeType(final String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String getName() {
+ return this.name;
+ }
+
+ @Override
+ public boolean isNodeType(final String nodeTypeName) {
+ // node type inheritance not supported
+ return this.name.equals(nodeTypeName);
+ }
+
+ // --- unsupported operations ---
+ @Override
+ public boolean canAddChildNode(final String childNodeName) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean canAddChildNode(final String childNodeName, final String nodeTypeName) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean canRemoveItem(final String itemName) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean canSetProperty(final String propertyName, final Value value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean canSetProperty(final String propertyName, final Value[] values) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public NodeDefinition[] getChildNodeDefinitions() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public NodeDefinition[] getDeclaredChildNodeDefinitions() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PropertyDefinition[] getDeclaredPropertyDefinitions() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public NodeType[] getDeclaredSupertypes() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getPrimaryItemName() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PropertyDefinition[] getPropertyDefinitions() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public NodeType[] getSupertypes() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean hasOrderableChildNodes() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isMixin() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean canRemoveNode(final String nodeName) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean canRemoveProperty(final String propertyName) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public NodeTypeIterator getDeclaredSubtypes() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public NodeTypeIterator getSubtypes() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String[] getDeclaredSupertypeNames() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isAbstract() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isQueryable() {
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/testing/mock/jcr/MockNodeTypeManager.java b/src/main/java/org/apache/sling/testing/mock/jcr/MockNodeTypeManager.java
new file mode 100644
index 0000000..edec788
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/mock/jcr/MockNodeTypeManager.java
@@ -0,0 +1,103 @@
+/*
+ * 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.sling.testing.mock.jcr;
+
+import javax.jcr.nodetype.NodeDefinitionTemplate;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.nodetype.NodeTypeDefinition;
+import javax.jcr.nodetype.NodeTypeIterator;
+import javax.jcr.nodetype.NodeTypeManager;
+import javax.jcr.nodetype.NodeTypeTemplate;
+import javax.jcr.nodetype.PropertyDefinitionTemplate;
+
+/**
+ * Mock {@link NodeTypeManager} implementation.
+ */
+class MockNodeTypeManager implements NodeTypeManager {
+
+ @Override
+ public NodeType getNodeType(String nodeTypeName) {
+ // accept all node types and return a mock
+ return new MockNodeType(nodeTypeName);
+ }
+
+ @Override
+ public boolean hasNodeType(String name) {
+ // accept all node types
+ return true;
+ }
+
+ // --- unsupported operations ---
+
+ @Override
+ public NodeTypeIterator getAllNodeTypes() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public NodeTypeIterator getPrimaryNodeTypes() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public NodeTypeIterator getMixinNodeTypes() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public NodeTypeTemplate createNodeTypeTemplate() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public NodeTypeTemplate createNodeTypeTemplate(NodeTypeDefinition ntd) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public NodeDefinitionTemplate createNodeDefinitionTemplate() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PropertyDefinitionTemplate createPropertyDefinitionTemplate() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public NodeType registerNodeType(NodeTypeDefinition ntd, boolean allowUpdate) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public NodeTypeIterator registerNodeTypes(NodeTypeDefinition[] ntds, boolean allowUpdate) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void unregisterNodeType(String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void unregisterNodeTypes(String[] names) {
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/testing/mock/jcr/MockNodeTypes.java b/src/main/java/org/apache/sling/testing/mock/jcr/MockNodeTypes.java
new file mode 100644
index 0000000..1b081b6
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/mock/jcr/MockNodeTypes.java
@@ -0,0 +1,49 @@
+/*
+ * 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.sling.testing.mock.jcr;
+
+import javax.jcr.nodetype.NodeType;
+
+import org.apache.jackrabbit.JcrConstants;
+
+/**
+ * Collection of mocked node type instances.
+ */
+public final class MockNodeTypes {
+
+ private MockNodeTypes() {
+ // constants only
+ }
+
+ /**
+ * Node type NT_UNSTRUCTURED
+ */
+ public static final NodeType NT_UNSTRUCTURED = new MockNodeType(JcrConstants.NT_UNSTRUCTURED);
+
+ /**
+ * Node type NT_FOLDER
+ */
+ public static final NodeType NT_FOLDER = new MockNodeType(JcrConstants.NT_FOLDER);
+
+ /**
+ * Node type NT_FILE
+ */
+ public static final NodeType NT_FILE = new MockNodeType(JcrConstants.NT_FILE);
+
+}
diff --git a/src/main/java/org/apache/sling/testing/mock/jcr/MockObservationManager.java b/src/main/java/org/apache/sling/testing/mock/jcr/MockObservationManager.java
new file mode 100644
index 0000000..4988fa0
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/mock/jcr/MockObservationManager.java
@@ -0,0 +1,65 @@
+/*
+ * 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.sling.testing.mock.jcr;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.observation.EventJournal;
+import javax.jcr.observation.EventListener;
+import javax.jcr.observation.EventListenerIterator;
+import javax.jcr.observation.ObservationManager;
+
+/**
+ * Mock {@link ObservationManager} implementation.
+ */
+class MockObservationManager implements ObservationManager {
+
+ @Override
+ public void addEventListener(final EventListener listener, final int eventTypes, final String absPath,
+ final boolean isDeep, final String[] uuid, final String[] nodeTypeName, final boolean noLocal) {
+ // do nothing
+ }
+
+ @Override
+ public void removeEventListener(final EventListener listener) {
+ // do nothing
+ }
+
+ // --- unsupported operations ---
+ @Override
+ public EventListenerIterator getRegisteredEventListeners() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setUserData(final String userData) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public EventJournal getEventJournal() throws RepositoryException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public EventJournal getEventJournal(final int eventTypes, final String absPath, final boolean isDeep,
+ final String[] uuid, final String[] nodeTypeName) {
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/testing/mock/jcr/MockProperty.java b/src/main/java/org/apache/sling/testing/mock/jcr/MockProperty.java
new file mode 100644
index 0000000..468a840
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/mock/jcr/MockProperty.java
@@ -0,0 +1,306 @@
+/*
+ * 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.sling.testing.mock.jcr;
+
+import java.io.InputStream;
+import java.math.BigDecimal;
+import java.util.Calendar;
+
+import javax.jcr.Binary;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+import javax.jcr.ValueFormatException;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.nodetype.PropertyDefinition;
+
+import org.apache.jackrabbit.value.BinaryValue;
+
+/**
+ * Mock {@link Property} implementation
+ */
+class MockProperty extends AbstractItem implements Property {
+
+ private Value[] values;
+ private boolean isMultiple;
+
+ public MockProperty(final String path, final Session session) throws RepositoryException {
+ super(path, session);
+ this.values = new Value[] { getSession().getValueFactory().createValue("") };
+ }
+
+ private Value internalGetValue() throws ValueFormatException {
+ if (this.values.length > 1) {
+ throw new ValueFormatException(this
+ + " is a multi-valued property, so it's values can only be retrieved as an array");
+ } else {
+ return this.values[0];
+ }
+ }
+
+ @Override
+ public Value getValue() throws ValueFormatException {
+ return internalGetValue();
+ }
+
+ @Override
+ public Value[] getValues() {
+ Value[] valuesCopy = new Value[this.values.length];
+ for (int i = 0; i < this.values.length; i++) {
+ valuesCopy[i] = this.values[i];
+ }
+ return valuesCopy;
+ }
+
+ @Override
+ public void setValue(final Value newValue) {
+ this.values = new Value[] { newValue };
+ this.isMultiple = false;
+ }
+
+ @Override
+ public void setValue(final Value[] newValues) {
+ this.values = new Value[newValues.length];
+ for (int i = 0; i < newValues.length; i++) {
+ this.values[i] = newValues[i];
+ }
+ this.isMultiple = true;
+ }
+
+ @Override
+ public void setValue(final String newValue) throws RepositoryException {
+ this.values = new Value[] { getSession().getValueFactory().createValue(newValue) };
+ this.isMultiple = false;
+ }
+
+ @Override
+ public void setValue(final String[] newValues) throws RepositoryException {
+ this.values = new Value[newValues.length];
+ for (int i = 0; i < newValues.length; i++) {
+ this.values[i] = getSession().getValueFactory().createValue(newValues[i]);
+ }
+ this.isMultiple = true;
+ }
+
+ @Override
+ public void setValue(final InputStream newValue) throws RepositoryException {
+ this.values = new Value[] { new BinaryValue(newValue) };
+ this.isMultiple = false;
+ }
+
+ @Override
+ public void setValue(final long newValue) throws RepositoryException {
+ this.values = new Value[] { getSession().getValueFactory().createValue(newValue) };
+ this.isMultiple = false;
+ }
+
+ @Override
+ public void setValue(final double newValue) throws RepositoryException {
+ this.values = new Value[] { getSession().getValueFactory().createValue(newValue) };
+ this.isMultiple = false;
+ }
+
+ @Override
+ public void setValue(final Calendar newValue) throws RepositoryException {
+ this.values = new Value[] { getSession().getValueFactory().createValue(newValue) };
+ this.isMultiple = false;
+ }
+
+ @Override
+ public void setValue(final boolean newValue) throws RepositoryException {
+ this.values = new Value[] { getSession().getValueFactory().createValue(newValue) };
+ this.isMultiple = false;
+ }
+
+ @Override
+ public void setValue(final Node newValue) throws RepositoryException {
+ this.values = new Value[] { getSession().getValueFactory().createValue(newValue) };
+ this.isMultiple = false;
+ }
+
+ @Override
+ public void setValue(final Binary newValue) throws RepositoryException {
+ this.values = new Value[] { new BinaryValue(newValue) };
+ this.isMultiple = false;
+ }
+
+ @Override
+ public void setValue(final BigDecimal newValue) throws RepositoryException {
+ this.values = new Value[] { getSession().getValueFactory().createValue(newValue) };
+ this.isMultiple = false;
+ }
+
+ @Override
+ public boolean getBoolean() throws RepositoryException {
+ return internalGetValue().getBoolean();
+ }
+
+ @Override
+ public Calendar getDate() throws RepositoryException {
+ return internalGetValue().getDate();
+ }
+
+ @Override
+ public double getDouble() throws RepositoryException {
+ return internalGetValue().getDouble();
+ }
+
+ @Override
+ public long getLong() throws RepositoryException {
+ return internalGetValue().getLong();
+ }
+
+ @Override
+ public String getString() throws RepositoryException {
+ return internalGetValue().getString();
+ }
+
+ @Override
+ @SuppressWarnings("deprecation")
+ public InputStream getStream() throws RepositoryException {
+ return internalGetValue().getStream();
+ }
+
+ @Override
+ public Binary getBinary() throws RepositoryException {
+ return internalGetValue().getBinary();
+ }
+
+ @Override
+ public BigDecimal getDecimal() throws RepositoryException {
+ return internalGetValue().getDecimal();
+ }
+
+ @Override
+ public int getType() throws RepositoryException {
+ return this.values[0].getType();
+ }
+
+ @Override
+ public long getLength() throws RepositoryException {
+ return getValue().getString().length();
+ }
+
+ @Override
+ public long[] getLengths() throws RepositoryException {
+ long[] lengths = new long[this.values.length];
+ for (int i = 0; i < this.values.length; i++) {
+ lengths[i] = this.values[i].getString().length();
+ }
+ return lengths;
+ }
+
+ @Override
+ public boolean isNode() {
+ return false;
+ }
+
+ @Override
+ public boolean isMultiple() {
+ return this.isMultiple;
+ }
+
+ @Override
+ public PropertyDefinition getDefinition() {
+ return new MockPropertyDefinition();
+ }
+
+ // --- unsupported operations ---
+ @Override
+ public Node getNode() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Property getProperty() {
+ throw new UnsupportedOperationException();
+ }
+
+ private final class MockPropertyDefinition implements PropertyDefinition {
+
+ @Override
+ public boolean isMultiple() {
+ return MockProperty.this.isMultiple();
+ }
+
+ // --- unsupported operations ---
+ @Override
+ public Value[] getDefaultValues() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getRequiredType() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String[] getValueConstraints() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public NodeType getDeclaringNodeType() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getName() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getOnParentVersion() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isAutoCreated() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isMandatory() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isProtected() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String[] getAvailableQueryOperators() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isFullTextSearchable() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isQueryOrderable() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/testing/mock/jcr/MockRepository.java b/src/main/java/org/apache/sling/testing/mock/jcr/MockRepository.java
new file mode 100644
index 0000000..324820e
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/mock/jcr/MockRepository.java
@@ -0,0 +1,85 @@
+/*
+ * 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.sling.testing.mock.jcr;
+
+import javax.jcr.Credentials;
+import javax.jcr.Repository;
+import javax.jcr.Session;
+import javax.jcr.Value;
+
+import org.apache.commons.lang3.ArrayUtils;
+
+/**
+ * Mock {@link Repository} implementation. The data is stored inside the mocked
+ * session, not the repository - so it is not possible to open multiple session
+ * to access the same data in this mock implementation.
+ */
+class MockRepository implements Repository {
+
+ @Override
+ public Session login() {
+ return new MockSession(this);
+ }
+
+ @Override
+ public Session login(final String workspaceName) {
+ return login();
+ }
+
+ @Override
+ public Session login(final Credentials credentials) {
+ return login();
+ }
+
+ @Override
+ public Session login(final Credentials credentials, final String workspaceName) {
+ return login();
+ }
+
+ @Override
+ public String[] getDescriptorKeys() {
+ return ArrayUtils.EMPTY_STRING_ARRAY;
+ }
+
+ @Override
+ public boolean isStandardDescriptor(final String key) {
+ return false;
+ }
+
+ @Override
+ public boolean isSingleValueDescriptor(final String key) {
+ return false;
+ }
+
+ @Override
+ public Value getDescriptorValue(final String key) {
+ return null;
+ }
+
+ @Override
+ public Value[] getDescriptorValues(final String key) { // NOPMD
+ return null; // NOPMD
+ }
+
+ @Override
+ public String getDescriptor(final String key) {
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/testing/mock/jcr/MockSession.java b/src/main/java/org/apache/sling/testing/mock/jcr/MockSession.java
new file mode 100644
index 0000000..5567318
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/mock/jcr/MockSession.java
@@ -0,0 +1,354 @@
+/*
+ * 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.sling.testing.mock.jcr;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import javax.jcr.Credentials;
+import javax.jcr.Item;
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.Property;
+import javax.jcr.RangeIterator;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.ValueFactory;
+import javax.jcr.Workspace;
+import javax.jcr.retention.RetentionManager;
+import javax.jcr.security.AccessControlManager;
+
+import org.apache.jackrabbit.commons.iterator.RangeIteratorAdapter;
+import org.apache.jackrabbit.value.ValueFactoryImpl;
+import org.xml.sax.ContentHandler;
+
+/**
+ * Mock {@link Session} implementation. This instance holds the JCR data in a
+ * simple ordered map.
+ */
+class MockSession implements Session {
+
+ private final Repository repository;
+ private final Workspace workspace;
+
+ // Use linked hashmap to ensure ordering when adding items is preserved.
+ private final Map<String, Item> items = new LinkedHashMap<String, Item>();
+
+ public MockSession(final Repository repository) {
+ this.repository = repository;
+ this.workspace = new MockWorkspace(this);
+ this.items.put("/", new MockNode("/", this, MockNodeTypes.NT_UNSTRUCTURED));
+ }
+
+ @Override
+ public ValueFactory getValueFactory() {
+ return ValueFactoryImpl.getInstance();
+ }
+
+ @Override
+ public Item getItem(final String absPath) throws RepositoryException {
+ Item item = this.items.get(absPath);
+ if (item != null) {
+ return item;
+ } else {
+ throw new PathNotFoundException(String.format("No item found at: %s.", absPath));
+ }
+ }
+
+ @Override
+ public Node getNode(final String absPath) throws RepositoryException {
+ Item item = getItem(absPath);
+ if (item instanceof Node) {
+ return (Node) item;
+ } else {
+ throw new PathNotFoundException(String.format("No node found at: %s.", absPath));
+ }
+ }
+
+ @Override
+ public Node getNodeByIdentifier(final String id) throws RepositoryException {
+ for (Item item : this.items.values()) {
+ if (item instanceof Node) {
+ Node node = (Node) item;
+ if (node.getIdentifier().equals(id)) {
+ return node;
+ }
+ }
+ }
+ throw new ItemNotFoundException(String.format("No node found with id: %s.", id));
+ }
+
+ @Override
+ public Property getProperty(final String absPath) throws RepositoryException {
+ Item item = getItem(absPath);
+ if (item instanceof Property) {
+ return (Property) item;
+ } else {
+ throw new PathNotFoundException(String.format("No property found at: %s.", absPath));
+ }
+ }
+
+ @Override
+ public boolean nodeExists(final String absPath) throws RepositoryException {
+ try {
+ getNode(absPath);
+ return true;
+ } catch (PathNotFoundException ex) {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean propertyExists(final String absPath) throws RepositoryException {
+ try {
+ getProperty(absPath);
+ return true;
+ } catch (PathNotFoundException ex) {
+ return false;
+ }
+ }
+
+ @Override
+ public void removeItem(final String absPath) {
+ removeItemWithChildren(absPath);
+ }
+
+ @Override
+ public Node getRootNode() {
+ return (Node) this.items.get("/");
+ }
+
+ @Override
+ public Node getNodeByUUID(final String uuid) throws RepositoryException {
+ return getNodeByIdentifier(uuid);
+ }
+
+ /**
+ * Add item
+ * @param item item
+ * @throws RepositoryException
+ */
+ void addItem(final Item item) throws RepositoryException {
+ this.items.put(item.getPath(), item);
+ }
+
+ /**
+ * Remove item incl. children
+ * @param path Item path
+ */
+ void removeItemWithChildren(final String path) {
+ List<String> pathsToRemove = new ArrayList<String>();
+
+ // build regex pattern for node and all its children
+ Pattern pattern = Pattern.compile("^" + Pattern.quote(path) + "(/.+)?$");
+
+ for (String itemPath : this.items.keySet()) {
+ if (pattern.matcher(itemPath).matches()) {
+ pathsToRemove.add(itemPath);
+ }
+ }
+ for (String pathToRemove : pathsToRemove) {
+ this.items.remove(pathToRemove);
+ }
+ }
+
+ RangeIterator listChildren(final String parentPath, final ItemFilter filter) throws RepositoryException {
+ List<Item> children = new ArrayList<Item>();
+
+ // build regex pattern for all child paths of parent
+ Pattern pattern = Pattern.compile("^" + Pattern.quote(parentPath) + "/[^/]+$");
+
+ // collect child resources
+ for (Item item : this.items.values()) {
+ if (pattern.matcher(item.getPath()).matches() && (filter == null || filter.accept(item))) {
+ children.add(item);
+ }
+ }
+
+ return new RangeIteratorAdapter(children.iterator(), children.size());
+ }
+
+ @Override
+ public boolean hasPendingChanges() {
+ return false;
+ }
+
+ @Override
+ public boolean itemExists(final String absPath) {
+ return this.items.get(absPath) != null;
+ }
+
+ @Override
+ public Workspace getWorkspace() {
+ return this.workspace;
+ }
+
+ @Override
+ public String getUserID() {
+ return "mockedUserId";
+ }
+
+ @Override
+ public String getNamespacePrefix(final String uri) throws RepositoryException {
+ return getWorkspace().getNamespaceRegistry().getPrefix(uri);
+ }
+
+ @Override
+ public String[] getNamespacePrefixes() throws RepositoryException {
+ return getWorkspace().getNamespaceRegistry().getPrefixes();
+ }
+
+ @Override
+ public String getNamespaceURI(final String prefix) throws RepositoryException {
+ return getWorkspace().getNamespaceRegistry().getURI(prefix);
+ }
+
+ @Override
+ public void setNamespacePrefix(final String prefix, final String uri) throws RepositoryException {
+ getWorkspace().getNamespaceRegistry().registerNamespace(prefix, uri);
+ }
+
+ @Override
+ public Repository getRepository() {
+ return this.repository;
+ }
+
+ @Override
+ public void save() {
+ // do nothing
+ }
+
+ @Override
+ public void refresh(final boolean keepChanges) throws RepositoryException {
+ // do nothing
+ }
+
+ @Override
+ public void checkPermission(final String absPath, final String actions) {
+ // always grant permission
+ }
+
+ // --- unsupported operations ---
+ @Override
+ public void addLockToken(final String lt) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void exportDocumentView(final String absPath, final ContentHandler contentHandler, final boolean skipBinary,
+ final boolean noRecurse) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void exportDocumentView(final String absPath, final OutputStream out, final boolean skipBinary,
+ final boolean noRecurse) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void exportSystemView(final String absPath, final ContentHandler contentHandler, final boolean skipBinary,
+ final boolean noRecurse) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void exportSystemView(final String absPath, final OutputStream out, final boolean skipBinary,
+ final boolean noRecurse) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Object getAttribute(final String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String[] getAttributeNames() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ContentHandler getImportContentHandler(final String parentAbsPath, final int uuidBehavior) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String[] getLockTokens() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Session impersonate(final Credentials credentials) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void importXML(final String parentAbsPath, final InputStream in, final int uuidBehavior) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isLive() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void logout() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void move(final String srcAbsPath, final String destAbsPath) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void removeLockToken(final String lt) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public AccessControlManager getAccessControlManager() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public RetentionManager getRetentionManager() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean hasCapability(final String methodName, final Object target, final Object[] arguments) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean hasPermission(final String absPath, final String actions) {
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/testing/mock/jcr/MockWorkspace.java b/src/main/java/org/apache/sling/testing/mock/jcr/MockWorkspace.java
new file mode 100644
index 0000000..6996103
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/mock/jcr/MockWorkspace.java
@@ -0,0 +1,149 @@
+/*
+ * 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.sling.testing.mock.jcr;
+
+import java.io.InputStream;
+
+import javax.jcr.NamespaceRegistry;
+import javax.jcr.Session;
+import javax.jcr.Workspace;
+import javax.jcr.lock.LockManager;
+import javax.jcr.nodetype.NodeTypeManager;
+import javax.jcr.observation.ObservationManager;
+import javax.jcr.query.QueryManager;
+import javax.jcr.version.Version;
+import javax.jcr.version.VersionManager;
+
+import org.xml.sax.ContentHandler;
+
+/**
+ * Mock {@link Workspace} implementation
+ */
+class MockWorkspace implements Workspace {
+
+ private final Session session;
+ private final NamespaceRegistry namespaceRegistry = new MockNamespaceRegistry();
+ private final ObservationManager observationManager = new MockObservationManager();
+ private final NodeTypeManager nodeTypeManager = new MockNodeTypeManager();
+
+ /**
+ * @param session JCR session
+ */
+ public MockWorkspace(final Session session) {
+ this.session = session;
+ }
+
+ @Override
+ public Session getSession() {
+ return this.session;
+ }
+
+ @Override
+ public String getName() {
+ return MockJcr.DEFAULT_WORKSPACE;
+ }
+
+ @Override
+ public NamespaceRegistry getNamespaceRegistry() {
+ return this.namespaceRegistry;
+ }
+
+ @Override
+ public ObservationManager getObservationManager() {
+ return this.observationManager;
+ }
+
+ @Override
+ public NodeTypeManager getNodeTypeManager() {
+ return this.nodeTypeManager;
+ }
+
+ // --- unsupported operations ---
+ @Override
+ public void copy(final String srcAbsPath, final String destAbsPath) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void copy(final String srcWorkspace, final String srcAbsPath, final String destAbsPath) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void clone(final String srcWorkspace, final String srcAbsPath, final String destAbsPath,
+ final boolean removeExisting) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void move(final String srcAbsPath, final String destAbsPath) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void restore(final Version[] versions, final boolean removeExisting) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public LockManager getLockManager() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public QueryManager getQueryManager() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public VersionManager getVersionManager() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String[] getAccessibleWorkspaceNames() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ContentHandler getImportContentHandler(final String parentAbsPath, final int uuidBehavior) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void importXML(final String parentAbsPath, final InputStream in, final int uuidBehavior) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void createWorkspace(final String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void createWorkspace(final String name, final String srcWorkspace) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void deleteWorkspace(final String name) {
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/testing/mock/jcr/ResourceUtil.java b/src/main/java/org/apache/sling/testing/mock/jcr/ResourceUtil.java
new file mode 100644
index 0000000..f1488bb
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/mock/jcr/ResourceUtil.java
@@ -0,0 +1,219 @@
+/*
+ * 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.sling.testing.mock.jcr;
+
+/**
+ * This is a stripped-down copy of org.apache.sling.api.resource.ResourceUtil
+ * with some methods required by the jcr-mock implementation internally.
+ */
+class ResourceUtil {
+
+ /**
+ * Resolves relative path segments '.' and '..' in the absolute path.
+ * Returns null if not possible (.. points above root) or if path is not
+ * absolute.
+ */
+ public static String normalize(String path) {
+
+ // don't care for empty paths
+ if (path.length() == 0) {
+ return path;
+ }
+
+ // prepare the path buffer with trailing slash (simplifies impl)
+ int absOffset = (path.charAt(0) == '/') ? 0 : 1;
+ char[] buf = new char[path.length() + 1 + absOffset];
+ if (absOffset == 1) {
+ buf[0] = '/';
+ }
+ path.getChars(0, path.length(), buf, absOffset);
+ buf[buf.length - 1] = '/';
+
+ int lastSlash = 0; // last slash in path
+ int numDots = 0; // number of consecutive dots after last slash
+
+ int bufPos = 0;
+ for (int bufIdx = lastSlash; bufIdx < buf.length; bufIdx++) {
+ char c = buf[bufIdx];
+ if (c == '/') {
+ if (numDots == 2) {
+ if (bufPos == 0) {
+ return null;
+ }
+
+ do {
+ bufPos--;
+ } while (bufPos > 0 && buf[bufPos] != '/');
+ }
+
+ lastSlash = bufIdx;
+ numDots = 0;
+ } else if (c == '.' && numDots < 2) {
+ numDots++;
+ } else {
+ // find the next slash
+ int nextSlash = bufIdx + 1;
+ while (nextSlash < buf.length && buf[nextSlash] != '/') {
+ nextSlash++;
+ }
+
+ // append up to the next slash (or end of path)
+ if (bufPos < lastSlash) {
+ int segLen = nextSlash - bufIdx + 1;
+ System.arraycopy(buf, lastSlash, buf, bufPos, segLen);
+ bufPos += segLen;
+ } else {
+ bufPos = nextSlash;
+ }
+
+ numDots = 0;
+ lastSlash = nextSlash;
+ bufIdx = nextSlash;
+ }
+ }
+
+ String resolved;
+ if (bufPos == 0 && numDots == 0) {
+ resolved = (absOffset == 0) ? "/" : "";
+ } else if ((bufPos - absOffset) == path.length()) {
+ resolved = path;
+ } else {
+ resolved = new String(buf, absOffset, bufPos - absOffset);
+ }
+
+ return resolved;
+ }
+
+ /**
+ * Utility method returns the parent path of the given <code>path</code>,
+ * which is normalized by {@link #normalize(String)} before resolving the
+ * parent.
+ *
+ * @param path The path whose parent is to be returned.
+ * @return <code>null</code> if <code>path</code> is the root path (
+ * <code>/</code>) or if <code>path</code> is a single name
+ * containing no slash (<code>/</code>) characters.
+ * @throws IllegalArgumentException If the path cannot be normalized by the
+ * {@link #normalize(String)} method.
+ * @throws NullPointerException If <code>path</code> is <code>null</code>.
+ */
+ public static String getParent(String path) {
+ if ("/".equals(path)) {
+ return null;
+ }
+
+ // normalize path (remove . and ..)
+ path = normalize(path);
+
+ // if normalized to root, there is no parent
+ if (path == null || "/".equals(path)) {
+ return null;
+ }
+
+ String workspaceName = null;
+
+ final int wsSepPos = path.indexOf(":/");
+ if (wsSepPos != -1) {
+ workspaceName = path.substring(0, wsSepPos);
+ path = path.substring(wsSepPos + 1);
+ }
+
+ // find the last slash, after which to cut off
+ int lastSlash = path.lastIndexOf('/');
+ if (lastSlash < 0) {
+ // no slash in the path
+ return null;
+ } else if (lastSlash == 0) {
+ // parent is root
+ if (workspaceName != null) {
+ return workspaceName + ":/";
+ }
+ return "/";
+ }
+
+ String parentPath = path.substring(0, lastSlash);
+ if (workspaceName != null) {
+ return workspaceName + ":" + parentPath;
+ }
+ return parentPath;
+ }
+
+ /**
+ * Utility method returns the ancestor's path at the given <code>level</code>
+ * relative to <code>path</code>, which is normalized by {@link #normalize(String)}
+ * before resolving the ancestor.
+ *
+ * <ul>
+ * <li><code>level</code> = 0 returns the <code>path</code>.</li>
+ * <li><code>level</code> = 1 returns the parent of <code>path</code>, if it exists, <code>null</code> otherwise.</li>
+ * <li><code>level</code> = 2 returns the grandparent of <code>path</code>, if it exists, <code>null</code> otherwise.</li>
+ * </ul>
+ *
+ * @param path The path whose ancestor is to be returned.
+ * @param level The relative level of the ancestor, relative to <code>path</code>.
+ * @return <code>null</code> if <code>path</code> doesn't have an ancestor at the
+ * specified <code>level</code>.
+ * @throws IllegalArgumentException If the path cannot be normalized by the
+ * {@link #normalize(String)} method or if <code>level</code> < 0.
+ * @throws NullPointerException If <code>path</code> is <code>null</code>.
+ * @since 2.2
+ */
+ public static String getParent(final String path, final int level) {
+ if ( level < 0 ) {
+ throw new IllegalArgumentException("level must be non-negative");
+ }
+ String result = path;
+ for(int i=0; i<level; i++) {
+ result = getParent(result);
+ if ( result == null ) {
+ break;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Utility method returns the name of the given <code>path</code>, which is
+ * normalized by {@link #normalize(String)} before resolving the name.
+ *
+ * @param path The path whose name (the last path element) is to be
+ * returned.
+ * @return The empty string if <code>path</code> is the root path (
+ * <code>/</code>) or if <code>path</code> is a single name
+ * containing no slash (<code>/</code>) characters.
+ * @throws IllegalArgumentException If the path cannot be normalized by the
+ * {@link #normalize(String)} method.
+ * @throws NullPointerException If <code>path</code> is <code>null</code>.
+ */
+ public static String getName(String path) {
+ if ("/".equals(path)) {
+ return "";
+ }
+
+ // normalize path (remove . and ..)
+ path = normalize(path);
+ if ("/".equals(path)) {
+ return "";
+ }
+
+ // find the last slash
+ return path.substring(path.lastIndexOf('/') + 1);
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/testing/mock/jcr/package-info.java b/src/main/java/org/apache/sling/testing/mock/jcr/package-info.java
new file mode 100644
index 0000000..cf6e2e7
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/mock/jcr/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+/**
+ * Mock implementation of selected JCR APIs.
+ */
+package org.apache.sling.testing.mock.jcr;
+
diff --git a/src/site/markdown/index.md b/src/site/markdown/index.md
new file mode 100644
index 0000000..544541e
--- /dev/null
+++ b/src/site/markdown/index.md
@@ -0,0 +1,38 @@
+## About JCR Mocks
+
+Mock implementation of selected JCR APIs.
+
+### Maven Dependency
+
+```xml
+<dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.testing.jcr-mock</artifactId>
+ <version>1.0.0-SNAPHOT</version>
+</dependency>
+```
+
+### Documentation
+
+* [Usage](usage.html)
+* [API Documentation](apidocs/)
+* [Changelog](changes-report.html)
+
+### Implemented mock features
+
+The mock implementation supports:
+
+* Reading and writing all data (primitive values, arrays, binary data) via the JCR API
+* Creating any number of nodes and properties (stored in-memory in a hash map)
+* Register namespaces
+
+The following features are *not supported*:
+
+* Node types are supported in the API, but their definitions and constraints are not applied
+* Versioning not supported
+* Search not supported
+* Transactions not supported
+* Observation events can be registered but are ignored
+* Access control always grants access
+* Exporting/Importing data via document and system views not supported
+* Workspace management methods not supported
diff --git a/src/site/markdown/usage.md b/src/site/markdown/usage.md
new file mode 100644
index 0000000..9ae3ce7
--- /dev/null
+++ b/src/site/markdown/usage.md
@@ -0,0 +1,17 @@
+## Usage
+
+### Getting JCR mock objects
+
+The factory class `MockJcr` allows to instantiate the different mock implementations.
+
+Example:
+
+```java
+// get session
+Session session = MockJcr.newSession();
+
+// get repository
+Repository repository = MockJcr.newRepository();
+```
+
+The repository is empty and contains only the root node. You can use the JCR API to fill it with content.
diff --git a/src/test/java/org/apache/sling/testing/mock/jcr/AbstractItemTest.java b/src/test/java/org/apache/sling/testing/mock/jcr/AbstractItemTest.java
new file mode 100644
index 0000000..e7bd268
--- /dev/null
+++ b/src/test/java/org/apache/sling/testing/mock/jcr/AbstractItemTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.sling.testing.mock.jcr;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public abstract class AbstractItemTest {
+
+ private Session session;
+ private Node rootNode;
+ private Node node1;
+ private Property prop1;
+ private Node node11;
+
+ @Before
+ public void setUp() throws RepositoryException {
+ this.session = MockJcr.newSession();
+ this.rootNode = this.session.getRootNode();
+ this.node1 = this.rootNode.addNode("node1");
+ this.prop1 = this.node1.setProperty("prop1", "value1");
+ this.node11 = this.node1.addNode("node11");
+ }
+
+ @Test
+ public void testGetName() throws RepositoryException {
+ assertEquals("node1", this.node1.getName());
+ assertEquals("prop1", this.prop1.getName());
+ }
+
+ @Test
+ public void testGetParent() throws RepositoryException {
+ assertSame(this.rootNode, this.node1.getParent());
+ assertSame(this.node1, this.prop1.getParent());
+ assertSame(this.node1, this.node11.getParent());
+ }
+
+ @Test
+ public void testGetAncestor() throws RepositoryException {
+ assertSame(this.node11, this.node11.getAncestor(0));
+ assertSame(this.node1, this.node11.getAncestor(1));
+ assertSame(this.rootNode, this.node11.getAncestor(2));
+ }
+
+ @Test(expected = ItemNotFoundException.class)
+ public void testGetAncestorNegative() throws RepositoryException {
+ assertSame(this.node11, this.node11.getAncestor(-1));
+ }
+
+ @Test(expected = ItemNotFoundException.class)
+ public void testGetAncestorTooDeep() throws RepositoryException {
+ this.node11.getAncestor(3);
+ }
+
+ @Test
+ public void testGetDepth() throws RepositoryException {
+ assertEquals(2, this.node11.getDepth());
+ assertEquals(1, this.node1.getDepth());
+ assertEquals(0, this.rootNode.getDepth());
+ }
+
+ @Test
+ public void testModifiedNew() {
+ // methods return always false
+ assertFalse(this.node1.isModified());
+ assertFalse(this.node1.isNew());
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/testing/mock/jcr/MockNodeTest.java b/src/test/java/org/apache/sling/testing/mock/jcr/MockNodeTest.java
new file mode 100644
index 0000000..f02df0e
--- /dev/null
+++ b/src/test/java/org/apache/sling/testing/mock/jcr/MockNodeTest.java
@@ -0,0 +1,129 @@
+/*
+ * 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.sling.testing.mock.jcr;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.jackrabbit.JcrConstants;
+import org.junit.Before;
+import org.junit.Test;
+
+public class MockNodeTest {
+
+ private Session session;
+ private Node rootNode;
+ private Node node1;
+ private Property prop1;
+ private Node node11;
+
+ @Before
+ public void setUp() throws RepositoryException {
+ this.session = MockJcr.newSession();
+ this.rootNode = this.session.getRootNode();
+ this.node1 = this.rootNode.addNode("node1");
+ this.prop1 = this.node1.setProperty("prop1", "value1");
+ this.node11 = this.node1.addNode("node11");
+ }
+
+ @Test
+ public void testGetNodes() throws RepositoryException {
+ NodeIterator nodes = this.node1.getNodes();
+ assertEquals(1, nodes.getSize());
+ assertSame(this.node11, nodes.next());
+
+ assertTrue(this.node1.hasNodes());
+ assertFalse(this.node11.hasNodes());
+
+ nodes = this.node1.getNodes("^node.*$");
+ assertEquals(1, nodes.getSize());
+ assertSame(this.node11, nodes.next());
+
+ nodes = this.node1.getNodes("unknown?");
+ assertEquals(0, nodes.getSize());
+ }
+
+ @Test
+ public void testGetProperties() throws RepositoryException {
+ PropertyIterator properties = this.node1.getProperties();
+ assertEquals(1, properties.getSize());
+ assertSame(this.prop1, properties.next());
+
+ assertTrue(this.node1.hasProperties());
+ assertFalse(this.node11.hasProperties());
+
+ properties = this.node1.getProperties("^prop.*$");
+ assertEquals(1, properties.getSize());
+ assertSame(this.prop1, properties.next());
+
+ properties = this.node1.getProperties("unknown?");
+ assertEquals(0, properties.getSize());
+ }
+
+ @Test
+ public void testIsNode() {
+ assertTrue(this.node1.isNode());
+ assertFalse(this.prop1.isNode());
+ }
+
+ @Test
+ public void testHasNode() throws RepositoryException {
+ assertTrue(this.node1.hasNode("node11"));
+ assertFalse(this.node1.hasNode("node25"));
+ }
+
+ @Test
+ public void testHasProperty() throws RepositoryException {
+ assertTrue(this.node1.hasProperty("prop1"));
+ assertFalse(this.node1.hasProperty("prop25"));
+ }
+
+ @SuppressWarnings("deprecation")
+ @Test
+ public void testGetUUID() throws RepositoryException {
+ assertEquals(this.node1.getIdentifier(), this.node1.getUUID());
+ }
+
+ @Test
+ public void testGetPrimaryItem() throws RepositoryException {
+ Node dataParent = this.node1.addNode("dataParent");
+ Property dataProperty = dataParent.setProperty(JcrConstants.JCR_DATA, "data");
+ assertSame(dataProperty, dataParent.getPrimaryItem());
+
+ Node contentParent = this.node1.addNode("contentParent");
+ Node contentNode = contentParent.addNode(JcrConstants.JCR_CONTENT);
+ assertSame(contentNode, contentParent.getPrimaryItem());
+ }
+
+ @Test(expected = ItemNotFoundException.class)
+ public void testGetPrimaryItemNotFound() throws RepositoryException {
+ this.node1.getPrimaryItem();
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/testing/mock/jcr/MockPropertyTest.java b/src/test/java/org/apache/sling/testing/mock/jcr/MockPropertyTest.java
new file mode 100644
index 0000000..a8f30a4
--- /dev/null
+++ b/src/test/java/org/apache/sling/testing/mock/jcr/MockPropertyTest.java
@@ -0,0 +1,240 @@
+/*
+ * 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.sling.testing.mock.jcr;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.Calendar;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.jackrabbit.value.BinaryValue;
+import org.junit.Before;
+import org.junit.Test;
+
+public class MockPropertyTest {
+
+ private Session session;
+ private Node rootNode;
+ private Node node1;
+
+ @Before
+ public void setUp() throws RepositoryException {
+ this.session = MockJcr.newSession();
+ this.rootNode = this.session.getRootNode();
+ this.node1 = this.rootNode.addNode("node1");
+ }
+
+ @Test
+ public void testRemove() throws RepositoryException {
+ this.node1.setProperty("prop1", "value1");
+ Property prop1 = this.node1.getProperty("prop1");
+ assertEquals("value1", prop1.getString());
+
+ prop1.remove();
+ assertFalse(this.node1.hasProperty("prop1"));
+ }
+
+ @Test
+ public void testString() throws RepositoryException {
+ this.node1.setProperty("prop1", "value1");
+ Property prop1 = this.node1.getProperty("prop1");
+ assertEquals("value1", prop1.getString());
+ assertEquals("value1", prop1.getValue().getString());
+
+ prop1.setValue("value2");
+ assertEquals("value2", prop1.getString());
+ assertEquals("value2", prop1.getValue().getString());
+
+ assertFalse(prop1.isMultiple());
+ assertFalse(prop1.getDefinition().isMultiple());
+ assertEquals(6, prop1.getLength());
+ }
+
+ @Test
+ public void testStringArray() throws RepositoryException {
+ String[] value1 = new String[] { "aaa", "bbb" };
+ this.node1.setProperty("prop1", value1);
+ Property prop1 = this.node1.getProperty("prop1");
+
+ Value[] values = prop1.getValues();
+ for (int i = 0; i < values.length; i++) {
+ assertEquals("value #" + i, value1[i], values[i].getString());
+ }
+
+ String[] value2 = new String[] { "cc" };
+ prop1.setValue(value2);
+ values = prop1.getValues();
+ for (int i = 0; i < values.length; i++) {
+ assertEquals("value #" + i, value2[i], values[i].getString());
+ }
+
+ assertTrue(prop1.isMultiple());
+ assertTrue(prop1.getDefinition().isMultiple());
+ assertArrayEquals(new long[] { 2 }, prop1.getLengths());
+ }
+
+ @Test
+ public void testBoolean() throws RepositoryException {
+ this.node1.setProperty("prop1", true);
+ Property prop1 = this.node1.getProperty("prop1");
+ assertEquals(true, prop1.getBoolean());
+ assertEquals(true, prop1.getValue().getBoolean());
+
+ prop1.setValue(false);
+ assertEquals(false, prop1.getBoolean());
+ assertEquals(false, prop1.getValue().getBoolean());
+ }
+
+ @Test
+ public void testDouble() throws RepositoryException {
+ this.node1.setProperty("prop1", 1.5d);
+ Property prop1 = this.node1.getProperty("prop1");
+ assertEquals(1.5d, prop1.getDouble(), 0.001d);
+ assertEquals(1.5d, prop1.getValue().getDouble(), 0.001d);
+
+ prop1.setValue(Double.MAX_VALUE);
+ assertEquals(Double.MAX_VALUE, prop1.getDouble(), 0.001d);
+ assertEquals(Double.MAX_VALUE, prop1.getValue().getDouble(), 0.001d);
+ }
+
+ @Test
+ public void testLong() throws RepositoryException {
+ this.node1.setProperty("prop1", 5L);
+ Property prop1 = this.node1.getProperty("prop1");
+ assertEquals(5L, prop1.getLong());
+ assertEquals(5L, prop1.getValue().getLong());
+
+ prop1.setValue(Long.MAX_VALUE);
+ assertEquals(Long.MAX_VALUE, prop1.getLong());
+ assertEquals(Long.MAX_VALUE, prop1.getValue().getLong());
+ }
+
+ @Test
+ public void testBigDecimal() throws RepositoryException {
+ this.node1.setProperty("prop1", new BigDecimal("1.5"));
+ Property prop1 = this.node1.getProperty("prop1");
+ assertEquals(new BigDecimal("1.5"), prop1.getDecimal());
+ assertEquals(new BigDecimal("1.5"), prop1.getValue().getDecimal());
+
+ prop1.setValue(new BigDecimal("99999999.99999"));
+ assertEquals(new BigDecimal("99999999.99999"), prop1.getDecimal());
+ assertEquals(new BigDecimal("99999999.99999"), prop1.getValue().getDecimal());
+ }
+
+ @Test
+ public void testCalendar() throws RepositoryException {
+ Calendar value1 = Calendar.getInstance();
+
+ this.node1.setProperty("prop1", value1);
+ Property prop1 = this.node1.getProperty("prop1");
+ assertEquals(value1, prop1.getDate());
+ assertEquals(value1, prop1.getValue().getDate());
+
+ Calendar value2 = Calendar.getInstance();
+ value2.add(Calendar.MONTH, -1);
+
+ prop1.setValue(value2);
+ assertEquals(value2, prop1.getDate());
+ assertEquals(value2, prop1.getValue().getDate());
+ }
+
+ @Test
+ public void testBinary() throws RepositoryException, IOException {
+ byte[] value1 = new byte[] { 0x01, 0x01, 0x03 };
+
+ this.node1.setProperty("prop1", new BinaryValue(value1).getBinary());
+ Property prop1 = this.node1.getProperty("prop1");
+ assertArrayEquals(value1, IOUtils.toByteArray(prop1.getBinary().getStream()));
+ assertArrayEquals(value1, IOUtils.toByteArray(prop1.getValue().getBinary().getStream()));
+
+ byte[] value2 = new byte[] { 0x04, 0x05, 0x06 };
+
+ prop1.setValue(new BinaryValue(value2).getBinary());
+ assertArrayEquals(value2, IOUtils.toByteArray(prop1.getBinary().getStream()));
+ assertArrayEquals(value2, IOUtils.toByteArray(prop1.getValue().getBinary().getStream()));
+ }
+
+ @SuppressWarnings("deprecation")
+ @Test
+ public void testInputStream() throws RepositoryException, IOException {
+ byte[] value1 = new byte[] { 0x01, 0x01, 0x03 };
+
+ this.node1.setProperty("prop1", new ByteArrayInputStream(value1));
+ Property prop1 = this.node1.getProperty("prop1");
+ assertArrayEquals(value1, IOUtils.toByteArray(prop1.getStream()));
+
+ byte[] value2 = new byte[] { 0x04, 0x05, 0x06 };
+
+ prop1.setValue(new ByteArrayInputStream(value2));
+ assertArrayEquals(value2, IOUtils.toByteArray(prop1.getValue().getStream()));
+ }
+
+ @Test
+ public void testValue() throws RepositoryException {
+ this.node1.setProperty("prop1", this.session.getValueFactory().createValue("value1"));
+ Property prop1 = this.node1.getProperty("prop1");
+ assertEquals("value1", prop1.getString());
+ assertEquals("value1", prop1.getValue().getString());
+
+ prop1.setValue(this.session.getValueFactory().createValue("value2"));
+ assertEquals("value2", prop1.getString());
+ assertEquals("value2", prop1.getValue().getString());
+
+ assertFalse(prop1.isMultiple());
+ assertFalse(prop1.getDefinition().isMultiple());
+ assertEquals(6, prop1.getLength());
+ }
+
+ @Test
+ public void testValueArray() throws RepositoryException {
+ Value[] value1 = new Value[] { this.session.getValueFactory().createValue("aaa"),
+ this.session.getValueFactory().createValue("bbb") };
+ this.node1.setProperty("prop1", value1);
+ Property prop1 = this.node1.getProperty("prop1");
+
+ Value[] values = prop1.getValues();
+ for (int i = 0; i < values.length; i++) {
+ assertEquals("value #" + i, value1[i].getString(), values[i].getString());
+ }
+
+ Value[] value2 = new Value[] { this.session.getValueFactory().createValue("cc") };
+ prop1.setValue(value2);
+ values = prop1.getValues();
+ for (int i = 0; i < values.length; i++) {
+ assertEquals("value #" + i, value2[i].getString(), values[i].getString());
+ }
+
+ assertTrue(prop1.isMultiple());
+ assertTrue(prop1.getDefinition().isMultiple());
+ assertArrayEquals(new long[] { 2 }, prop1.getLengths());
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/testing/mock/jcr/MockRepositoryTest.java b/src/test/java/org/apache/sling/testing/mock/jcr/MockRepositoryTest.java
new file mode 100644
index 0000000..6143456
--- /dev/null
+++ b/src/test/java/org/apache/sling/testing/mock/jcr/MockRepositoryTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.sling.testing.mock.jcr;
+
+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 javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.SimpleCredentials;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class MockRepositoryTest {
+
+ private static final String USER_NAME = "user";
+ private static final char[] PASSWORD = "pwd".toCharArray();
+
+ private Repository repository;
+
+ @Before
+ public void setUp() {
+ this.repository = MockJcr.newRepository();
+ }
+
+ @Test
+ public void testLogin() throws RepositoryException {
+ assertNotNull(this.repository.login());
+ assertNotNull(this.repository.login(new SimpleCredentials(USER_NAME, PASSWORD)));
+ assertNotNull(this.repository.login(MockJcr.DEFAULT_WORKSPACE));
+ assertNotNull(this.repository.login(new SimpleCredentials(USER_NAME, PASSWORD), MockJcr.DEFAULT_WORKSPACE));
+ }
+
+ @Test
+ public void testDescriptor() {
+ assertEquals(0, this.repository.getDescriptorKeys().length);
+ assertNull(this.repository.getDescriptor("test"));
+ assertNull(this.repository.getDescriptorValue("test"));
+ assertNull(this.repository.getDescriptorValues("test"));
+ assertFalse(this.repository.isStandardDescriptor("test"));
+ assertFalse(this.repository.isSingleValueDescriptor("test"));
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/testing/mock/jcr/MockSessionTest.java b/src/test/java/org/apache/sling/testing/mock/jcr/MockSessionTest.java
new file mode 100644
index 0000000..4c297c1
--- /dev/null
+++ b/src/test/java/org/apache/sling/testing/mock/jcr/MockSessionTest.java
@@ -0,0 +1,228 @@
+/*
+ * 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.sling.testing.mock.jcr;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.NamespaceRegistry;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.jackrabbit.JcrConstants;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+public class MockSessionTest {
+
+ private Session session;
+
+ @Before
+ public void setUp() {
+ this.session = MockJcr.newSession();
+ }
+
+ @Test
+ public void testEmptySession() throws RepositoryException {
+ Node rootNode = this.session.getRootNode();
+ assertNotNull(rootNode);
+ assertFalse(rootNode.getProperties().hasNext());
+ assertFalse(rootNode.getNodes().hasNext());
+ }
+
+ @Test
+ public void testNodePropertyCreateRead() throws RepositoryException {
+ Node rootNode = this.session.getNode("/");
+ assertSame(rootNode, this.session.getRootNode());
+
+ Node node1 = rootNode.addNode("node1");
+ node1.setProperty("prop1a", "value1a");
+ node1.setProperty("prop1b", "value1b");
+
+ Node node2 = rootNode.addNode("node2");
+ node2.setProperty("prop2", "value2");
+
+ assertSame(node1, rootNode.getNode("node1"));
+ assertSame(node1, this.session.getNode("/node1"));
+ assertSame(node1, this.session.getItem("/node1"));
+ assertSame(node1, this.session.getNodeByIdentifier(node1.getIdentifier()));
+ assertTrue(this.session.nodeExists("/node1"));
+ assertTrue(this.session.itemExists("/node1"));
+ assertSame(node2, rootNode.getNode("node2"));
+ assertSame(node2, this.session.getNode("/node2"));
+ assertSame(node2, this.session.getItem("/node2"));
+ assertSame(node2, this.session.getNodeByIdentifier(node2.getIdentifier()));
+ assertTrue(this.session.nodeExists("/node2"));
+ assertTrue(this.session.itemExists("/node2"));
+
+ Property prop1a = node1.getProperty("prop1a");
+ Property prop1b = node1.getProperty("prop1b");
+ Property prop2 = node2.getProperty("prop2");
+
+ assertSame(prop1a, this.session.getProperty("/node1/prop1a"));
+ assertSame(prop1a, this.session.getItem("/node1/prop1a"));
+ assertTrue(this.session.propertyExists("/node1/prop1a"));
+ assertTrue(this.session.itemExists("/node1/prop1a"));
+ assertSame(prop1b, this.session.getProperty("/node1/prop1b"));
+ assertSame(prop1b, this.session.getItem("/node1/prop1b"));
+ assertTrue(this.session.propertyExists("/node1/prop1b"));
+ assertTrue(this.session.itemExists("/node1/prop1b"));
+ assertSame(prop2, this.session.getProperty("/node2/prop2"));
+ assertSame(prop2, this.session.getItem("/node2/prop2"));
+ assertTrue(this.session.propertyExists("/node2/prop2"));
+ assertTrue(this.session.itemExists("/node2/prop2"));
+
+ assertEquals("value1a", prop1a.getString());
+ assertEquals("value1b", prop1b.getString());
+ assertEquals("value2", prop2.getString());
+
+ assertFalse(this.session.propertyExists("/node1"));
+ assertFalse(this.session.nodeExists("/node1/prop1a"));
+
+ assertEquals(JcrConstants.NT_UNSTRUCTURED, node1.getPrimaryNodeType().getName());
+ assertTrue(node1.isNodeType(JcrConstants.NT_UNSTRUCTURED));
+ assertTrue(node1.getPrimaryNodeType().isNodeType(JcrConstants.NT_UNSTRUCTURED));
+
+ }
+
+ @Test
+ public void testNodeRemove() throws RepositoryException {
+ Node rootNode = this.session.getRootNode();
+ Node node1 = rootNode.addNode("node1");
+ assertTrue(this.session.itemExists("/node1"));
+ node1.remove();
+ assertFalse(this.session.itemExists("/node1"));
+ assertFalse(rootNode.getNodes().hasNext());
+ }
+
+ @Test
+ public void testNodesWithSpecialNames() throws RepositoryException {
+ Node rootNode = this.session.getRootNode();
+
+ Node node1 = rootNode.addNode("node1.ext");
+ Node node11 = node1.addNode("Node Name With Spaces");
+ node11.setProperty("prop11", "value11");
+ Node node12 = node1.addNode("node12_ext");
+ node12.setProperty("prop12", "value12");
+
+ assertTrue(this.session.itemExists("/node1.ext"));
+ assertTrue(this.session.itemExists("/node1.ext/Node Name With Spaces"));
+ assertTrue(this.session.itemExists("/node1.ext/node12_ext"));
+
+ assertEquals("value11", node11.getProperty("prop11").getString());
+ assertEquals("value12", node12.getProperty("prop12").getString());
+
+ NodeIterator nodes = node1.getNodes();
+ assertEquals(2, nodes.getSize());
+ }
+
+ @Test
+ public void testItemsExists() throws RepositoryException {
+ assertFalse(this.session.nodeExists("/node1"));
+ assertFalse(this.session.itemExists("/node2"));
+ assertFalse(this.session.propertyExists("/node1/prop1"));
+ }
+
+ @Test(expected = PathNotFoundException.class)
+ public void testNodeNotFoundException() throws RepositoryException {
+ this.session.getNode("/node1");
+ }
+
+ @Test(expected = PathNotFoundException.class)
+ public void testPropertyNotFoundException() throws RepositoryException {
+ this.session.getProperty("/node1/prop1");
+ }
+
+ @Test(expected = PathNotFoundException.class)
+ public void testItemNotFoundException() throws RepositoryException {
+ this.session.getItem("/node2");
+ }
+
+ @Test(expected = ItemNotFoundException.class)
+ public void testIdentifierFoundException() throws RepositoryException {
+ this.session.getNodeByIdentifier("unknown");
+ }
+
+ @Test
+ public void testNamespaces() throws RepositoryException {
+ // test initial namespaces
+ assertArrayEquals(new String[] { "jcr" }, this.session.getNamespacePrefixes());
+ assertEquals("http://www.jcp.org/jcr/1.0", this.session.getNamespaceURI("jcr"));
+ assertEquals("jcr", this.session.getNamespacePrefix("http://www.jcp.org/jcr/1.0"));
+
+ // add dummy namespace
+ this.session.setNamespacePrefix("dummy", "http://mydummy");
+
+ assertEquals(ImmutableSet.of("jcr", "dummy"), ImmutableSet.copyOf(this.session.getNamespacePrefixes()));
+ assertEquals("http://mydummy", this.session.getNamespaceURI("dummy"));
+ assertEquals("dummy", this.session.getNamespacePrefix("http://mydummy"));
+
+ // test via namespace registry
+ NamespaceRegistry namespaceRegistry = this.session.getWorkspace().getNamespaceRegistry();
+
+ assertEquals(ImmutableSet.of("jcr", "dummy"), ImmutableSet.copyOf(namespaceRegistry.getPrefixes()));
+ assertEquals(ImmutableSet.of("http://www.jcp.org/jcr/1.0", "http://mydummy"),
+ ImmutableSet.copyOf(namespaceRegistry.getURIs()));
+ assertEquals("http://mydummy", namespaceRegistry.getURI("dummy"));
+ assertEquals("dummy", namespaceRegistry.getPrefix("http://mydummy"));
+
+ // remove dummy namespace
+ namespaceRegistry.unregisterNamespace("dummy");
+
+ assertEquals(ImmutableSet.of("jcr"), ImmutableSet.copyOf(this.session.getNamespacePrefixes()));
+ assertEquals("http://www.jcp.org/jcr/1.0", this.session.getNamespaceURI("jcr"));
+ assertEquals("jcr", this.session.getNamespacePrefix("http://www.jcp.org/jcr/1.0"));
+ }
+
+ @Test
+ public void testUserId() {
+ assertEquals("mockedUserId", this.session.getUserID());
+ }
+
+ @Test
+ public void testSaveRefresh() throws RepositoryException {
+ // methods can be called without any effect
+ assertFalse(this.session.hasPendingChanges());
+ this.session.save();
+ this.session.refresh(true);
+ this.session.refresh(false);
+ }
+
+ @Test
+ public void testGetRepository() {
+ assertNotNull(this.session.getRepository());
+ }
+
+ @Test
+ public void testCheckPermission() throws RepositoryException {
+ this.session.checkPermission("/any/path", "anyActions");
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/testing/mock/jcr/MockWorkspaceTest.java b/src/test/java/org/apache/sling/testing/mock/jcr/MockWorkspaceTest.java
new file mode 100644
index 0000000..662a623
--- /dev/null
+++ b/src/test/java/org/apache/sling/testing/mock/jcr/MockWorkspaceTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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.sling.testing.mock.jcr;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Workspace;
+import javax.jcr.observation.ObservationManager;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class MockWorkspaceTest {
+
+ private Workspace underTest;
+
+ @Before
+ public void setUp() {
+ underTest = MockJcr.newSession().getWorkspace();
+ }
+
+ @Test
+ public void testName() {
+ assertEquals(MockJcr.DEFAULT_WORKSPACE, underTest.getName());
+ }
+
+ @Test
+ public void testNameSpaceRegistry() throws RepositoryException {
+ assertNotNull(underTest.getNamespaceRegistry());
+ }
+
+ @Test
+ public void testObservationManager() throws RepositoryException {
+ // just mage sure listener methods can be called, although they do
+ // nothing
+ ObservationManager observationManager = underTest.getObservationManager();
+ observationManager.addEventListener(null, 0, null, false, null, null, false);
+ observationManager.removeEventListener(null);
+ }
+
+ @Test
+ public void testNodeTypeManager() throws RepositoryException {
+ assertNotNull(underTest.getNodeTypeManager());
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/testing/mock/jcr/ResourceUtilTest.java b/src/test/java/org/apache/sling/testing/mock/jcr/ResourceUtilTest.java
new file mode 100644
index 0000000..05256bc
--- /dev/null
+++ b/src/test/java/org/apache/sling/testing/mock/jcr/ResourceUtilTest.java
@@ -0,0 +1,221 @@
+/*
+ * 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.sling.testing.mock.jcr;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+
+public class ResourceUtilTest {
+
+ @Test
+ public void testResolveRelativeSegments() {
+
+ assertEquals("/", ResourceUtil.normalize("/"));
+ assertEquals("/", ResourceUtil.normalize("///"));
+
+ assertEquals("/a/b/c", ResourceUtil.normalize("/a//b/c"));
+ assertEquals("/a/b/c", ResourceUtil.normalize("/a/b//c"));
+ assertEquals("/a/b/c", ResourceUtil.normalize("/a///b///c"));
+ assertEquals("/a/b/c", ResourceUtil.normalize("/a/b/c/"));
+ assertEquals("/a/b/c", ResourceUtil.normalize("/a/b/c//"));
+ assertEquals("/a/b/c", ResourceUtil.normalize("/a/b/c///"));
+
+ assertEquals("/az/bz/cz", ResourceUtil.normalize("/az//bz/cz"));
+ assertEquals("/az/bz/cz", ResourceUtil.normalize("/az/bz//cz"));
+ assertEquals("/az/bz/cz", ResourceUtil.normalize("/az///bz///cz"));
+ assertEquals("/az/bz/cz", ResourceUtil.normalize("/az/bz/cz/"));
+ assertEquals("/az/bz/cz", ResourceUtil.normalize("/az/bz/cz//"));
+ assertEquals("/az/bz/cz", ResourceUtil.normalize("/az/bz/cz///"));
+
+ assertEquals("/a", ResourceUtil.normalize("/a"));
+ assertEquals("/a", ResourceUtil.normalize("//a"));
+ assertEquals("/a", ResourceUtil.normalize("///a"));
+
+ assertEquals("/az", ResourceUtil.normalize("/az"));
+ assertEquals("/az", ResourceUtil.normalize("//az"));
+ assertEquals("/az", ResourceUtil.normalize("///az"));
+
+ assertEquals("/", ResourceUtil.normalize("/."));
+ assertEquals("/a", ResourceUtil.normalize("/a/."));
+ assertEquals("/a", ResourceUtil.normalize("/./a"));
+ assertEquals("/a/b", ResourceUtil.normalize("/a/./b"));
+ assertEquals("/a/b", ResourceUtil.normalize("/a/b/."));
+ assertEquals("/a/b", ResourceUtil.normalize("/a/./b/."));
+
+ assertEquals("/", ResourceUtil.normalize("/."));
+ assertEquals("/az", ResourceUtil.normalize("/az/."));
+ assertEquals("/az", ResourceUtil.normalize("/./az"));
+ assertEquals("/az/bz", ResourceUtil.normalize("/az/./bz"));
+ assertEquals("/az/bz", ResourceUtil.normalize("/az/bz/."));
+ assertEquals("/az/bz", ResourceUtil.normalize("/az/./bz/."));
+
+ assertNull(ResourceUtil.normalize("/.."));
+ assertNull(ResourceUtil.normalize("/.."));
+ assertEquals("/", ResourceUtil.normalize("/a/.."));
+ assertEquals("/a", ResourceUtil.normalize("/a/b/.."));
+ assertEquals("/", ResourceUtil.normalize("/a/b/../.."));
+ assertNull(ResourceUtil.normalize("/a/b/../../.."));
+
+ assertNull(ResourceUtil.normalize("/.."));
+ assertNull(ResourceUtil.normalize("/.."));
+ assertEquals("/", ResourceUtil.normalize("/az/.."));
+ assertEquals("/az", ResourceUtil.normalize("/az/bz/.."));
+ assertEquals("/", ResourceUtil.normalize("/az/bz/../.."));
+ assertNull(ResourceUtil.normalize("/az/bz/../../.."));
+
+ assertEquals("/b", ResourceUtil.normalize("/a/../b"));
+ assertEquals("/a/c", ResourceUtil.normalize("/a/b/../c"));
+ assertEquals("/c", ResourceUtil.normalize("/a/b/../../c"));
+ assertNull(ResourceUtil.normalize("/a/b/../../../c"));
+
+ assertEquals("/bz", ResourceUtil.normalize("/az/../bz"));
+ assertEquals("/az/cz", ResourceUtil.normalize("/az/bz/../cz"));
+ assertEquals("/cz", ResourceUtil.normalize("/az/bz/../../cz"));
+ assertNull(ResourceUtil.normalize("/az/bz/../../../cz"));
+
+ assertEquals("/...", ResourceUtil.normalize("/..."));
+ assertEquals("/a/...", ResourceUtil.normalize("/a/..."));
+ assertEquals("/a/b/...", ResourceUtil.normalize("/a/b/..."));
+
+ assertEquals("/az/...", ResourceUtil.normalize("/az/..."));
+ assertEquals("/az/bz/...", ResourceUtil.normalize("/az/bz/..."));
+
+ try {
+ ResourceUtil.normalize(null);
+ fail("Resolving null expects NullPointerException");
+ } catch (NullPointerException npe) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testResolveRelativeSegmentsRelative() {
+ assertEquals("a/b", ResourceUtil.normalize("a/b"));
+ assertEquals("a", ResourceUtil.normalize("a/b/.."));
+
+ assertEquals("b", ResourceUtil.normalize("a/../b"));
+ assertEquals("a/c", ResourceUtil.normalize("a/b/../c"));
+ assertEquals("c", ResourceUtil.normalize("a/b/../../c"));
+ assertEquals("", ResourceUtil.normalize("a/b/../.."));
+ assertEquals("a/c/d", ResourceUtil.normalize("a/b/../c/d"));
+ assertNull(ResourceUtil.normalize("a/b/../../../c"));
+
+ assertEquals("a/b/c", ResourceUtil.normalize("a/b/c"));
+ assertEquals("az/bz/cz", ResourceUtil.normalize("az/bz/cz"));
+ assertEquals("", ResourceUtil.normalize(""));
+ }
+
+ @Test
+ public void testGetParent() {
+ assertNull(ResourceUtil.getParent("/"));
+ assertNull(ResourceUtil.getParent("/.."));
+
+ assertEquals("/", ResourceUtil.getParent("/b"));
+ assertEquals("b/c", ResourceUtil.getParent("b/c/d"));
+ assertEquals("/b/c", ResourceUtil.getParent("/b/c/d"));
+
+ assertNull(ResourceUtil.getParent("b"));
+ assertNull(ResourceUtil.getParent("/b/.."));
+
+ assertEquals("security:/", ResourceUtil.getParent("security:/b"));
+ assertEquals("security:/b", ResourceUtil.getParent("security:/b/c"));
+ assertEquals("security:/b/c", ResourceUtil.getParent("security:/b/c/d"));
+ }
+
+ @Test
+ public void testGetName() {
+ assertEquals("", ResourceUtil.getName("/"));
+ assertEquals("", ResourceUtil.getName("/a/.."));
+
+ assertEquals("c", ResourceUtil.getName("c"));
+ assertEquals("c", ResourceUtil.getName("/c"));
+
+ assertEquals("c", ResourceUtil.getName("b/c"));
+ assertEquals("c", ResourceUtil.getName("/b/c"));
+
+ assertEquals("c", ResourceUtil.getName("b/c/"));
+ assertEquals("c", ResourceUtil.getName("/b/c/"));
+
+ assertEquals("b", ResourceUtil.getName("b/c/.."));
+ assertEquals("b", ResourceUtil.getName("/b/c/.."));
+ assertEquals("", ResourceUtil.getName("/b/c/../.."));
+ }
+
+ @Test
+ public void testGetParentLevel() throws Exception {
+ boolean caughtNullPointerException = false;
+ try {
+ ResourceUtil.getParent(null, 4);
+ } catch (NullPointerException e) {
+ // Expected exception
+ caughtNullPointerException = true;
+ } catch (Exception e) {
+ fail("Expected NullPointerException, but caught " + e.getClass().getName() + " instead.");
+ }
+ if (!caughtNullPointerException) {
+ fail("Expected NullPointerException, but no exception was thrown.");
+ }
+
+ boolean caughtIllegalArgumentException = false;
+ try {
+ ResourceUtil.getParent("/a/b", -2);
+ } catch (IllegalArgumentException e) {
+ // Expected exception
+ caughtIllegalArgumentException = true;
+ } catch (Exception e) {
+ fail("Expected IllegalArgumentException, but caught " + e.getClass().getName() + " instead.");
+ }
+ if (!caughtIllegalArgumentException) {
+ fail("Expected IllegalArgumentException, but no exception was thrown.");
+ }
+
+ assertNull(ResourceUtil.getParent("/a", 4));
+ assertNull(ResourceUtil.getParent("/", 1));
+ assertNull(ResourceUtil.getParent("b/c", 2));
+ assertNull(ResourceUtil.getParent("/b/..", 1));
+ assertNull(ResourceUtil.getParent("b", 1));
+ assertNull(ResourceUtil.getParent("", 3));
+ assertNull(ResourceUtil.getParent("/..", 1));
+ assertNull(ResourceUtil.getParent("security:/b", 2));
+ assertNull(ResourceUtil.getParent("/b///", 2));
+
+ assertEquals("", ResourceUtil.getParent("", 0));
+ assertEquals("b", ResourceUtil.getParent("b", 0));
+ assertEquals("/", ResourceUtil.getParent("/", 0));
+ assertEquals("/a/b", ResourceUtil.getParent("/a/b", 0));
+ assertEquals("security:/b", ResourceUtil.getParent("security:/b", 0));
+
+ assertEquals("/", ResourceUtil.getParent("/b", 1));
+ assertEquals("b", ResourceUtil.getParent("b/c", 1));
+ assertEquals("b/c", ResourceUtil.getParent("b/c/d", 1));
+ assertEquals("/b/c", ResourceUtil.getParent("/b/c/d", 1));
+ assertEquals("security:/", ResourceUtil.getParent("security:/b", 1));
+ assertEquals("security:/b", ResourceUtil.getParent("security:/b/c", 1));
+ assertEquals("security:/b/c", ResourceUtil.getParent("security:/b/c/d", 1));
+
+ assertEquals("b", ResourceUtil.getParent("b/c/d", 2));
+ assertEquals("b/c", ResourceUtil.getParent("b/c/d/e", 2));
+ assertEquals("/", ResourceUtil.getParent("/b/c/d", 3));
+ assertEquals("/", ResourceUtil.getParent("/b///", 1));
+ }
+
+}
--
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.