View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.plugins.clean;
20  
21  import java.io.ByteArrayOutputStream;
22  import java.io.File;
23  import java.io.IOException;
24  import java.io.RandomAccessFile;
25  import java.nio.channels.FileChannel;
26  import java.nio.channels.FileLock;
27  import java.nio.file.Files;
28  import java.nio.file.Path;
29  import java.nio.file.Paths;
30  import java.util.Collections;
31  
32  import org.apache.maven.plugin.MojoExecutionException;
33  import org.apache.maven.plugin.testing.AbstractMojoTestCase;
34  
35  import static org.apache.commons.io.FileUtils.copyDirectory;
36  import static org.codehaus.plexus.util.IOUtil.copy;
37  
38  /**
39   * Test the clean mojo.
40   *
41   * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
42   */
43  public class CleanMojoTest extends AbstractMojoTestCase {
44      /**
45       * Tests the simple removal of directories
46       *
47       * @throws Exception in case of an error.
48       */
49      public void testBasicClean() throws Exception {
50          String pluginPom = getBasedir() + "/src/test/resources/unit/basic-clean-test/plugin-pom.xml";
51  
52          // safety
53          copyDirectory(
54                  new File(getBasedir(), "src/test/resources/unit/basic-clean-test"),
55                  new File(getBasedir(), "target/test-classes/unit/basic-clean-test"));
56  
57          CleanMojo mojo = (CleanMojo) lookupMojo("clean", pluginPom);
58          assertNotNull(mojo);
59  
60          mojo.execute();
61  
62          assertFalse(
63                  "Directory exists",
64                  checkExists(getBasedir() + "/target/test-classes/unit/" + "basic-clean-test/buildDirectory"));
65          assertFalse(
66                  "Directory exists",
67                  checkExists(getBasedir() + "/target/test-classes/unit/basic-clean-test/" + "buildOutputDirectory"));
68          assertFalse(
69                  "Directory exists",
70                  checkExists(getBasedir() + "/target/test-classes/unit/basic-clean-test/" + "buildTestDirectory"));
71      }
72  
73      /**
74       * Tests the removal of files and nested directories
75       *
76       * @throws Exception in case of an error.
77       */
78      public void testCleanNestedStructure() throws Exception {
79          String pluginPom = getBasedir() + "/src/test/resources/unit/nested-clean-test/plugin-pom.xml";
80  
81          // safety
82          copyDirectory(
83                  new File(getBasedir(), "src/test/resources/unit/nested-clean-test"),
84                  new File(getBasedir(), "target/test-classes/unit/nested-clean-test"));
85  
86          CleanMojo mojo = (CleanMojo) lookupMojo("clean", pluginPom);
87          assertNotNull(mojo);
88  
89          mojo.execute();
90  
91          assertFalse(checkExists(getBasedir() + "/target/test-classes/unit/nested-clean-test/target"));
92          assertFalse(checkExists(getBasedir() + "/target/test-classes/unit/nested-clean-test/target/classes"));
93          assertFalse(checkExists(getBasedir() + "/target/test-classes/unit/nested-clean-test/target/test-classes"));
94      }
95  
96      /**
97       * Tests that no exception is thrown when all internal variables are empty and that it doesn't
98       * just remove whats there
99       *
100      * @throws Exception in case of an error.
101      */
102     public void testCleanEmptyDirectories() throws Exception {
103         String pluginPom = getBasedir() + "/src/test/resources/unit/empty-clean-test/plugin-pom.xml";
104 
105         // safety
106         copyDirectory(
107                 new File(getBasedir(), "src/test/resources/unit/empty-clean-test"),
108                 new File(getBasedir(), "target/test-classes/unit/empty-clean-test"));
109 
110         CleanMojo mojo = (CleanMojo) lookupEmptyMojo("clean", pluginPom);
111         assertNotNull(mojo);
112 
113         mojo.execute();
114 
115         assertTrue(checkExists(getBasedir() + "/target/test-classes/unit/empty-clean-test/testDirectoryStructure"));
116         assertTrue(checkExists(
117                 getBasedir() + "/target/test-classes/unit/empty-clean-test/" + "testDirectoryStructure/file.txt"));
118         assertTrue(checkExists(getBasedir() + "/target/test-classes/unit/empty-clean-test/"
119                 + "testDirectoryStructure/outputDirectory"));
120         assertTrue(checkExists(getBasedir() + "/target/test-classes/unit/empty-clean-test/"
121                 + "testDirectoryStructure/outputDirectory/file.txt"));
122     }
123 
124     /**
125      * Tests the removal of files using fileset
126      *
127      * @throws Exception in case of an error.
128      */
129     public void testFilesetsClean() throws Exception {
130         String pluginPom = getBasedir() + "/src/test/resources/unit/fileset-clean-test/plugin-pom.xml";
131 
132         // safety
133         copyDirectory(
134                 new File(getBasedir(), "src/test/resources/unit/fileset-clean-test"),
135                 new File(getBasedir(), "target/test-classes/unit/fileset-clean-test"));
136 
137         CleanMojo mojo = (CleanMojo) lookupMojo("clean", pluginPom);
138         assertNotNull(mojo);
139 
140         mojo.execute();
141 
142         // fileset 1
143         assertTrue(checkExists(getBasedir() + "/target/test-classes/unit/fileset-clean-test/target"));
144         assertTrue(checkExists(getBasedir() + "/target/test-classes/unit/fileset-clean-test/target/classes"));
145         assertFalse(checkExists(getBasedir() + "/target/test-classes/unit/fileset-clean-test/target/test-classes"));
146         assertTrue(checkExists(getBasedir() + "/target/test-classes/unit/fileset-clean-test/target/subdir"));
147         assertFalse(checkExists(getBasedir() + "/target/test-classes/unit/fileset-clean-test/target/classes/file.txt"));
148         assertTrue(checkEmpty(getBasedir() + "/target/test-classes/unit/fileset-clean-test/target/classes"));
149         assertFalse(checkEmpty(getBasedir() + "/target/test-classes/unit/fileset-clean-test/target/subdir"));
150         assertTrue(checkExists(getBasedir() + "/target/test-classes/unit/fileset-clean-test/target/subdir/file.txt"));
151 
152         // fileset 2
153         assertTrue(
154                 checkExists(getBasedir() + "/target/test-classes/unit/fileset-clean-test/" + "buildOutputDirectory"));
155         assertFalse(checkExists(
156                 getBasedir() + "/target/test-classes/unit/fileset-clean-test/" + "buildOutputDirectory/file.txt"));
157     }
158 
159     /**
160      * Tests the removal of a directory as file
161      *
162      * @throws Exception in case of an error.
163      */
164     public void testCleanInvalidDirectory() throws Exception {
165         String pluginPom = getBasedir() + "/src/test/resources/unit/invalid-directory-test/plugin-pom.xml";
166 
167         // safety
168         copyDirectory(
169                 new File(getBasedir(), "src/test/resources/unit/invalid-directory-test"),
170                 new File(getBasedir(), "target/test-classes/unit/invalid-directory-test"));
171 
172         CleanMojo mojo = (CleanMojo) lookupMojo("clean", pluginPom);
173         assertNotNull(mojo);
174 
175         try {
176             mojo.execute();
177 
178             fail("Should fail to delete a file treated as a directory");
179         } catch (MojoExecutionException expected) {
180             assertTrue(true);
181         }
182     }
183 
184     /**
185      * Tests the removal of a missing directory
186      *
187      * @throws Exception in case of an error.
188      */
189     public void testMissingDirectory() throws Exception {
190         String pluginPom = getBasedir() + "/src/test/resources/unit/missing-directory-test/plugin-pom.xml";
191 
192         // safety
193         copyDirectory(
194                 new File(getBasedir(), "src/test/resources/unit/missing-directory-test"),
195                 new File(getBasedir(), "target/test-classes/unit/missing-directory-test"));
196 
197         CleanMojo mojo = (CleanMojo) lookupMojo("clean", pluginPom);
198         assertNotNull(mojo);
199 
200         mojo.execute();
201 
202         assertFalse(checkExists(getBasedir() + "/target/test-classes/unit/missing-directory-test/does-not-exist"));
203     }
204 
205     /**
206      * Test the removal of a locked file on Windows systems.
207      * <p>
208      * Note: Unix systems doesn't lock any files.
209      * </p>
210      *
211      * @throws Exception in case of an error.
212      */
213     public void testCleanLockedFile() throws Exception {
214         if (!System.getProperty("os.name").toLowerCase().contains("windows")) {
215             assertTrue("Ignored this test on non Windows based systems", true);
216             return;
217         }
218 
219         String pluginPom = getBasedir() + "/src/test/resources/unit/locked-file-test/plugin-pom.xml";
220 
221         // safety
222         copyDirectory(
223                 new File(getBasedir(), "src/test/resources/unit/locked-file-test"),
224                 new File(getBasedir(), "target/test-classes/unit/locked-file-test"));
225 
226         CleanMojo mojo = (CleanMojo) lookupMojo("clean", pluginPom);
227         assertNotNull(mojo);
228 
229         File f = new File(getBasedir(), "target/test-classes/unit/locked-file-test/buildDirectory/file.txt");
230         try (FileChannel channel = new RandomAccessFile(f, "rw").getChannel();
231                 FileLock ignored = channel.lock()) {
232             mojo.execute();
233             fail("Should fail to delete a file that is locked");
234         } catch (MojoExecutionException expected) {
235             assertTrue(true);
236         }
237     }
238 
239     /**
240      * Test the removal of a locked file on Windows systems.
241      * <p>
242      * Note: Unix systems doesn't lock any files.
243      * </p>
244      *
245      * @throws Exception in case of an error.
246      */
247     public void testCleanLockedFileWithNoError() throws Exception {
248         if (!System.getProperty("os.name").toLowerCase().contains("windows")) {
249             assertTrue("Ignore this test on non Windows based systems", true);
250             return;
251         }
252 
253         String pluginPom = getBasedir() + "/src/test/resources/unit/locked-file-test/plugin-pom.xml";
254 
255         // safety
256         copyDirectory(
257                 new File(getBasedir(), "src/test/resources/unit/locked-file-test"),
258                 new File(getBasedir(), "target/test-classes/unit/locked-file-test"));
259 
260         CleanMojo mojo = (CleanMojo) lookupMojo("clean", pluginPom);
261         setVariableValueToObject(mojo, "failOnError", Boolean.FALSE);
262         assertNotNull(mojo);
263 
264         File f = new File(getBasedir(), "target/test-classes/unit/locked-file-test/buildDirectory/file.txt");
265         try (FileChannel channel = new RandomAccessFile(f, "rw").getChannel();
266                 FileLock ignored = channel.lock()) {
267             mojo.execute();
268             assertTrue(true);
269         } catch (MojoExecutionException expected) {
270             fail("Should display a warning when deleting a file that is locked");
271         }
272     }
273 
274     /**
275      * Test the followLink option with windows junctions
276      * @throws Exception
277      */
278     public void testFollowLinksWithWindowsJunction() throws Exception {
279         if (!System.getProperty("os.name").toLowerCase().contains("windows")) {
280             assertTrue("Ignore this test on non Windows based systems", true);
281             return;
282         }
283 
284         testSymlink((link, target) -> {
285             Process process = new ProcessBuilder()
286                     .directory(link.getParent().toFile())
287                     .command("cmd", "/c", "mklink", "/j", link.getFileName().toString(), target.toString())
288                     .start();
289             process.waitFor();
290             ByteArrayOutputStream baos = new ByteArrayOutputStream();
291             copy(process.getInputStream(), baos);
292             copy(process.getErrorStream(), baos);
293             if (!Files.exists(link)) {
294                 throw new IOException("Unable to create junction: " + baos);
295             }
296         });
297     }
298 
299     /**
300      * Test the followLink option with sym link
301      * @throws Exception
302      */
303     public void testFollowLinksWithSymLinkOnPosix() throws Exception {
304         if (System.getProperty("os.name").toLowerCase().contains("windows")) {
305             assertTrue("Ignore this test on Windows based systems", true);
306             return;
307         }
308 
309         testSymlink((link, target) -> {
310             try {
311                 Files.createSymbolicLink(link, target);
312             } catch (IOException e) {
313                 throw new IOException("Unable to create symbolic link", e);
314             }
315         });
316     }
317 
318     @FunctionalInterface
319     interface LinkCreator {
320         void createLink(Path link, Path target) throws Exception;
321     }
322 
323     private void testSymlink(LinkCreator linkCreator) throws Exception {
324         Cleaner cleaner = new Cleaner(null, null, false, null, null);
325         Path testDir = Paths.get("target/test-classes/unit/test-dir").toAbsolutePath();
326         Path dirWithLnk = testDir.resolve("dir");
327         Path orgDir = testDir.resolve("org-dir");
328         Path jctDir = dirWithLnk.resolve("jct-dir");
329         Path file = orgDir.resolve("file.txt");
330 
331         // create directories, links and file
332         Files.createDirectories(dirWithLnk);
333         Files.createDirectories(orgDir);
334         Files.write(file, Collections.singleton("Hello world"));
335         linkCreator.createLink(jctDir, orgDir);
336         // delete
337         cleaner.delete(dirWithLnk.toFile(), null, false, true, false);
338         // verify
339         assertTrue(Files.exists(file));
340         assertFalse(Files.exists(jctDir));
341         assertTrue(Files.exists(orgDir));
342         assertFalse(Files.exists(dirWithLnk));
343 
344         // create directories, links and file
345         Files.createDirectories(dirWithLnk);
346         Files.createDirectories(orgDir);
347         Files.write(file, Collections.singleton("Hello world"));
348         linkCreator.createLink(jctDir, orgDir);
349         // delete
350         cleaner.delete(dirWithLnk.toFile(), null, true, true, false);
351         // verify
352         assertFalse(Files.exists(file));
353         assertFalse(Files.exists(jctDir));
354         assertTrue(Files.exists(orgDir));
355         assertFalse(Files.exists(dirWithLnk));
356     }
357 
358     /**
359      * @param dir a dir or a file
360      * @return true if a file/dir exists, false otherwise
361      */
362     private boolean checkExists(String dir) {
363         return new File(new File(dir).getAbsolutePath()).exists();
364     }
365 
366     /**
367      * @param dir a directory
368      * @return true if a dir is empty, false otherwise
369      */
370     private boolean checkEmpty(String dir) {
371         File[] files = new File(dir).listFiles();
372         return files == null || files.length == 0;
373     }
374 }