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