Skip to content

Commit 6e92bf9

Browse files
test: add tests for embed plugin (#12)
* Add test dependencies * add simple java test app * add missing dependency * test in progress * add test on annotated fields * add ci * fix surefire version * Update maven-plugin/src/test/java/io/github/chains_project/classport/plugin/EmbeddingMojoTest.java Co-authored-by: Aman Sharma <[email protected]> * build project before tests * add maven compiler plugin * Up --------- Co-authored-by: Aman Sharma <[email protected]>
1 parent 83ad43b commit 6e92bf9

File tree

9 files changed

+401
-10
lines changed

9 files changed

+401
-10
lines changed

.github/workflows/tests.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: tests
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches: [ main ]
7+
8+
jobs:
9+
test:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v4
13+
with:
14+
submodules: true
15+
- name: Setup JDK 17
16+
uses: actions/setup-java@v4
17+
with:
18+
distribution: 'temurin'
19+
java-version: '17'
20+
- name: Build project
21+
run: mvn install -DskipTests
22+
- name: Run tests
23+
run: mvn test

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,6 @@ target/
77
.factorypath
88
.project
99
.settings/
10+
11+
# vscode settings
12+
settings.json

classport-agent/pom.xml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,9 @@
7575
</plugin>
7676
<plugin>
7777
<artifactId>maven-compiler-plugin</artifactId>
78-
<version>3.8.0</version>
7978
</plugin>
8079
<plugin>
8180
<artifactId>maven-surefire-plugin</artifactId>
82-
<version>2.22.1</version>
8381
</plugin>
8482
<plugin>
8583
<artifactId>maven-install-plugin</artifactId>

classport-analyser/pom.xml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,9 @@
9292
</plugin>
9393
<plugin>
9494
<artifactId>maven-compiler-plugin</artifactId>
95-
<version>3.8.0</version>
9695
</plugin>
9796
<plugin>
9897
<artifactId>maven-surefire-plugin</artifactId>
99-
<version>2.22.1</version>
10098
</plugin>
10199
<plugin>
102100
<artifactId>maven-install-plugin</artifactId>

maven-plugin/pom.xml

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,16 +80,53 @@
8080
<version>2.2.1</version>
8181
<scope>provided</scope>
8282
</dependency>
83-
<dependency>
84-
<groupId>junit</groupId>
85-
<artifactId>junit</artifactId>
86-
<version>3.8.1</version>
87-
<scope>test</scope>
88-
</dependency>
83+
<dependency>
84+
<groupId>org.junit.jupiter</groupId>
85+
<artifactId>junit-jupiter-api</artifactId>
86+
<version>5.11.3</version>
87+
<scope>test</scope>
88+
</dependency>
89+
<dependency>
90+
<groupId>org.junit.jupiter</groupId>
91+
<artifactId>junit-jupiter-engine</artifactId>
92+
<version>5.11.3</version>
93+
<scope>test</scope>
94+
</dependency>
95+
96+
97+
<dependency>
98+
<groupId>org.hamcrest</groupId>
99+
<artifactId>hamcrest</artifactId>
100+
<version>3.0</version>
101+
<scope>test</scope>
102+
</dependency>
103+
104+
<dependency>
105+
<groupId>org.apache.maven.shared</groupId>
106+
<artifactId>maven-invoker</artifactId>
107+
<version>3.3.0</version>
108+
</dependency>
109+
110+
<dependency>
111+
<groupId>org.twdata.maven</groupId>
112+
<artifactId>mojo-executor</artifactId>
113+
<version>2.4.1-m2</version>
114+
<scope>test</scope>
115+
</dependency>
116+
117+
89118
</dependencies>
90119

91120
<build>
92121
<plugins>
122+
<plugin>
123+
<groupId>org.apache.maven.plugins</groupId>
124+
<artifactId>maven-surefire-plugin</artifactId>
125+
</plugin>
126+
<plugin>
127+
<groupId>org.apache.maven.plugins</groupId>
128+
<artifactId>maven-compiler-plugin</artifactId>
129+
</plugin>
93130
<plugin>
94131
<groupId>org.apache.maven.plugins</groupId>
95132
<artifactId>maven-plugin-plugin</artifactId>
Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
package io.github.chains_project.classport.plugin;
2+
3+
import java.io.File;
4+
import java.io.FileInputStream;
5+
import java.io.FileOutputStream;
6+
import java.io.IOException;
7+
import java.nio.file.Files;
8+
import java.nio.file.Path;
9+
import java.nio.file.Paths;
10+
import java.util.Arrays;
11+
import java.util.Comparator;
12+
import java.util.zip.ZipEntry;
13+
import java.util.zip.ZipInputStream;
14+
15+
import org.apache.maven.shared.invoker.DefaultInvocationRequest;
16+
import org.apache.maven.shared.invoker.DefaultInvoker;
17+
import org.apache.maven.shared.invoker.InvocationRequest;
18+
import org.apache.maven.shared.invoker.InvocationResult;
19+
import org.apache.maven.shared.invoker.Invoker;
20+
import org.apache.maven.shared.invoker.MavenInvocationException;
21+
import static org.junit.jupiter.api.Assertions.assertEquals;
22+
import static org.junit.jupiter.api.Assertions.assertTrue;
23+
import org.junit.jupiter.api.Test;
24+
import org.junit.jupiter.api.io.TempDir;
25+
import org.objectweb.asm.AnnotationVisitor;
26+
import org.objectweb.asm.ClassReader;
27+
import org.objectweb.asm.ClassVisitor;
28+
import org.objectweb.asm.Opcodes;
29+
30+
import io.github.chains_project.classport.commons.ClassportInfo;
31+
32+
33+
public class EmbeddingMojoTest {
34+
35+
private final Class<?> annotationClass = ClassportInfo.class;
36+
private final String annotatedProjectClassPath = "src/test/resources/test-app/target/classes/org/example/Main.class";
37+
38+
@TempDir
39+
Path tempDir;
40+
41+
@Test
42+
void shouldEmbedAllProjectClasses_whenPluginRuns() throws MavenInvocationException, IOException {
43+
44+
assertEquals(0, getExitCodeRunMavenPlugin(), "Maven plugin not executed.");
45+
46+
File projectClassFilesDir = new File("src/test/resources/test-app/target");
47+
File classportFilesDir = new File("src/test/resources/test-app/classport-files");
48+
assertTrue(projectClassFilesDir.exists(), "Missing target dir. Something wrong in execution of the Maven plugin.");
49+
assertTrue(classportFilesDir.exists(), "Classport-files dir not found. Something wrong in execution of the Maven plugin.");
50+
51+
assertTrue(areAllClassesEmbedded(projectClassFilesDir, true), "Not all project classes are embedded with ClassportInfo annotation");
52+
checkAnnotationValues(
53+
"org.example",
54+
"0.1.0",
55+
"org.example:hello:jar:0.1.0",
56+
"hello"
57+
);
58+
59+
cleanUpArtifactsDir(projectClassFilesDir.toPath());
60+
cleanUpArtifactsDir(classportFilesDir.toPath());
61+
}
62+
63+
64+
@Test
65+
void shouldEmbedAllDependencyClasses_whenPluginRuns() throws MavenInvocationException, IOException {
66+
67+
assertEquals(0, getExitCodeRunMavenPlugin(), "Maven plugin not executed.");
68+
69+
File classportFilesDir = new File("src/test/resources/test-app/classport-files");
70+
File projectClassFilesDir = new File("src/test/resources/test-app/target");
71+
assertTrue(projectClassFilesDir.exists(), "Missing target dir. Something wrong in execution of the Maven plugin.");
72+
assertTrue(classportFilesDir.exists(), "Classport-files dir not found. Something wrong in execution of the Maven plugin.");
73+
74+
assertTrue(areAllClassesEmbedded(classportFilesDir, false), "Not all dependency classes are embedded with ClassportInfo annotation");
75+
76+
cleanUpArtifactsDir(projectClassFilesDir.toPath());
77+
cleanUpArtifactsDir(classportFilesDir.toPath());
78+
}
79+
80+
81+
private boolean areAllClassesEmbedded(File classportFilesDir, boolean areProjectClasses) {
82+
try {
83+
tempDir = Files.createTempDirectory("classportFilesTempDir");
84+
tempDir.toFile().deleteOnExit();
85+
86+
if (!areProjectClasses) {
87+
return processJarFiles(classportFilesDir, tempDir);
88+
} else {
89+
return processClassFilesInDirectory(classportFilesDir.toPath());
90+
}
91+
92+
} catch (IOException e) {
93+
e.printStackTrace();
94+
return false;
95+
}
96+
}
97+
98+
99+
private void cleanUpArtifactsDir(Path tempDir) {
100+
if (tempDir != null) {
101+
try {
102+
Files.walk(tempDir)
103+
.sorted(Comparator.reverseOrder())
104+
.map(Path::toFile)
105+
.forEach(file -> {
106+
if (file.isDirectory()) {
107+
if (file.list().length == 0) {
108+
file.delete();
109+
}
110+
} else {
111+
file.delete();
112+
}
113+
});
114+
} catch (IOException e) {
115+
e.printStackTrace();
116+
}
117+
}
118+
}
119+
120+
private boolean processJarFiles(File classportFilesDir, Path tempDir) {
121+
try {
122+
return Files.walk(classportFilesDir.toPath())
123+
.filter(file -> file.toString().endsWith(".jar"))
124+
.allMatch(file -> {
125+
try {
126+
unzip(file.toString(), tempDir.toString());
127+
128+
return processClassFilesInDirectory(tempDir);
129+
} catch (IOException ex) {
130+
ex.printStackTrace();
131+
return false;
132+
}
133+
});
134+
} catch (IOException e) {
135+
e.printStackTrace();
136+
return false;
137+
}
138+
}
139+
140+
private boolean processClassFilesInDirectory(Path directory) {
141+
try {
142+
return Files.walk(directory)
143+
.filter(f -> f.toString().endsWith(".class"))
144+
.allMatch(f -> readAnnotation(f.toString()));
145+
} catch (IOException ex) {
146+
ex.printStackTrace();
147+
return false;
148+
}
149+
}
150+
151+
private int getExitCodeRunMavenPlugin() throws MavenInvocationException, IOException{
152+
String projectDir = "src/test/resources/test-app";
153+
String goal = "io.github.chains-project:classport-maven-plugin:0.1.0-SNAPSHOT:embed";
154+
155+
InvocationRequest request = new DefaultInvocationRequest();
156+
request.setPomFile(new File(projectDir, "pom.xml"));
157+
request.setGoals(Arrays.asList(goal.split(" ")));
158+
request.setBatchMode(true);
159+
160+
Invoker invoker = new DefaultInvoker();
161+
162+
163+
InvocationResult result = invoker.execute(request);
164+
return result.getExitCode();
165+
166+
}
167+
168+
private static void unzip(String zipFile, String destFolder) throws IOException {
169+
try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile))) {
170+
ZipEntry entry;
171+
byte[] buffer = new byte[1024];
172+
while ((entry = zis.getNextEntry()) != null) {
173+
File newFile = new File(destFolder + File.separator + entry.getName());
174+
if (entry.isDirectory()) {
175+
newFile.mkdirs();
176+
} else {
177+
new File(newFile.getParent()).mkdirs();
178+
try (FileOutputStream fos = new FileOutputStream(newFile)) {
179+
int length;
180+
while ((length = zis.read(buffer)) > 0) {
181+
fos.write(buffer, 0, length);
182+
}
183+
}
184+
}
185+
}
186+
}
187+
}
188+
189+
private boolean readAnnotation(String classFilePath) {
190+
byte[] classBytes;
191+
try {
192+
classBytes = Files.readAllBytes(Paths.get(classFilePath));
193+
ClassReader classReader = new ClassReader(classBytes);
194+
195+
final boolean[] isAnnotationPresent = {false};
196+
197+
classReader.accept(new ClassVisitor(Opcodes.ASM9) {
198+
@Override
199+
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
200+
if (descriptor.equals(annotationClass.descriptorString())) {
201+
isAnnotationPresent[0] = true;
202+
return null;
203+
}
204+
205+
return super.visitAnnotation(descriptor, visible);
206+
}
207+
}, 0);
208+
209+
return isAnnotationPresent[0];
210+
} catch (IOException ex) {
211+
ex.printStackTrace();
212+
return false;
213+
}
214+
}
215+
216+
private void checkAnnotationValues(String expectedGroup, String expectedVersion, String expectedId, String expectedArtefac) throws IOException {
217+
// Load the class file
218+
byte[] classBytes = Files.readAllBytes(Paths.get(annotatedProjectClassPath));
219+
ClassReader classReader = new ClassReader(classBytes);
220+
221+
// Analyze the class using a custom ClassVisitor
222+
classReader.accept(new ClassVisitor(Opcodes.ASM9) {
223+
@Override
224+
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
225+
if (descriptor.equals(annotationClass.descriptorString())) {
226+
return new AnnotationVisitor(Opcodes.ASM9) {
227+
@Override
228+
public void visit(String name, Object value) {
229+
if ("group".equals(name)) {
230+
assertEquals(expectedGroup, value, "Annotation field 'value' is incorrect");
231+
}
232+
if ("version".equals(name)) {
233+
assertEquals(expectedVersion, value, "Annotation field 'version' is incorrect");
234+
}
235+
if ("id".equals(name)) {
236+
assertEquals(expectedId, value, "Annotation field 'id' is incorrect");
237+
}
238+
if ("artefact".equals(name)) {
239+
assertEquals(expectedArtefac, value, "Annotation field 'artefact' is incorrect");
240+
}
241+
}
242+
};
243+
}
244+
return super.visitAnnotation(descriptor, visible);
245+
}
246+
}, 0);
247+
}
248+
249+
}

0 commit comments

Comments
 (0)