SLING-5294 - TeleporterRule.withResources(...) implemented, with integration tests
git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1715072 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/pom.xml b/pom.xml
index 7b4c49e..1cf9a95 100644
--- a/pom.xml
+++ b/pom.xml
@@ -31,7 +31,7 @@
<packaging>jar</packaging>
<name>Apache Sling JUnit Tests Teleporter</name>
<description>Client-side implementation of the Teleporter mechanism for server-side JUnit tests</description>
-
+
<dependencies>
<dependency>
<groupId>junit</groupId>
@@ -54,6 +54,12 @@
<version>1.0.13-SNAPSHOT</version>
</dependency>
<dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>2.4</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
diff --git a/src/main/java/org/apache/sling/testing/teleporter/client/ClassResourceVisitor.java b/src/main/java/org/apache/sling/testing/teleporter/client/ClassResourceVisitor.java
new file mode 100644
index 0000000..7373fee
--- /dev/null
+++ b/src/main/java/org/apache/sling/testing/teleporter/client/ClassResourceVisitor.java
@@ -0,0 +1,127 @@
+/*
+ * 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.teleporter.client;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/** Finds and visits resources provided by ClassLoaders based
+ * on their path. If a path points to a folder, its child
+ * resources are also visited. Currently works for the "file:"
+ * and "jar:" resource protocols.
+ */
+public class ClassResourceVisitor {
+
+ private final Class<?> clazz;
+ private final String path;
+
+ static interface Processor {
+ void process(String resourcePath, InputStream resourceStream) throws IOException;
+ }
+
+ /** Visit the resources provided by clazz's ClassLoader,
+ * based on the supplied path.
+ *
+ * @param clazz
+ * @param path If that points to a folder, it is visited recursively
+ */
+ public ClassResourceVisitor(Class<?> clazz, String path) {
+ this.clazz = clazz;
+ this.path = path;
+ }
+
+ public void visit(Processor p) throws IOException {
+ final URL resourceURL = clazz.getResource(path);
+ if(resourceURL == null) {
+ return;
+ }
+
+ final String protocol = resourceURL.getProtocol();
+
+ if("file".equals(protocol)) {
+ // Get base path and remove ending slash
+ String basePath = clazz.getResource("/").getPath();
+ basePath = basePath.substring(0, basePath.length() - 1);
+ processFile(basePath, new File(resourceURL.getPath()), p);
+
+ } else if("jar".equals(protocol)) {
+ // Jar entries use relative paths
+ final String rPath = path.startsWith("/") ? path.substring(1) : path;
+ final String jarFilePath = resourceURL.getPath().split("!")[0].substring("file:".length());
+ final JarFile jar = new JarFile(jarFilePath);
+ try {
+ final Enumeration<JarEntry> entries = jar.entries();
+ while (entries.hasMoreElements()) {
+ final JarEntry e = entries.nextElement();
+ if(!e.isDirectory() && jarEntryMatches(rPath, e.getName())) {
+ final InputStream is = jar.getInputStream(e);
+ try {
+ p.process("/" + e.getName(), is);
+ } finally {
+ is.close();
+ }
+ }
+ }
+ } finally {
+ jar.close();
+ }
+
+
+ } else {
+ throw new IllegalArgumentException("Unkown protocol " + protocol);
+ }
+ }
+
+ /** True if the entry part matches the given path.
+ * @param givenPath if it ends with / it's considered a folder name, otherwise
+ * we check for an exact match
+ * @param entryPath the path of the jar entry to check
+ * @return true if there's a match
+ */
+ private boolean jarEntryMatches(String givenPath, String entryPath) {
+ if(givenPath.endsWith("/")) {
+ return entryPath.startsWith(givenPath);
+ } else {
+ return entryPath.equals(givenPath);
+ }
+ }
+
+ private void processFile(String basePath, File f, Processor p) throws IOException {
+ if(f.isDirectory()) {
+ final String [] names = f.list();
+ if(names != null) {
+ for(String name : names) {
+ processFile(basePath, new File(f, name), p);
+ }
+ }
+ } else {
+ final InputStream is = new BufferedInputStream(new FileInputStream(f));
+ try {
+ p.process(f.getAbsolutePath().substring(basePath.length()), is);
+ } finally {
+ is.close();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/testing/teleporter/client/ClientSideTeleporter.java b/src/main/java/org/apache/sling/testing/teleporter/client/ClientSideTeleporter.java
index 758d503..2ed7330 100644
--- a/src/main/java/org/apache/sling/testing/teleporter/client/ClientSideTeleporter.java
+++ b/src/main/java/org/apache/sling/testing/teleporter/client/ClientSideTeleporter.java
@@ -48,14 +48,31 @@
private String serverCredentials;
private final Set<Class<?>> embeddedClasses = new HashSet<Class<?>>();
- private InputStream buildTestBundle(Class<?> c, Collection<Class<?>> embeddedClasses, String bundleSymbolicName) {
+ private InputStream buildTestBundle(Class<?> c, Collection<Class<?>> embeddedClasses, String bundleSymbolicName) throws IOException {
final TinyBundle b = TinyBundles.bundle()
.set(Constants.BUNDLE_SYMBOLICNAME, bundleSymbolicName)
.set("Sling-Test-Regexp", c.getName() + ".*")
.add(c);
+
+ // Embed specified classes
for(Class<?> clz : embeddedClasses) {
b.add(clz);
}
+
+ // Embed specified resources
+ if(!embeddedResourcePaths.isEmpty()) {
+ for(String path : embeddedResourcePaths) {
+ final ClassResourceVisitor.Processor p = new ClassResourceVisitor.Processor() {
+ @Override
+ public void process(String resourcePath, InputStream resourceStream) throws IOException {
+ b.add(resourcePath, resourceStream);
+ }
+
+ };
+ new ClassResourceVisitor(getClass(), path).visit(p);
+ }
+ }
+
return b.build(TinyBundles.withBnd());
}
diff --git a/src/test/java/org/apache/sling/testing/teleporter/client/ClassResourceVisitorTest.java b/src/test/java/org/apache/sling/testing/teleporter/client/ClassResourceVisitorTest.java
new file mode 100644
index 0000000..af7b77f
--- /dev/null
+++ b/src/test/java/org/apache/sling/testing/teleporter/client/ClassResourceVisitorTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.teleporter.client;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.io.IOUtils;
+import org.junit.Before;
+import org.junit.Test;
+
+public class ClassResourceVisitorTest implements ClassResourceVisitor.Processor {
+
+ private Map<String, String> resources;
+
+ @Override
+ public void process(String resourceName, InputStream resourceStream) throws IOException {
+ final StringWriter w = new StringWriter();
+ IOUtils.copy(resourceStream, w);
+ resources.put(resourceName, w.toString());
+ }
+
+ @Before
+ public void setup() {
+ resources = new HashMap<String, String>();
+ }
+
+ private void assertResource(String name, String expected) {
+ final String content = resources.get(name);
+ assertNotNull("Expecting resource " + name + " (keys=" + resources.keySet() + ")", content);
+ assertTrue("Expecting " + name + " to contain " + expected, content.contains(expected));
+ }
+
+ @Test
+ public void testSingleFile() throws IOException {
+ new ClassResourceVisitor(getClass(), "/somepath/two.txt").visit(this);
+ assertResource("/somepath/two.txt", "two");
+ assertEquals(1, resources.size());
+ }
+
+ @Test
+ public void testSomepathFiles() throws IOException {
+ new ClassResourceVisitor(getClass(), "/somepath").visit(this);
+ assertResource("/somepath/two.txt", "two");
+ assertEquals(3, resources.size());
+ }
+
+ @Test
+ public void testSubFiles() throws IOException {
+ new ClassResourceVisitor(getClass(), "/somepath/sub").visit(this);
+ assertResource("/somepath/sub/trois.txt", "three");
+ assertEquals(1, resources.size());
+ }
+
+ @Test
+ public void testNotFound() throws Exception {
+ new ClassResourceVisitor(IOUtils.class, "/NOT_FOUND").visit(this);
+ assertEquals(0, resources.size());
+ }
+
+ @Test
+ public void testJarResourcesFolder() throws Exception {
+ // Get resources from the IOUtils jar to test the jar protocol
+ new ClassResourceVisitor(IOUtils.class, "/META-INF/maven/commons-io/").visit(this);
+ assertResource("/META-INF/maven/commons-io/commons-io/pom.properties", "artifactId=commons-io");
+ assertResource("/META-INF/maven/commons-io/commons-io/pom.xml", "Licensed to the Apache Software Foundation");
+ assertEquals(2, resources.size());
+ }
+
+ @Test
+ public void testSinglJarResource() throws Exception {
+ new ClassResourceVisitor(IOUtils.class, "/META-INF/maven/commons-io/commons-io/pom.properties").visit(this);
+ assertResource("/META-INF/maven/commons-io/commons-io/pom.properties", "artifactId=commons-io");
+ assertEquals(1, resources.size());
+ }
+}
\ No newline at end of file
diff --git a/src/test/resources/somepath/1.txt b/src/test/resources/somepath/1.txt
new file mode 100644
index 0000000..7abaf4e
--- /dev/null
+++ b/src/test/resources/somepath/1.txt
@@ -0,0 +1,19 @@
+one
+
+/*
+ * 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.
+ */
+
diff --git a/src/test/resources/somepath/sub/trois.txt b/src/test/resources/somepath/sub/trois.txt
new file mode 100644
index 0000000..4706298
--- /dev/null
+++ b/src/test/resources/somepath/sub/trois.txt
@@ -0,0 +1,18 @@
+three
+
+/*
+ * 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.
+ */
diff --git a/src/test/resources/somepath/two.txt b/src/test/resources/somepath/two.txt
new file mode 100644
index 0000000..73c2aa1
--- /dev/null
+++ b/src/test/resources/somepath/two.txt
@@ -0,0 +1,19 @@
+two
+
+/*
+ * 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.
+ */
+