View Javadoc
1   /*
2    * YuiCompressor Maven plugin
3    *
4    * Copyright 2012-2025 Hazendaz.
5    *
6    * Licensed under the GNU Lesser General Public License (LGPL),
7    * version 2.1 or later (the "License").
8    * You may not use this file except in compliance with the License.
9    * You may read the licence in the 'lgpl.txt' file in the root folder of
10   * project or obtain a copy at
11   *
12   *     https://www.gnu.org/licenses/lgpl-2.1.html
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" basis,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package net.alchim31.maven.yuicompressor;
21  
22  import java.io.File;
23  import java.io.IOException;
24  import java.nio.charset.StandardCharsets;
25  import java.nio.file.Files;
26  import java.nio.file.StandardOpenOption;
27  import java.util.Arrays;
28  import java.util.Collection;
29  import java.util.HashSet;
30  
31  import org.codehaus.plexus.build.DefaultBuildContext;
32  import org.junit.jupiter.api.Assertions;
33  import org.junit.jupiter.api.BeforeEach;
34  import org.junit.jupiter.api.Test;
35  import org.junit.jupiter.api.extension.ExtendWith;
36  import org.junit.jupiter.api.io.TempDir;
37  import org.mockito.ArgumentMatchers;
38  import org.mockito.Mock;
39  import org.mockito.Mockito;
40  import org.mockito.junit.jupiter.MockitoExtension;
41  import org.sonatype.plexus.build.incremental.BuildContext;
42  
43  /**
44   * The Class AggregationTestCase.
45   */
46  // Note: public here needed for javadocs to work so don't remove it
47  @ExtendWith(MockitoExtension.class)
48  public class AggregationTestCase {
49  
50      /** The dir. */
51      @TempDir
52      File dir;
53  
54      /** The legacy build context. */
55      @Mock
56      BuildContext legacyBuildContext;
57  
58      /** The default build context. */
59      DefaultBuildContext defaultBuildContext;
60  
61      /**
62       * Sets the up.
63       *
64       * @throws IOException
65       *             the io exception
66       */
67      @BeforeEach
68      void setUp() throws IOException {
69          // Ensure the mock returns a real OutputStream for output files
70          Mockito.lenient().when(this.legacyBuildContext.newFileOutputStream(ArgumentMatchers.any(File.class)))
71                  .thenAnswer(invocation -> Files.newOutputStream(((File) invocation.getArgument(0)).toPath()));
72          this.defaultBuildContext = new DefaultBuildContext(this.legacyBuildContext);
73      }
74  
75      /**
76       * Test 0 to 1.
77       *
78       * @throws IOException
79       *             the IO exception
80       */
81      @Test
82      void test0to1() throws IOException {
83          final var target = new Aggregation();
84          target.setOutput(this.dir.toPath().resolve("output.js").toFile());
85  
86          Assertions.assertFalse(target.getOutput().exists());
87          target.run(null, this.defaultBuildContext);
88          Assertions.assertFalse(target.getOutput().exists());
89  
90          target.setIncludes(new String[] {});
91          Assertions.assertFalse(target.getOutput().exists());
92          target.run(null, this.defaultBuildContext);
93          Assertions.assertFalse(target.getOutput().exists());
94  
95          target.setIncludes(new String[] { "**/*.js" });
96          Assertions.assertFalse(target.getOutput().exists());
97          target.run(null, this.defaultBuildContext);
98          Assertions.assertFalse(target.getOutput().exists());
99      }
100 
101     /**
102      * Test 1 to 1.
103      *
104      * @throws IOException
105      *             the IO exception
106      */
107     @Test
108     void test1to1() throws IOException {
109         final var f1 = this.dir.toPath().resolve("01.js").toFile();
110         Files.write(f1.toPath(), "1".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
111         final var target = new Aggregation();
112         target.setOutput(this.dir.toPath().resolve("output.js").toFile());
113         target.setIncludes(new String[] { f1.getName() });
114 
115         Assertions.assertFalse(target.getOutput().exists());
116         target.run(null, this.defaultBuildContext);
117         Assertions.assertTrue(target.getOutput().exists());
118         Assertions.assertEquals(new String(Files.readAllBytes(f1.toPath()), StandardCharsets.UTF_8),
119                 new String(Files.readAllBytes(target.getOutput().toPath()), StandardCharsets.UTF_8));
120     }
121 
122     /**
123      * Test 2 to 1.
124      *
125      * @throws IOException
126      *             the IO exception
127      */
128     @Test
129     void test2to1() throws IOException {
130         final var f1 = this.dir.toPath().resolve("01.js").toFile();
131         Files.write(f1.toPath(), "1".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
132 
133         final var f2 = this.dir.toPath().resolve("02.js").toFile();
134         Files.write(f2.toPath(), "22\n22".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
135 
136         final var target = new Aggregation();
137         target.setOutput(this.dir.toPath().resolve("output.js").toFile());
138 
139         target.setIncludes(new String[] { f1.getName(), f2.getName() });
140         Assertions.assertFalse(target.getOutput().exists());
141         target.run(null, this.defaultBuildContext);
142         Assertions.assertTrue(target.getOutput().exists());
143         Assertions.assertEquals(
144                 new String(Files.readAllBytes(f1.toPath()), StandardCharsets.UTF_8)
145                         + new String(Files.readAllBytes(f2.toPath()), StandardCharsets.UTF_8),
146                 new String(Files.readAllBytes(target.getOutput().toPath()), StandardCharsets.UTF_8));
147 
148         target.getOutput().delete();
149         target.setIncludes(new String[] { "*.js" });
150         Assertions.assertFalse(target.getOutput().exists());
151         target.run(null, this.defaultBuildContext);
152         Assertions.assertTrue(target.getOutput().exists());
153         Assertions.assertEquals(
154                 new String(Files.readAllBytes(f1.toPath()), StandardCharsets.UTF_8)
155                         + new String(Files.readAllBytes(f2.toPath()), StandardCharsets.UTF_8),
156                 new String(Files.readAllBytes(target.getOutput().toPath()), StandardCharsets.UTF_8));
157     }
158 
159     /**
160      * Test no duplicate aggregation.
161      *
162      * @throws IOException
163      *             the IO exception
164      */
165     @Test
166     void testNoDuplicateAggregation() throws IOException {
167         final var f1 = this.dir.toPath().resolve("01.js").toFile();
168         Files.write(f1.toPath(), "1".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
169 
170         final var f2 = this.dir.toPath().resolve("02.js").toFile();
171         Files.write(f2.toPath(), "22\n22".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
172 
173         final var target = new Aggregation();
174         target.setOutput(this.dir.toPath().resolve("output.js").toFile());
175 
176         target.setIncludes(new String[] { f1.getName(), f1.getName(), f2.getName() });
177         Assertions.assertFalse(target.getOutput().exists());
178         target.run(null, this.defaultBuildContext);
179         Assertions.assertTrue(target.getOutput().exists());
180         Assertions.assertEquals(
181                 new String(Files.readAllBytes(f1.toPath()), StandardCharsets.UTF_8)
182                         + new String(Files.readAllBytes(f2.toPath()), StandardCharsets.UTF_8),
183                 new String(Files.readAllBytes(target.getOutput().toPath()), StandardCharsets.UTF_8));
184 
185         target.getOutput().delete();
186         target.setIncludes(new String[] { f1.getName(), "*.js" });
187         Assertions.assertFalse(target.getOutput().exists());
188         target.run(null, this.defaultBuildContext);
189         Assertions.assertTrue(target.getOutput().exists());
190         Assertions.assertEquals(
191                 new String(Files.readAllBytes(f1.toPath()), StandardCharsets.UTF_8)
192                         + new String(Files.readAllBytes(f2.toPath()), StandardCharsets.UTF_8),
193                 new String(Files.readAllBytes(target.getOutput().toPath()), StandardCharsets.UTF_8));
194     }
195 
196     /**
197      * Test 2 to 1 order.
198      *
199      * @throws IOException
200      *             the IO exception
201      */
202     @Test
203     void test2to1Order() throws IOException {
204         final var f1 = this.dir.toPath().resolve("01.js").toFile();
205         Files.write(f1.toPath(), "1".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
206 
207         final var f2 = this.dir.toPath().resolve("02.js").toFile();
208         Files.write(f2.toPath(), "2".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
209 
210         final var target = new Aggregation();
211         target.setOutput(this.dir.toPath().resolve("output.js").toFile());
212 
213         target.setIncludes(new String[] { f2.getName(), f1.getName() });
214         Assertions.assertFalse(target.getOutput().exists());
215         target.run(null, this.defaultBuildContext);
216         Assertions.assertTrue(target.getOutput().exists());
217         Assertions.assertEquals(
218                 new String(Files.readAllBytes(f2.toPath()), StandardCharsets.UTF_8)
219                         + new String(Files.readAllBytes(f1.toPath()), StandardCharsets.UTF_8),
220                 new String(Files.readAllBytes(target.getOutput().toPath()), StandardCharsets.UTF_8));
221     }
222 
223     /**
224      * Test 2 to 1 with new line.
225      *
226      * @throws IOException
227      *             the IO exception
228      */
229     @Test
230     void test2to1WithNewLine() throws IOException {
231         final var f1 = this.dir.toPath().resolve("01.js").toFile();
232         Files.write(f1.toPath(), "1".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
233 
234         final var f2 = this.dir.toPath().resolve("02.js").toFile();
235         Files.write(f2.toPath(), "22\n22".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
236 
237         final var target = new Aggregation();
238         target.setOutput(this.dir.toPath().resolve("output.js").toFile());
239         target.setInsertNewLine(true);
240         target.setIncludes(new String[] { f1.getName(), f2.getName() });
241 
242         Assertions.assertFalse(target.getOutput().exists());
243         target.run(null, this.defaultBuildContext);
244         Assertions.assertTrue(target.getOutput().exists());
245         Assertions.assertEquals(
246                 new String(Files.readAllBytes(f1.toPath()), StandardCharsets.UTF_8) + "\n"
247                         + new String(Files.readAllBytes(f2.toPath()), StandardCharsets.UTF_8) + "\n",
248                 new String(Files.readAllBytes(target.getOutput().toPath()), StandardCharsets.UTF_8));
249     }
250 
251     /**
252      * Test absolute path from inside.
253      *
254      * @throws IOException
255      *             the IO exception
256      */
257     @Test
258     void testAbsolutePathFromInside() throws IOException {
259         final var f1 = this.dir.toPath().resolve("01.js").toFile();
260         Files.write(f1.toPath(), "1".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
261 
262         final var f2 = this.dir.toPath().resolve("02.js").toFile();
263         Files.write(f2.toPath(), "22\n22".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
264 
265         final var target = new Aggregation();
266         target.setOutput(this.dir.toPath().resolve("output.js").toFile());
267 
268         target.setIncludes(new String[] { f1.getAbsolutePath(), f2.getName() });
269         Assertions.assertFalse(target.getOutput().exists());
270         target.run(null, this.defaultBuildContext);
271         Assertions.assertTrue(target.getOutput().exists());
272         Assertions.assertEquals(
273                 new String(Files.readAllBytes(f1.toPath()), StandardCharsets.UTF_8)
274                         + new String(Files.readAllBytes(f2.toPath()), StandardCharsets.UTF_8),
275                 new String(Files.readAllBytes(target.getOutput().toPath()), StandardCharsets.UTF_8));
276     }
277 
278     /**
279      * Test absolute path from outside.
280      *
281      * @throws IOException
282      *             the IO exception
283      */
284     @Test
285     void testAbsolutePathFromOutside() throws IOException {
286         final var f1 = File.createTempFile("test-01", ".js");
287         Files.write(f1.toPath(), "1".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
288 
289         final var f2 = this.dir.toPath().resolve("02.js").toFile();
290         Files.write(f2.toPath(), "22\n22".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
291 
292         final var target = new Aggregation();
293         target.setOutput(this.dir.toPath().resolve("output.js").toFile());
294 
295         try {
296             target.setIncludes(new String[] { f1.getAbsolutePath(), f2.getName() });
297             Assertions.assertFalse(target.getOutput().exists());
298             target.run(null, this.defaultBuildContext);
299             Assertions.assertTrue(target.getOutput().exists());
300             Assertions.assertEquals(
301                     new String(Files.readAllBytes(f1.toPath()), StandardCharsets.UTF_8)
302                             + new String(Files.readAllBytes(f2.toPath()), StandardCharsets.UTF_8),
303                     new String(Files.readAllBytes(target.getOutput().toPath()), StandardCharsets.UTF_8));
304         } finally {
305             f1.delete();
306         }
307     }
308 
309     /**
310      * Test auto exclude wildcards.
311      *
312      * @throws IOException
313      *             the IO exception
314      */
315     @Test
316     void testAutoExcludeWildcards() throws IOException {
317         final var f1 = this.dir.toPath().resolve("01.js").toFile();
318         Files.write(f1.toPath(), "1".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
319 
320         final var f2 = this.dir.toPath().resolve("02.js").toFile();
321         Files.write(f2.toPath(), "22\n22".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
322 
323         final var target = new Aggregation();
324         target.setAutoExcludeWildcards(true);
325         target.setOutput(this.dir.toPath().resolve("output.js").toFile());
326 
327         final Collection<File> previouslyIncluded = new HashSet<>();
328         previouslyIncluded.add(f1.getCanonicalFile());
329 
330         target.setIncludes(new String[] { f1.getName(), f2.getName() });
331         Assertions.assertFalse(target.getOutput().exists());
332         // First call uses path that does not deal with previouslyIncluded so both files are added
333         final var content = target.run(previouslyIncluded, this.defaultBuildContext);
334         Assertions.assertEquals(2, content.size());
335         Assertions.assertTrue(target.getOutput().exists());
336         Assertions.assertEquals(
337                 new String(Files.readAllBytes(f1.toPath()), StandardCharsets.UTF_8)
338                         + new String(Files.readAllBytes(f2.toPath()), StandardCharsets.UTF_8),
339                 new String(Files.readAllBytes(target.getOutput().toPath()), StandardCharsets.UTF_8));
340 
341         target.getOutput().delete();
342         target.setIncludes(new String[] { "*.js" });
343         Assertions.assertFalse(target.getOutput().exists());
344         // f1 was in previouslyIncluded so it is not included
345         Assertions.assertEquals(target.run(previouslyIncluded, this.defaultBuildContext),
346                 Arrays.asList(f2.getCanonicalFile()));
347         Assertions.assertTrue(target.getOutput().exists());
348         Assertions.assertEquals(new String(Files.readAllBytes(f2.toPath()), StandardCharsets.UTF_8),
349                 new String(Files.readAllBytes(target.getOutput().toPath()), StandardCharsets.UTF_8));
350     }
351 }