diff --git a/core/pom.xml b/core/pom.xml
index 3469815e7ae..9edcaefa9b2 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -82,6 +82,13 @@
+
+
+ fakejar
+ fakejar
+ 0
+ test
+
@@ -232,4 +239,11 @@
+
+
+
+ testlib
+ file://${project.basedir}/testlib/repo
+
+
diff --git a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java
index ecd23770db6..b69e2abcf8f 100644
--- a/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java
+++ b/core/src/main/java/org/testcontainers/containers/DockerComposeContainer.java
@@ -8,7 +8,6 @@
import com.google.common.base.Splitter;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.Uninterruptibles;
-import org.apache.commons.lang.SystemUtils;
import org.junit.runner.Description;
import org.rnorth.ducttape.ratelimits.RateLimiter;
import org.rnorth.ducttape.ratelimits.RateLimiterBuilder;
@@ -32,6 +31,7 @@
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.stream.Collectors.toList;
import static org.testcontainers.containers.BindMode.READ_ONLY;
import static org.testcontainers.containers.BindMode.READ_WRITE;
@@ -196,7 +196,7 @@ private List listChildContainers() {
.exec().stream()
.filter(container -> Arrays.stream(container.getNames()).anyMatch(name ->
name.startsWith("/" + identifier)))
- .collect(Collectors.toList());
+ .collect(toList());
}
private void startAmbassadorContainers(Profiler profiler) {
@@ -394,15 +394,13 @@ public ContainerisedDockerCompose(List composeFiles, String identifier) {
// Map the docker compose file into the container
final File dockerComposeBaseFile = composeFiles.get(0);
final String pwd = dockerComposeBaseFile.getAbsoluteFile().getParentFile().getAbsolutePath();
- final String containerPwd;
- if (SystemUtils.IS_OS_WINDOWS) {
- containerPwd = PathUtils.createMinGWPath(pwd).substring(1);
- } else {
- containerPwd = pwd;
- }
+ final String containerPwd = MountableFile.forHostPath(pwd).getResolvedPath();
- final List absoluteDockerComposeFiles = composeFiles.stream().map(
- file -> containerPwd + "/" + file.getAbsoluteFile().getName()).collect(Collectors.toList());
+ final List absoluteDockerComposeFiles = composeFiles.stream()
+ .map(File::getAbsolutePath)
+ .map(MountableFile::forHostPath)
+ .map(MountableFile::getResolvedPath)
+ .collect(toList());
final String composeFileEnvVariableValue = Joiner.on(File.pathSeparator).join(absoluteDockerComposeFiles);
logger().debug("Set env COMPOSE_FILE={}", composeFileEnvVariableValue);
addEnv(ENV_COMPOSE_FILE, composeFileEnvVariableValue);
diff --git a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java
index e51e6d003af..6d9427b61f3 100644
--- a/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java
+++ b/core/src/main/java/org/testcontainers/images/builder/ImageFromDockerfile.java
@@ -9,7 +9,6 @@
import lombok.Cleanup;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
@@ -128,16 +127,12 @@ public void onNext(BuildResponseItem item) {
profiler.start("Send context as TAR");
try (TarArchiveOutputStream tarArchive = new TarArchiveOutputStream(new GZIPOutputStream(out))) {
+ tarArchive.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX);
+
for (Map.Entry entry : transferables.entrySet()) {
- TarArchiveEntry tarEntry = new TarArchiveEntry(entry.getKey());
Transferable transferable = entry.getValue();
- tarEntry.setSize(transferable.getSize());
- tarEntry.setMode(transferable.getFileMode());
-
- tarArchive.putArchiveEntry(tarEntry);
- transferable.transferTo(tarArchive);
- tarArchive.closeArchiveEntry();
-
+ final String destination = entry.getKey();
+ transferable.transferTo(tarArchive, destination);
}
tarArchive.finish();
}
diff --git a/core/src/main/java/org/testcontainers/images/builder/Transferable.java b/core/src/main/java/org/testcontainers/images/builder/Transferable.java
index eb245172596..b68ae95ac43 100644
--- a/core/src/main/java/org/testcontainers/images/builder/Transferable.java
+++ b/core/src/main/java/org/testcontainers/images/builder/Transferable.java
@@ -1,16 +1,21 @@
package org.testcontainers.images.builder;
-import java.io.OutputStream;
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
+import org.apache.commons.io.IOUtils;
+
+import java.io.IOException;
public interface Transferable {
int DEFAULT_FILE_MODE = 0100644;
+ int DEFAULT_DIR_MODE = 040755;
/**
* Get file mode. Default is 0100644.
- * @see Transferable#DEFAULT_FILE_MODE
*
* @return file mode
+ * @see Transferable#DEFAULT_FILE_MODE
*/
default int getFileMode() {
return DEFAULT_FILE_MODE;
@@ -26,7 +31,26 @@ default int getFileMode() {
/**
* transfer content of this Transferable to the output stream. Must not close the stream.
*
- * @param outputStream stream to output
+ * @param tarArchiveOutputStream stream to output
+ * @param destination
*/
- void transferTo(OutputStream outputStream);
+ default void transferTo(TarArchiveOutputStream tarArchiveOutputStream, final String destination) {
+ TarArchiveEntry tarEntry = new TarArchiveEntry(destination);
+ tarEntry.setSize(getSize());
+ tarEntry.setMode(getFileMode());
+
+ try {
+ tarArchiveOutputStream.putArchiveEntry(tarEntry);
+ IOUtils.write(getBytes(), tarArchiveOutputStream);
+ tarArchiveOutputStream.closeArchiveEntry();
+ } catch (IOException e) {
+ throw new RuntimeException("Can't transfer " + getDescription(), e);
+ }
+ }
+
+ default byte[] getBytes() {
+ return new byte[0];
+ }
+
+ String getDescription();
}
diff --git a/core/src/main/java/org/testcontainers/images/builder/traits/DockerfileTrait.java b/core/src/main/java/org/testcontainers/images/builder/traits/DockerfileTrait.java
index a4740220fc8..07ed13ed0fb 100644
--- a/core/src/main/java/org/testcontainers/images/builder/traits/DockerfileTrait.java
+++ b/core/src/main/java/org/testcontainers/images/builder/traits/DockerfileTrait.java
@@ -1,12 +1,9 @@
package org.testcontainers.images.builder.traits;
import lombok.Getter;
-import org.apache.commons.io.IOUtils;
import org.testcontainers.images.builder.Transferable;
import org.testcontainers.images.builder.dockerfile.DockerfileBuilder;
-import java.io.IOException;
-import java.io.OutputStream;
import java.util.function.Consumer;
/**
@@ -33,12 +30,8 @@ public long getSize() {
}
@Override
- public void transferTo(OutputStream outputStream) {
- try {
- IOUtils.write(getBytes(), outputStream);
- } catch (IOException e) {
- throw new RuntimeException("Can't transfer Dockerfile", e);
- }
+ public String getDescription() {
+ return "Dockerfile: " + builder;
}
});
}
diff --git a/core/src/main/java/org/testcontainers/images/builder/traits/FilesTrait.java b/core/src/main/java/org/testcontainers/images/builder/traits/FilesTrait.java
index d837d360a75..882c0e4231b 100644
--- a/core/src/main/java/org/testcontainers/images/builder/traits/FilesTrait.java
+++ b/core/src/main/java/org/testcontainers/images/builder/traits/FilesTrait.java
@@ -1,11 +1,8 @@
package org.testcontainers.images.builder.traits;
-import org.testcontainers.images.builder.Transferable;
+import org.testcontainers.utility.MountableFile;
import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.file.Files;
import java.nio.file.Path;
/**
@@ -19,31 +16,7 @@ default SELF withFileFromFile(String path, File file) {
}
default SELF withFileFromPath(String path, Path filePath) {
- return ((SELF) this).withFileFromTransferable(path, new Transferable() {
-
- @Override
- public long getSize() {
- try {
- return Files.size(filePath);
- } catch (IOException e) {
- throw new RuntimeException("Can't get size from " + filePath, e);
- }
- }
-
- @Override
- public int getFileMode() {
- return DEFAULT_FILE_MODE | (Files.isExecutable(filePath) ? 0755 : 0);
- }
-
- @Override
- public void transferTo(OutputStream outputStream) {
- try {
- Files.copy(filePath, outputStream);
- } catch (IOException e) {
- throw new RuntimeException("Can't transfer file " + filePath, e);
- }
- }
-
- });
+ final MountableFile mountableFile = MountableFile.forHostPath(filePath);
+ return ((SELF) this).withFileFromTransferable(path, mountableFile);
}
}
diff --git a/core/src/main/java/org/testcontainers/images/builder/traits/StringsTrait.java b/core/src/main/java/org/testcontainers/images/builder/traits/StringsTrait.java
index 5a3f6bf0f95..51423068ec9 100644
--- a/core/src/main/java/org/testcontainers/images/builder/traits/StringsTrait.java
+++ b/core/src/main/java/org/testcontainers/images/builder/traits/StringsTrait.java
@@ -1,12 +1,9 @@
package org.testcontainers.images.builder.traits;
-import org.apache.commons.io.IOUtils;
+import lombok.Getter;
import org.apache.commons.lang.StringUtils;
import org.testcontainers.images.builder.Transferable;
-import java.io.IOException;
-import java.io.OutputStream;
-
/**
* BuildContextBuilder's trait for String-based manipulations.
*
@@ -16,6 +13,7 @@ public interface StringsTrait & BuildContextBuil
default SELF withFileFromString(String path, String content) {
return ((SELF) this).withFileFromTransferable(path, new Transferable() {
+ @Getter
byte[] bytes = content.getBytes();
@Override
@@ -24,14 +22,9 @@ public long getSize() {
}
@Override
- public void transferTo(OutputStream outputStream) {
- try {
- IOUtils.write(bytes, outputStream);
- } catch (IOException e) {
- throw new RuntimeException("Can't transfer string " + StringUtils.abbreviate(content, 100), e);
- }
+ public String getDescription() {
+ return "String: " + StringUtils.abbreviate(content, 100);
}
-
});
}
}
diff --git a/core/src/main/java/org/testcontainers/utility/MountableFile.java b/core/src/main/java/org/testcontainers/utility/MountableFile.java
index 31ee6008751..8fef823274c 100644
--- a/core/src/main/java/org/testcontainers/utility/MountableFile.java
+++ b/core/src/main/java/org/testcontainers/utility/MountableFile.java
@@ -4,35 +4,34 @@
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.lang.SystemUtils;
import org.jetbrains.annotations.NotNull;
+import org.testcontainers.images.builder.Transferable;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
-import java.net.URI;
-import java.net.URISyntaxException;
+import java.io.*;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
-import static lombok.AccessLevel.PRIVATE;
+import static lombok.AccessLevel.PACKAGE;
import static org.testcontainers.utility.PathUtils.recursiveDeleteDir;
/**
* An abstraction over files and classpath resources aimed at encapsulating all the complexity of generating
* a path that the Docker daemon is about to create a volume mount for.
*/
-@RequiredArgsConstructor(access = PRIVATE)
+@RequiredArgsConstructor(access = PACKAGE)
@Slf4j
-public class MountableFile {
+public class MountableFile implements Transferable {
private final String path;
@@ -60,25 +59,13 @@ public static MountableFile forHostPath(@NotNull final String path) {
}
/**
- * Obtain a path that the Docker daemon should be able to use to volume mount a file/resource
- * into a container. If this is a classpath resource residing in a JAR, it will be extracted to
- * a temporary location so that the Docker daemon is able to access it.
+ * Obtains a {@link MountableFile} corresponding to a file on the docker host filesystem.
*
- * @return a volume-mountable path.
+ * @param path the path to the resource
+ * @return a {@link MountableFile} that may be used to obtain a mountable path
*/
- private String resolvePath() {
- String result;
- if (path.contains(".jar!")) {
- result = extractClassPathResourceToTempLocation(this.path);
- } else {
- result = unencodeResourceURIToFilePath(path);
- }
-
- if (SystemUtils.IS_OS_WINDOWS) {
- result = PathUtils.createMinGWPath(result);
- }
-
- return result;
+ public static MountableFile forHostPath(final Path path) {
+ return new MountableFile(path.toAbsolutePath().toString());
}
@NotNull
@@ -105,18 +92,43 @@ private static URL getClasspathResource(@NotNull final String resourcePath, @Not
}
}
- throw new IllegalArgumentException("Resource with path " + resourcePath + " could not be found on any of these classloaders: " + classLoaders);
+ throw new IllegalArgumentException("Resource with path " + resourcePath + " could not be found on any of these classloaders: " + classLoadersToSearch);
}
private static String unencodeResourceURIToFilePath(@NotNull final String resource) {
try {
// Convert any url-encoded characters (e.g. spaces) back into unencoded form
- return new URI(resource).getPath();
- } catch (URISyntaxException e) {
+ return URLDecoder.decode(resource, Charsets.UTF_8.name())
+ .replaceFirst("jar:", "")
+ .replaceFirst("file:", "")
+ .replaceAll("!.*", "");
+ } catch (UnsupportedEncodingException e) {
throw new IllegalStateException(e);
}
}
+ /**
+ * Obtain a path that the Docker daemon should be able to use to volume mount a file/resource
+ * into a container. If this is a classpath resource residing in a JAR, it will be extracted to
+ * a temporary location so that the Docker daemon is able to access it.
+ *
+ * @return a volume-mountable path.
+ */
+ private String resolvePath() {
+ String result;
+ if (path.contains(".jar!")) {
+ result = extractClassPathResourceToTempLocation(this.path);
+ } else {
+ result = unencodeResourceURIToFilePath(path);
+ }
+
+ if (SystemUtils.IS_OS_WINDOWS) {
+ result = PathUtils.createMinGWPath(result);
+ }
+
+ return result;
+ }
+
/**
* Extract a file or directory tree from a JAR file to a temporary location.
* This allows Docker to mount classpath resources as files.
@@ -129,13 +141,7 @@ private String extractClassPathResourceToTempLocation(final String hostPath) {
//noinspection ResultOfMethodCallIgnored
tmpLocation.delete();
- String jarPath = hostPath.replaceFirst("jar:", "").replaceFirst("file:", "").replaceAll("!.*", "");
- String urldecodedJarPath;
- try {
- urldecodedJarPath = URLDecoder.decode(jarPath, Charsets.UTF_8.name());
- } catch (UnsupportedEncodingException e) {
- throw new IllegalArgumentException("Could not URLDecode path with UTF-8 encoding: " + hostPath, e);
- }
+ String urldecodedJarPath = unencodeResourceURIToFilePath(hostPath);
String internalPath = hostPath.replaceAll("[^!]*!/", "");
try (JarFile jarFile = new JarFile(urldecodedJarPath)) {
@@ -164,9 +170,9 @@ private String extractClassPathResourceToTempLocation(final String hostPath) {
@SuppressWarnings("ResultOfMethodCallIgnored")
private void copyFromJarToLocation(final JarFile jarFile,
- final JarEntry entry,
- final String fromRoot,
- final File toRoot) throws IOException {
+ final JarEntry entry,
+ final String fromRoot,
+ final File toRoot) throws IOException {
String destinationName = entry.getName().replaceFirst(fromRoot, "");
File newFile = new File(toRoot, destinationName);
@@ -193,4 +199,84 @@ private void copyFromJarToLocation(final JarFile jarFile,
private void deleteOnExit(final Path path) {
Runtime.getRuntime().addShutdownHook(new Thread(() -> recursiveDeleteDir(path)));
}
-}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void transferTo(final TarArchiveOutputStream outputStream, String destinationPathInTar) {
+ recursiveTar(destinationPathInTar, this.getResolvedPath(), this.getResolvedPath(), outputStream);
+ }
+
+ /*
+ * Recursively copies a file/directory into a TarArchiveOutputStream
+ */
+ private void recursiveTar(String destination, String sourceRootDir, String sourceCurrentItem, TarArchiveOutputStream tarArchive) {
+ try {
+ final File sourceFile = new File(sourceCurrentItem).getCanonicalFile(); // e.g. /foo/bar/baz
+ final File sourceRootFile = new File(sourceRootDir).getCanonicalFile(); // e.g. /foo
+ final String relativePathToSourceFile = sourceRootFile.toPath().relativize(sourceFile.toPath()).toFile().toString(); // e.g. /bar/baz
+
+ final TarArchiveEntry tarEntry = new TarArchiveEntry(sourceFile, destination + "/" + relativePathToSourceFile); // entry filename e.g. /xyz/bar/baz
+
+ // TarArchiveEntry automatically sets the mode for file/directory, but we can update to ensure that the mode is set exactly (inc executable bits)
+ tarEntry.setMode(getUnixFileMode(sourceCurrentItem));
+ tarArchive.putArchiveEntry(tarEntry);
+
+ if (sourceFile.isFile()) {
+ Files.copy(sourceFile.toPath(), tarArchive);
+ }
+ // a directory entry merely needs to exist in the TAR file - there is no data stored yet
+ tarArchive.closeArchiveEntry();
+
+ final File[] children = sourceFile.listFiles();
+ if (children != null) {
+ // recurse into child files/directories
+ for (final File child : children) {
+ recursiveTar(destination, sourceRootDir + File.separator, child.getCanonicalPath(), tarArchive);
+ }
+ }
+ } catch (IOException e) {
+ log.error("Error when copying TAR file entry: {}", sourceCurrentItem, e);
+ throw new UncheckedIOException(e); // fail fast
+ }
+ }
+
+ @Override
+ public long getSize() {
+
+ final File file = new File(this.getResolvedPath());
+ if (file.isFile()) {
+ return file.length();
+ } else {
+ return 0;
+ }
+ }
+
+ @Override
+ public String getDescription() {
+ return this.getResolvedPath();
+ }
+
+ @Override
+ public int getFileMode() {
+ return getUnixFileMode(this.getResolvedPath());
+ }
+
+ private int getUnixFileMode(final String pathAsString) {
+ final Path path = Paths.get(pathAsString);
+ try {
+ return (int) Files.getAttribute(path, "unix:mode");
+ } catch (IOException e) {
+ // fallback for non-posix environments
+ int mode = DEFAULT_FILE_MODE;
+ if (Files.isDirectory(path)) {
+ mode = DEFAULT_DIR_MODE;
+ } else if (Files.isExecutable(path)) {
+ mode |= 0111; // equiv to +x for user/group/others
+ }
+
+ return mode;
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/src/test/java/org/testcontainers/junit/DockerfileTest.java b/core/src/test/java/org/testcontainers/junit/DockerfileTest.java
index a2b11e5bc9d..7390b21fce2 100644
--- a/core/src/test/java/org/testcontainers/junit/DockerfileTest.java
+++ b/core/src/test/java/org/testcontainers/junit/DockerfileTest.java
@@ -10,7 +10,6 @@
import org.testcontainers.images.builder.ImageFromDockerfile;
import org.testcontainers.images.builder.Transferable;
-import java.io.OutputStream;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -85,14 +84,21 @@ public long getSize() {
}
@Override
- public int getFileMode() {
- return 0123;
+ public byte[] getBytes() {
+ return new byte[0];
}
@Override
- public void transferTo(OutputStream outputStream) {
+ public String getDescription() {
+ return "test file";
+ }
+ @Override
+ public int getFileMode() {
+ return 0123;
}
+
+
})
.withDockerfileFromBuilder(builder -> builder
.from("alpine:3.2")
diff --git a/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java b/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java
new file mode 100644
index 00000000000..c4b719ea1ca
--- /dev/null
+++ b/core/src/test/java/org/testcontainers/utility/DirectoryTarResourceTest.java
@@ -0,0 +1,71 @@
+package org.testcontainers.utility;
+
+import org.junit.Test;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.output.ToStringConsumer;
+import org.testcontainers.containers.output.WaitingConsumer;
+import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy;
+import org.testcontainers.images.builder.ImageFromDockerfile;
+
+import java.io.File;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue;
+
+public class DirectoryTarResourceTest {
+
+ @Test
+ public void simpleRecursiveFileTest() throws TimeoutException {
+
+ WaitingConsumer wait = new WaitingConsumer();
+
+ final ToStringConsumer toString = new ToStringConsumer();
+
+ GenericContainer container = new GenericContainer(
+ new ImageFromDockerfile()
+ .withDockerfileFromBuilder(builder ->
+ builder.from("alpine:3.3")
+ .copy("/tmp/foo", "/foo")
+ .cmd("cat /foo/src/test/resources/test-recursive-file.txt")
+ .build()
+ ).withFileFromFile("/tmp/foo", new File("."))) // '.' is expected to be the project base directory, so all source code/resources should be copied in
+ .withStartupCheckStrategy(new OneShotStartupCheckStrategy())
+ .withLogConsumer(wait.andThen(toString));
+
+ container.start();
+ wait.waitUntilEnd(60, TimeUnit.SECONDS);
+
+ final String results = toString.toUtf8String();
+
+ assertTrue("The container has a file that was copied in via a recursive copy", results.contains("Used for DirectoryTarResourceTest"));
+ }
+
+ @Test
+ public void simpleRecursiveClasspathResourceTest() throws TimeoutException {
+ // This test combines the copying of classpath resources from JAR files with the recursive TAR approach, to allow JARed classpath resources to be copied in to an image
+
+ WaitingConsumer wait = new WaitingConsumer();
+
+ final ToStringConsumer toString = new ToStringConsumer();
+
+ GenericContainer container = new GenericContainer(
+ new ImageFromDockerfile()
+ .withDockerfileFromBuilder(builder ->
+ builder.from("alpine:3.3")
+ .copy("/tmp/foo", "/foo")
+ .cmd("ls -lRt /foo")
+ .build()
+ ).withFileFromClasspath("/tmp/foo", "/recursive/dir")) // here we use /org/junit as a directory that really should exist on the classpath
+ .withStartupCheckStrategy(new OneShotStartupCheckStrategy())
+ .withLogConsumer(wait.andThen(toString));
+
+ container.start();
+ wait.waitUntilEnd(60, TimeUnit.SECONDS);
+
+ final String results = toString.toUtf8String();
+
+ // ExternalResource.class is known to exist in a subdirectory of /org/junit so should be successfully copied in
+ assertTrue("The container has a file that was copied in via a recursive copy from a JAR resource", results.contains("content.txt"));
+ }
+}
diff --git a/core/src/test/java/org/testcontainers/utility/MountableFileTest.java b/core/src/test/java/org/testcontainers/utility/MountableFileTest.java
index e42d92f07c1..5ac1a1c0ed8 100644
--- a/core/src/test/java/org/testcontainers/utility/MountableFileTest.java
+++ b/core/src/test/java/org/testcontainers/utility/MountableFileTest.java
@@ -29,7 +29,14 @@ public void forClasspathResourceWithAbsolutePath() throws Exception {
@Test
public void forClasspathResourceFromJar() throws Exception {
- final MountableFile mountableFile = MountableFile.forClasspathResource("docker-java.properties");
+ final MountableFile mountableFile = MountableFile.forClasspathResource("META-INF/dummy_unique_name.txt");
+
+ performChecks(mountableFile);
+ }
+
+ @Test
+ public void forClasspathResourceFromJarWithAbsolutePath() throws Exception {
+ final MountableFile mountableFile = MountableFile.forClasspathResource("/META-INF/dummy_unique_name.txt");
performChecks(mountableFile);
}
@@ -49,7 +56,8 @@ public void forHostPathWithSpaces() throws Exception {
performChecks(mountableFile);
- assertTrue("The resolved path contains the original space", mountableFile.getResolvedPath().contains(" "));assertFalse("The resolved path does not contain an escaped space", mountableFile.getResolvedPath().contains("\\ "));
+ assertTrue("The resolved path contains the original space", mountableFile.getResolvedPath().contains(" "));
+ assertFalse("The resolved path does not contain an escaped space", mountableFile.getResolvedPath().contains("\\ "));
}
/*
diff --git a/core/src/test/resources/test-recursive-file.txt b/core/src/test/resources/test-recursive-file.txt
new file mode 100644
index 00000000000..067480d972b
--- /dev/null
+++ b/core/src/test/resources/test-recursive-file.txt
@@ -0,0 +1 @@
+Used for DirectoryTarResourceTest
\ No newline at end of file
diff --git a/core/testlib/META-INF/dummy_unique_name.txt b/core/testlib/META-INF/dummy_unique_name.txt
new file mode 100644
index 00000000000..48162f49928
--- /dev/null
+++ b/core/testlib/META-INF/dummy_unique_name.txt
@@ -0,0 +1 @@
+This is a file inside a JAR archive
\ No newline at end of file
diff --git a/core/testlib/README.md b/core/testlib/README.md
new file mode 100644
index 00000000000..437680a18fd
--- /dev/null
+++ b/core/testlib/README.md
@@ -0,0 +1,3 @@
+This directory contains a synthetic JAR (`fakejar.jar`) that is needed for `org.testcontainers.utility.MountableFileTest`.
+
+The `create_fakejar.sh` script may be used to recreate it.
\ No newline at end of file
diff --git a/core/testlib/create_fakejar.sh b/core/testlib/create_fakejar.sh
new file mode 100755
index 00000000000..e944cec94bc
--- /dev/null
+++ b/core/testlib/create_fakejar.sh
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+rm fakejar.jar
+zip -r fakejar.jar META-INF/ recursive/
+mv fakejar.jar repo/fakejar/fakejar/0/fakejar-0.jar
\ No newline at end of file
diff --git a/core/testlib/recursive/dir/content.txt b/core/testlib/recursive/dir/content.txt
new file mode 100644
index 00000000000..d4e6444e225
--- /dev/null
+++ b/core/testlib/recursive/dir/content.txt
@@ -0,0 +1 @@
+This is example content to show recursive mounting of files from inside a JAR archive.
\ No newline at end of file
diff --git a/core/testlib/repo/fakejar/fakejar/0/fakejar-0.jar b/core/testlib/repo/fakejar/fakejar/0/fakejar-0.jar
new file mode 100644
index 00000000000..9cd86a3c137
Binary files /dev/null and b/core/testlib/repo/fakejar/fakejar/0/fakejar-0.jar differ
diff --git a/core/testlib/repo/fakejar/fakejar/0/fakejar-0.pom b/core/testlib/repo/fakejar/fakejar/0/fakejar-0.pom
new file mode 100644
index 00000000000..dda792b1989
--- /dev/null
+++ b/core/testlib/repo/fakejar/fakejar/0/fakejar-0.pom
@@ -0,0 +1,9 @@
+
+
+ 4.0.0
+ fakejar
+ fakejar
+ 0
+ POM was created from install:install-file
+
diff --git a/core/testlib/repo/fakejar/fakejar/maven-metadata-local.xml b/core/testlib/repo/fakejar/fakejar/maven-metadata-local.xml
new file mode 100644
index 00000000000..cca5c37c2f1
--- /dev/null
+++ b/core/testlib/repo/fakejar/fakejar/maven-metadata-local.xml
@@ -0,0 +1,12 @@
+
+
+ fakejar
+ fakejar
+
+ 0
+
+ 0
+
+ 20170414082010
+
+