Skip to content

Commit 5ccb305

Browse files
bsideuprnorth
authored andcommitted
Public API tests (#391)
* Add shading tests * do not shade jdbc module * Add public API tests to make sure that we don't expose classes from shaded packages
1 parent 66bd7c0 commit 5ccb305

File tree

4 files changed

+161
-25
lines changed

4 files changed

+161
-25
lines changed

shade-test/jar-file/pom.xml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,14 @@
1616
<dependency>
1717
<groupId>org.assertj</groupId>
1818
<artifactId>assertj-core</artifactId>
19-
<version>3.6.2</version>
19+
<version>3.8.0</version>
20+
<scope>test</scope>
21+
</dependency>
22+
23+
<dependency>
24+
<groupId>org.ow2.asm</groupId>
25+
<artifactId>asm-debug-all</artifactId>
26+
<version>5.2</version>
2027
<scope>test</scope>
2128
</dependency>
2229
</dependencies>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package org.testcontainers;
2+
3+
import java.io.IOException;
4+
import java.net.URI;
5+
import java.nio.file.FileSystem;
6+
import java.nio.file.FileSystems;
7+
import java.nio.file.Path;
8+
import java.nio.file.Paths;
9+
10+
import static java.util.Collections.emptyMap;
11+
12+
public abstract class AbstractJarFileTest {
13+
14+
public static Path root;
15+
16+
static {
17+
Path path = Paths.get("..", "..", "core", "target", "testcontainers-0-SNAPSHOT.jar");
18+
19+
try {
20+
FileSystem fileSystem = FileSystems.newFileSystem(URI.create("jar:" + path.toUri()), emptyMap());
21+
root = fileSystem.getPath("/");
22+
} catch (IOException e) {
23+
throw new RuntimeException(e);
24+
}
25+
}
26+
}

shade-test/jar-file/src/test/java/org/testcontainers/JarFileShadingTest.java

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,15 @@
11
package org.testcontainers;
22

33
import org.assertj.core.api.ListAssert;
4-
import org.junit.AfterClass;
5-
import org.junit.BeforeClass;
64
import org.junit.Test;
75

86
import java.io.IOException;
9-
import java.net.URI;
10-
import java.nio.file.*;
7+
import java.nio.file.Files;
8+
import java.nio.file.Path;
119

12-
import static java.util.Collections.emptyMap;
1310
import static org.assertj.core.api.Assertions.assertThat;
1411

15-
public class JarFileShadingTest {
16-
17-
private static FileSystem fileSystem;
18-
19-
private static Path root;
20-
21-
@BeforeClass
22-
public static void setUp() throws Exception {
23-
Path path = Paths.get("..", "..", "core", "target", "testcontainers-0-SNAPSHOT.jar");
24-
25-
fileSystem = FileSystems.newFileSystem(URI.create("jar:" + path.toUri()), emptyMap());
26-
27-
root = fileSystem.getRootDirectories().iterator().next();
28-
}
29-
30-
@AfterClass
31-
public static void tearDown() throws Exception {
32-
fileSystem.close();
33-
}
12+
public class JarFileShadingTest extends AbstractJarFileTest {
3413

3514
@Test
3615
public void testPackages() throws Exception {
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
package org.testcontainers;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import org.assertj.core.api.Assertions;
5+
import org.junit.Test;
6+
import org.junit.runner.RunWith;
7+
import org.junit.runners.Parameterized;
8+
import org.junit.runners.Parameterized.Parameters;
9+
import org.objectweb.asm.ClassReader;
10+
import org.objectweb.asm.Opcodes;
11+
import org.objectweb.asm.Type;
12+
import org.objectweb.asm.tree.ClassNode;
13+
import org.objectweb.asm.tree.FieldNode;
14+
import org.objectweb.asm.tree.MethodNode;
15+
16+
import java.io.IOException;
17+
import java.io.InputStream;
18+
import java.nio.file.FileVisitResult;
19+
import java.nio.file.Files;
20+
import java.nio.file.Path;
21+
import java.nio.file.SimpleFileVisitor;
22+
import java.nio.file.attribute.BasicFileAttributes;
23+
import java.util.ArrayList;
24+
import java.util.Arrays;
25+
import java.util.List;
26+
27+
import static org.assertj.core.api.Assertions.assertThat;
28+
29+
/**
30+
* This test checks that we don't expose any shaded class in our public API.
31+
* We use {@link Parameterized} runner here to create a test per public class in Testcontainers' JAR file.
32+
*/
33+
@RunWith(Parameterized.class)
34+
@RequiredArgsConstructor
35+
public class PublicBinaryAPITest extends AbstractJarFileTest {
36+
37+
private static String SHADED_PACKAGE = "org.testcontainers.shaded.";
38+
private static String SHADED_PACKAGE_PATH = SHADED_PACKAGE.replaceAll("\\.", "/");
39+
40+
static {
41+
Assertions.registerFormatterForType(ClassNode.class, it -> it.name);
42+
Assertions.registerFormatterForType(FieldNode.class, it -> it.name);
43+
Assertions.registerFormatterForType(MethodNode.class, it -> it.name + it.desc);
44+
}
45+
46+
@Parameters(name = "{0}")
47+
public static List<Object[]> data() throws Exception {
48+
List<Object[]> result = new ArrayList<>();
49+
50+
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
51+
52+
@Override
53+
public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
54+
String fileName = path.toString();
55+
56+
if (!fileName.endsWith(".class")) {
57+
return super.visitFile(path, attrs);
58+
}
59+
60+
if (!fileName.startsWith("/org/testcontainers/") ) {
61+
return super.visitFile(path, attrs);
62+
}
63+
64+
if (fileName.startsWith("/" + SHADED_PACKAGE_PATH)) {
65+
return super.visitFile(path, attrs);
66+
}
67+
68+
try(InputStream inputStream = Files.newInputStream(path)) {
69+
ClassReader reader = new ClassReader(inputStream);
70+
ClassNode node = new ClassNode();
71+
reader.accept(node, ClassReader.SKIP_CODE);
72+
if ((node.access & Opcodes.ACC_PUBLIC) != 0) {
73+
result.add(new Object[]{ fileName, node });
74+
}
75+
}
76+
77+
return super.visitFile(path, attrs);
78+
}
79+
});
80+
return result;
81+
}
82+
83+
private final String fileName;
84+
85+
private final ClassNode classNode;
86+
87+
@Test
88+
public void testSuperClass() {
89+
assertThat(classNode.superName)
90+
.doesNotStartWith(SHADED_PACKAGE_PATH);
91+
}
92+
93+
@Test
94+
public void testInterfaces() {
95+
assertThat(classNode.interfaces)
96+
.allSatisfy(it -> assertThat(it).doesNotStartWith(SHADED_PACKAGE_PATH));
97+
}
98+
99+
@Test
100+
public void testMethodReturnTypes() {
101+
assertThat(classNode.methods)
102+
.filteredOn(it -> (it.access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) != 0)
103+
.allSatisfy(it -> assertThat(Type.getReturnType(it.desc).getClassName()).doesNotStartWith(SHADED_PACKAGE));
104+
}
105+
106+
@Test
107+
public void testMethodArguments() {
108+
assertThat(classNode.methods)
109+
.filteredOn(it -> (it.access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) != 0)
110+
.allSatisfy(method -> assertThat(Arrays.asList(Type.getArgumentTypes(method.desc)))
111+
.extracting(Type::getClassName)
112+
.allSatisfy(it -> assertThat(it).doesNotStartWith(SHADED_PACKAGE))
113+
);
114+
}
115+
116+
@Test
117+
public void testFields() {
118+
assertThat(classNode.fields)
119+
.filteredOn(it -> (it.access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) != 0)
120+
.allSatisfy(it -> assertThat(Type.getType(it.desc).getClassName())
121+
.doesNotStartWith(SHADED_PACKAGE)
122+
);
123+
}
124+
}

0 commit comments

Comments
 (0)