1
2
3
4
5
6
7 package net.alchim31.maven.yuicompressor;
8
9 import java.io.File;
10 import java.io.IOException;
11 import java.lang.reflect.Field;
12 import java.nio.charset.StandardCharsets;
13 import java.nio.file.Files;
14 import java.nio.file.StandardOpenOption;
15 import java.util.Arrays;
16 import java.util.Collection;
17 import java.util.HashSet;
18 import java.util.Set;
19
20 import org.codehaus.plexus.build.DefaultBuildContext;
21 import org.junit.jupiter.api.Assertions;
22 import org.junit.jupiter.api.BeforeEach;
23 import org.junit.jupiter.api.Test;
24 import org.junit.jupiter.api.extension.ExtendWith;
25 import org.junit.jupiter.api.io.TempDir;
26 import org.mockito.ArgumentMatchers;
27 import org.mockito.Mock;
28 import org.mockito.Mockito;
29 import org.mockito.junit.jupiter.MockitoExtension;
30 import org.sonatype.plexus.build.incremental.BuildContext;
31
32
33
34
35
36 @ExtendWith(MockitoExtension.class)
37 public class AggregationTestCase {
38
39
40 @TempDir
41 File dir;
42
43
44 @Mock
45 BuildContext legacyBuildContext;
46
47
48 DefaultBuildContext defaultBuildContext;
49
50
51
52
53
54
55
56 @BeforeEach
57 void setUp() throws IOException {
58
59 Mockito.lenient().when(this.legacyBuildContext.newFileOutputStream(ArgumentMatchers.any(File.class)))
60 .thenAnswer(invocation -> Files.newOutputStream(((File) invocation.getArgument(0)).toPath()));
61 this.defaultBuildContext = new DefaultBuildContext(this.legacyBuildContext);
62 }
63
64
65
66
67
68
69
70 @Test
71 void test0to1() throws IOException {
72 final var target = new Aggregation();
73 target.setOutput(this.dir.toPath().resolve("output.js").toFile());
74
75 Assertions.assertFalse(target.getOutput().exists());
76 target.run(null, this.defaultBuildContext);
77 Assertions.assertFalse(target.getOutput().exists());
78
79 target.setIncludes(new String[] {});
80 Assertions.assertFalse(target.getOutput().exists());
81 target.run(null, this.defaultBuildContext);
82 Assertions.assertFalse(target.getOutput().exists());
83
84 target.setIncludes(new String[] { "**/*.js" });
85 Assertions.assertFalse(target.getOutput().exists());
86 target.run(null, this.defaultBuildContext);
87 Assertions.assertFalse(target.getOutput().exists());
88 }
89
90
91
92
93
94
95
96 @Test
97 void test1to1() throws IOException {
98 final var f1 = this.dir.toPath().resolve("01.js").toFile();
99 Files.write(f1.toPath(), "1".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
100 final var target = new Aggregation();
101 target.setOutput(this.dir.toPath().resolve("output.js").toFile());
102 target.setIncludes(new String[] { f1.getName() });
103
104 Assertions.assertFalse(target.getOutput().exists());
105 target.run(null, this.defaultBuildContext);
106 Assertions.assertTrue(target.getOutput().exists());
107 Assertions.assertEquals(new String(Files.readAllBytes(f1.toPath()), StandardCharsets.UTF_8),
108 new String(Files.readAllBytes(target.getOutput().toPath()), StandardCharsets.UTF_8));
109 }
110
111
112
113
114
115
116
117 @Test
118 void test2to1() throws IOException {
119 final var f1 = this.dir.toPath().resolve("01.js").toFile();
120 Files.write(f1.toPath(), "1".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
121
122 final var f2 = this.dir.toPath().resolve("02.js").toFile();
123 Files.write(f2.toPath(), "22\n22".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
124
125 final var target = new Aggregation();
126 target.setOutput(this.dir.toPath().resolve("output.js").toFile());
127
128 target.setIncludes(new String[] { f1.getName(), f2.getName() });
129 Assertions.assertFalse(target.getOutput().exists());
130 target.run(null, this.defaultBuildContext);
131 Assertions.assertTrue(target.getOutput().exists());
132 Assertions.assertEquals(
133 new String(Files.readAllBytes(f1.toPath()), StandardCharsets.UTF_8)
134 + new String(Files.readAllBytes(f2.toPath()), StandardCharsets.UTF_8),
135 new String(Files.readAllBytes(target.getOutput().toPath()), StandardCharsets.UTF_8));
136
137 target.getOutput().delete();
138 target.setIncludes(new String[] { "*.js" });
139 Assertions.assertFalse(target.getOutput().exists());
140 target.run(null, this.defaultBuildContext);
141 Assertions.assertTrue(target.getOutput().exists());
142 Assertions.assertEquals(
143 new String(Files.readAllBytes(f1.toPath()), StandardCharsets.UTF_8)
144 + new String(Files.readAllBytes(f2.toPath()), StandardCharsets.UTF_8),
145 new String(Files.readAllBytes(target.getOutput().toPath()), StandardCharsets.UTF_8));
146 }
147
148
149
150
151
152
153
154 @Test
155 void testNoDuplicateAggregation() throws IOException {
156 final var f1 = this.dir.toPath().resolve("01.js").toFile();
157 Files.write(f1.toPath(), "1".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
158
159 final var f2 = this.dir.toPath().resolve("02.js").toFile();
160 Files.write(f2.toPath(), "22\n22".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
161
162 final var target = new Aggregation();
163 target.setOutput(this.dir.toPath().resolve("output.js").toFile());
164
165 target.setIncludes(new String[] { f1.getName(), f1.getName(), f2.getName() });
166 Assertions.assertFalse(target.getOutput().exists());
167 target.run(null, this.defaultBuildContext);
168 Assertions.assertTrue(target.getOutput().exists());
169 Assertions.assertEquals(
170 new String(Files.readAllBytes(f1.toPath()), StandardCharsets.UTF_8)
171 + new String(Files.readAllBytes(f2.toPath()), StandardCharsets.UTF_8),
172 new String(Files.readAllBytes(target.getOutput().toPath()), StandardCharsets.UTF_8));
173
174 target.getOutput().delete();
175 target.setIncludes(new String[] { f1.getName(), "*.js" });
176 Assertions.assertFalse(target.getOutput().exists());
177 target.run(null, this.defaultBuildContext);
178 Assertions.assertTrue(target.getOutput().exists());
179 Assertions.assertEquals(
180 new String(Files.readAllBytes(f1.toPath()), StandardCharsets.UTF_8)
181 + new String(Files.readAllBytes(f2.toPath()), StandardCharsets.UTF_8),
182 new String(Files.readAllBytes(target.getOutput().toPath()), StandardCharsets.UTF_8));
183 }
184
185
186
187
188
189
190
191 @Test
192 void test2to1Order() throws IOException {
193 final var f1 = this.dir.toPath().resolve("01.js").toFile();
194 Files.write(f1.toPath(), "1".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
195
196 final var f2 = this.dir.toPath().resolve("02.js").toFile();
197 Files.write(f2.toPath(), "2".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
198
199 final var target = new Aggregation();
200 target.setOutput(this.dir.toPath().resolve("output.js").toFile());
201
202 target.setIncludes(new String[] { f2.getName(), f1.getName() });
203 Assertions.assertFalse(target.getOutput().exists());
204 target.run(null, this.defaultBuildContext);
205 Assertions.assertTrue(target.getOutput().exists());
206 Assertions.assertEquals(
207 new String(Files.readAllBytes(f2.toPath()), StandardCharsets.UTF_8)
208 + new String(Files.readAllBytes(f1.toPath()), StandardCharsets.UTF_8),
209 new String(Files.readAllBytes(target.getOutput().toPath()), StandardCharsets.UTF_8));
210 }
211
212
213
214
215
216
217
218 @Test
219 void test2to1WithNewLine() throws IOException {
220 final var f1 = this.dir.toPath().resolve("01.js").toFile();
221 Files.write(f1.toPath(), "1".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
222
223 final var f2 = this.dir.toPath().resolve("02.js").toFile();
224 Files.write(f2.toPath(), "22\n22".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
225
226 final var target = new Aggregation();
227 target.setOutput(this.dir.toPath().resolve("output.js").toFile());
228 target.setInsertNewLine(true);
229 target.setIncludes(new String[] { f1.getName(), f2.getName() });
230
231 Assertions.assertFalse(target.getOutput().exists());
232 target.run(null, this.defaultBuildContext);
233 Assertions.assertTrue(target.getOutput().exists());
234 Assertions.assertEquals(
235 new String(Files.readAllBytes(f1.toPath()), StandardCharsets.UTF_8) + "\n"
236 + new String(Files.readAllBytes(f2.toPath()), StandardCharsets.UTF_8) + "\n",
237 new String(Files.readAllBytes(target.getOutput().toPath()), StandardCharsets.UTF_8));
238 }
239
240
241
242
243
244
245
246 @Test
247 void testAbsolutePathFromInside() throws IOException {
248 final var f1 = this.dir.toPath().resolve("01.js").toFile();
249 Files.write(f1.toPath(), "1".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
250
251 final var f2 = this.dir.toPath().resolve("02.js").toFile();
252 Files.write(f2.toPath(), "22\n22".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
253
254 final var target = new Aggregation();
255 target.setOutput(this.dir.toPath().resolve("output.js").toFile());
256
257 target.setIncludes(new String[] { f1.getAbsolutePath(), f2.getName() });
258 Assertions.assertFalse(target.getOutput().exists());
259 target.run(null, this.defaultBuildContext);
260 Assertions.assertTrue(target.getOutput().exists());
261 Assertions.assertEquals(
262 new String(Files.readAllBytes(f1.toPath()), StandardCharsets.UTF_8)
263 + new String(Files.readAllBytes(f2.toPath()), StandardCharsets.UTF_8),
264 new String(Files.readAllBytes(target.getOutput().toPath()), StandardCharsets.UTF_8));
265 }
266
267
268
269
270
271
272
273 @Test
274 void testAbsolutePathFromOutside() throws IOException {
275 final var f1 = File.createTempFile("test-01", ".js");
276 Files.write(f1.toPath(), "1".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
277
278 final var f2 = this.dir.toPath().resolve("02.js").toFile();
279 Files.write(f2.toPath(), "22\n22".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
280
281 final var target = new Aggregation();
282 target.setOutput(this.dir.toPath().resolve("output.js").toFile());
283
284 try {
285 target.setIncludes(new String[] { f1.getAbsolutePath(), f2.getName() });
286 Assertions.assertFalse(target.getOutput().exists());
287 target.run(null, this.defaultBuildContext);
288 Assertions.assertTrue(target.getOutput().exists());
289 Assertions.assertEquals(
290 new String(Files.readAllBytes(f1.toPath()), StandardCharsets.UTF_8)
291 + new String(Files.readAllBytes(f2.toPath()), StandardCharsets.UTF_8),
292 new String(Files.readAllBytes(target.getOutput().toPath()), StandardCharsets.UTF_8));
293 } finally {
294 f1.delete();
295 }
296 }
297
298
299
300
301
302
303
304 @Test
305 void testAutoExcludeWildcards() throws IOException {
306 final var f1 = this.dir.toPath().resolve("01.js").toFile();
307 Files.write(f1.toPath(), "1".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
308
309 final var f2 = this.dir.toPath().resolve("02.js").toFile();
310 Files.write(f2.toPath(), "22\n22".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
311
312 final var target = new Aggregation();
313 target.setAutoExcludeWildcards(true);
314 target.setOutput(this.dir.toPath().resolve("output.js").toFile());
315
316 final Collection<File> previouslyIncluded = new HashSet<>();
317 previouslyIncluded.add(f1.getCanonicalFile());
318
319 target.setIncludes(new String[] { f1.getName(), f2.getName() });
320 Assertions.assertFalse(target.getOutput().exists());
321
322 final var content = target.run(previouslyIncluded, this.defaultBuildContext);
323 Assertions.assertEquals(2, content.size());
324 Assertions.assertTrue(target.getOutput().exists());
325 Assertions.assertEquals(
326 new String(Files.readAllBytes(f1.toPath()), StandardCharsets.UTF_8)
327 + new String(Files.readAllBytes(f2.toPath()), StandardCharsets.UTF_8),
328 new String(Files.readAllBytes(target.getOutput().toPath()), StandardCharsets.UTF_8));
329
330 target.getOutput().delete();
331 target.setIncludes(new String[] { "*.js" });
332 Assertions.assertFalse(target.getOutput().exists());
333
334 Assertions.assertEquals(target.run(previouslyIncluded, this.defaultBuildContext),
335 Arrays.asList(f2.getCanonicalFile()));
336 Assertions.assertTrue(target.getOutput().exists());
337 Assertions.assertEquals(new String(Files.readAllBytes(f2.toPath()), StandardCharsets.UTF_8),
338 new String(Files.readAllBytes(target.getOutput().toPath()), StandardCharsets.UTF_8));
339 }
340
341
342
343
344
345
346
347 @Test
348 void testInsertFileHeader() throws IOException {
349 final var f1 = this.dir.toPath().resolve("01.js").toFile();
350 Files.write(f1.toPath(), "content1".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
351
352 final var target = new Aggregation();
353 target.setOutput(this.dir.toPath().resolve("output.js").toFile());
354 target.setIncludes(new String[] { f1.getName() });
355 setField(target, "insertFileHeader", true);
356
357 target.run(null, this.defaultBuildContext);
358
359 final var result = new String(Files.readAllBytes(target.getOutput().toPath()), StandardCharsets.UTF_8);
360 Assertions.assertTrue(result.startsWith("/*01.js*/"), "Expected file header '/*01.js*/' but was: " + result);
361 Assertions.assertTrue(result.contains("content1"), "Expected file content after header");
362 }
363
364
365
366
367
368
369
370 @Test
371 void testInsertFileHeaderWithNewLine() throws IOException {
372 final var f1 = this.dir.toPath().resolve("myfile.js").toFile();
373 Files.write(f1.toPath(), "abc".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
374
375 final var target = new Aggregation();
376 target.setOutput(this.dir.toPath().resolve("output.js").toFile());
377 target.setIncludes(new String[] { f1.getName() });
378 target.setInsertNewLine(true);
379 setField(target, "insertFileHeader", true);
380
381 target.run(null, this.defaultBuildContext);
382
383 final var result = new String(Files.readAllBytes(target.getOutput().toPath()), StandardCharsets.UTF_8);
384
385 Assertions.assertTrue(result.startsWith("/*myfile.js*/\n"),
386 "Expected header with trailing newline, but was: " + result);
387 }
388
389
390
391
392
393
394
395 @Test
396 void testFixLastSemicolon() throws IOException {
397 final var f1 = this.dir.toPath().resolve("01.js").toFile();
398 Files.write(f1.toPath(), "var a=1".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
399
400 final var f2 = this.dir.toPath().resolve("02.js").toFile();
401 Files.write(f2.toPath(), "var b=2".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
402
403 final var target = new Aggregation();
404 target.setOutput(this.dir.toPath().resolve("output.js").toFile());
405 target.setIncludes(new String[] { f1.getName(), f2.getName() });
406 setField(target, "fixLastSemicolon", true);
407
408 target.run(null, this.defaultBuildContext);
409
410 final var result = new String(Files.readAllBytes(target.getOutput().toPath()), StandardCharsets.UTF_8);
411
412 Assertions.assertEquals("var a=1;var b=2;", result);
413 }
414
415
416
417
418
419
420
421 @Test
422 void testRemoveIncluded() throws IOException {
423 final var f1 = this.dir.toPath().resolve("01.js").toFile();
424 Files.write(f1.toPath(), "1".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
425
426 final var f2 = this.dir.toPath().resolve("02.js").toFile();
427 Files.write(f2.toPath(), "2".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
428
429 final var target = new Aggregation();
430 target.setOutput(this.dir.toPath().resolve("output.js").toFile());
431 target.setIncludes(new String[] { f1.getName(), f2.getName() });
432 setField(target, "removeIncluded", true);
433
434 target.run(null, this.defaultBuildContext);
435
436 Assertions.assertFalse(f1.exists(), "f1 should be removed after aggregation");
437 Assertions.assertFalse(f2.exists(), "f2 should be removed after aggregation");
438 Assertions.assertTrue(target.getOutput().exists(), "Output file should still exist");
439 }
440
441
442
443
444
445
446
447 @Test
448 void testInvalidInputDirectory_throwsIllegalStateException() throws IOException {
449 final var notADir = File.createTempFile("notadir", ".tmp");
450 notADir.deleteOnExit();
451
452 final var target = new Aggregation();
453
454 setField(target, "inputDir", notADir);
455 target.setOutput(this.dir.toPath().resolve("output.js").toFile());
456 target.setIncludes(new String[] { "*.js" });
457
458 Assertions.assertThrows(IllegalStateException.class, () -> target.run(null, this.defaultBuildContext));
459 }
460
461
462
463
464
465
466
467
468 @Test
469 void testIncrementalBuildWithDelta() throws IOException {
470 final var f1 = this.dir.toPath().resolve("01.js").toFile();
471 Files.write(f1.toPath(), "1".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
472
473
474 Mockito.when(this.legacyBuildContext.isIncremental()).thenReturn(true);
475
476 final var target = new Aggregation();
477 target.setOutput(this.dir.toPath().resolve("output.js").toFile());
478 target.setIncludes(new String[] { f1.getName() });
479
480 final Set<String> incrementalFiles = new HashSet<>();
481 incrementalFiles.add(f1.getCanonicalPath());
482
483 final var result = target.run(null, this.defaultBuildContext, incrementalFiles);
484 Assertions.assertEquals(1, result.size(), "Expected one file to be aggregated");
485 Assertions.assertTrue(target.getOutput().exists(), "Output should exist after aggregation with delta");
486 }
487
488
489
490
491
492
493
494
495 @Test
496 void testIncrementalBuildNoDelta() throws IOException {
497 final var f1 = this.dir.toPath().resolve("01.js").toFile();
498 Files.write(f1.toPath(), "1".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
499
500
501 Mockito.when(this.legacyBuildContext.isIncremental()).thenReturn(true);
502
503 final var target = new Aggregation();
504 target.setOutput(this.dir.toPath().resolve("output.js").toFile());
505 target.setIncludes(new String[] { f1.getName() });
506
507
508 final Set<String> incrementalFiles = new HashSet<>();
509
510 final var result = target.run(null, this.defaultBuildContext, incrementalFiles);
511 Assertions.assertTrue(result.isEmpty(), "Expected no files to be aggregated when no delta");
512 Assertions.assertFalse(target.getOutput().exists(), "Output should not be created when no delta");
513 }
514
515
516
517
518
519
520
521 @Test
522 void testExcludes_wildcardIncludeWithExclude() throws IOException {
523 final var f1 = this.dir.toPath().resolve("include.js").toFile();
524 Files.write(f1.toPath(), "include".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
525
526 final var f2 = this.dir.toPath().resolve("exclude.js").toFile();
527 Files.write(f2.toPath(), "exclude".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
528
529 final var target = new Aggregation();
530 target.setOutput(this.dir.toPath().resolve("output.js").toFile());
531 target.setIncludes(new String[] { "*.js" });
532 setField(target, "excludes", new String[] { "exclude.js" });
533
534 target.run(null, this.defaultBuildContext);
535
536 Assertions.assertTrue(target.getOutput().exists());
537 final var result = new String(Files.readAllBytes(target.getOutput().toPath()), StandardCharsets.UTF_8);
538 Assertions.assertEquals("include", result, "Excluded file content should not appear in output");
539 }
540
541
542
543
544
545
546
547
548
549
550
551 private static void setField(Object target, String fieldName, Object value) {
552 try {
553 final Field field = target.getClass().getDeclaredField(fieldName);
554 field.setAccessible(true);
555 field.set(target, value);
556 } catch (NoSuchFieldException | IllegalAccessException e) {
557 throw new RuntimeException("Cannot set field '" + fieldName + "'", e);
558 }
559 }
560 }