blob: ba6d3df04415334ff9d52ca984dc4bc909e14dd1 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.plugins.clean;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import com.google.inject.Provides;
import com.google.inject.Singleton;
import org.apache.maven.execution.DefaultMavenExecutionRequest;
import org.apache.maven.execution.DefaultMavenExecutionResult;
import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.execution.MavenExecutionResult;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.logging.SystemStreamLog;
import org.apache.maven.plugin.testing.junit5.InjectMojo;
import org.apache.maven.plugin.testing.junit5.MojoTest;
import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
import org.codehaus.plexus.PlexusContainer;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledOnOs;
import org.junit.jupiter.api.condition.EnabledOnOs;
import org.junit.jupiter.api.condition.OS;
import static org.apache.maven.plugin.testing.junit5.MojoExtension.setVariableValueToObject;
import static org.codehaus.plexus.testing.PlexusExtension.getBasedir;
import static org.codehaus.plexus.util.IOUtil.copy;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
/**
* Test the clean mojo.
*
* @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
*/
@MojoTest
public class CleanMojoTest {
/**
* Tests the simple removal of directories
*
* @throws Exception in case of an error.
*/
@Test
@InjectMojo(goal = "clean", pom = "classpath:/unit/basic-clean-test/plugin-pom.xml")
public void testBasicClean(CleanMojo mojo) throws Exception {
mojo.execute();
assertFalse(
checkExists(getBasedir() + "/target/test-classes/unit/" + "basic-clean-test/buildDirectory"),
"Directory exists");
assertFalse(
checkExists(getBasedir() + "/target/test-classes/unit/basic-clean-test/" + "buildOutputDirectory"),
"Directory exists");
assertFalse(
checkExists(getBasedir() + "/target/test-classes/unit/basic-clean-test/" + "buildTestDirectory"),
"Directory exists");
}
/**
* Tests the removal of files and nested directories
*
* @throws Exception in case of an error.
*/
@Test
@InjectMojo(goal = "clean", pom = "classpath:/unit/nested-clean-test/plugin-pom.xml")
public void testCleanNestedStructure(CleanMojo mojo) throws Exception {
mojo.execute();
assertFalse(checkExists(getBasedir() + "/target/test-classes/unit/nested-clean-test/target"));
assertFalse(checkExists(getBasedir() + "/target/test-classes/unit/nested-clean-test/target/classes"));
assertFalse(checkExists(getBasedir() + "/target/test-classes/unit/nested-clean-test/target/test-classes"));
}
/**
* Tests that no exception is thrown when all internal variables are empty and that it doesn't
* just remove whats there
*
* @throws Exception in case of an error.
*/
@Test
@InjectMojo(goal = "clean", pom = "classpath:/unit/empty-clean-test/plugin-pom.xml")
public void testCleanEmptyDirectories(CleanMojo mojo) throws Exception {
mojo.execute();
assertTrue(checkExists(getBasedir() + "/target/test-classes/unit/empty-clean-test/testDirectoryStructure"));
assertTrue(checkExists(
getBasedir() + "/target/test-classes/unit/empty-clean-test/" + "testDirectoryStructure/file.txt"));
assertTrue(checkExists(getBasedir() + "/target/test-classes/unit/empty-clean-test/"
+ "testDirectoryStructure/outputDirectory"));
assertTrue(checkExists(getBasedir() + "/target/test-classes/unit/empty-clean-test/"
+ "testDirectoryStructure/outputDirectory/file.txt"));
}
/**
* Tests the removal of files using fileset
*
* @throws Exception in case of an error.
*/
@Test
@InjectMojo(goal = "clean", pom = "classpath:/unit/fileset-clean-test/plugin-pom.xml")
public void testFilesetsClean(CleanMojo mojo) throws Exception {
mojo.execute();
// fileset 1
assertTrue(checkExists(getBasedir() + "/target/test-classes/unit/fileset-clean-test/target"));
assertTrue(checkExists(getBasedir() + "/target/test-classes/unit/fileset-clean-test/target/classes"));
assertFalse(checkExists(getBasedir() + "/target/test-classes/unit/fileset-clean-test/target/test-classes"));
assertTrue(checkExists(getBasedir() + "/target/test-classes/unit/fileset-clean-test/target/subdir"));
assertFalse(checkExists(getBasedir() + "/target/test-classes/unit/fileset-clean-test/target/classes/file.txt"));
assertTrue(checkEmpty(getBasedir() + "/target/test-classes/unit/fileset-clean-test/target/classes"));
assertFalse(checkEmpty(getBasedir() + "/target/test-classes/unit/fileset-clean-test/target/subdir"));
assertTrue(checkExists(getBasedir() + "/target/test-classes/unit/fileset-clean-test/target/subdir/file.txt"));
// fileset 2
assertTrue(
checkExists(getBasedir() + "/target/test-classes/unit/fileset-clean-test/" + "buildOutputDirectory"));
assertFalse(checkExists(
getBasedir() + "/target/test-classes/unit/fileset-clean-test/" + "buildOutputDirectory/file.txt"));
}
/**
* Tests the removal of a directory as file
*
* @throws Exception in case of an error.
*/
@Test
@InjectMojo(goal = "clean", pom = "classpath:/unit/invalid-directory-test/plugin-pom.xml")
public void testCleanInvalidDirectory(CleanMojo mojo) throws Exception {
assertThrows(MojoExecutionException.class, mojo::execute);
}
/**
* Tests the removal of a missing directory
*
* @throws Exception in case of an error.
*/
@Test
@InjectMojo(goal = "clean", pom = "classpath:/unit/missing-directory-test/plugin-pom.xml")
public void testMissingDirectory(CleanMojo mojo) throws Exception {
mojo.execute();
assertFalse(checkExists(getBasedir() + "/target/test-classes/unit/missing-directory-test/does-not-exist"));
}
/**
* Test the removal of a locked file on Windows systems.
* <p>
* Note: Unix systems doesn't lock any files.
* </p>
*
* @throws Exception in case of an error.
*/
@Test
@EnabledOnOs(OS.WINDOWS)
@InjectMojo(goal = "clean", pom = "classpath:/unit/locked-file-test/plugin-pom.xml")
public void testCleanLockedFile(CleanMojo mojo) throws Exception {
File f = new File(getBasedir(), "target/test-classes/unit/locked-file-test/buildDirectory/file.txt");
try (FileChannel channel = new RandomAccessFile(f, "rw").getChannel();
FileLock ignored = channel.lock()) {
mojo.execute();
fail("Should fail to delete a file that is locked");
} catch (MojoExecutionException expected) {
assertTrue(true);
}
}
/**
* Test the removal of a locked file on Windows systems.
* <p>
* Note: Unix systems doesn't lock any files.
* </p>
*
* @throws Exception in case of an error.
*/
@Test
@EnabledOnOs(OS.WINDOWS)
@InjectMojo(goal = "clean", pom = "classpath:/unit/locked-file-test/plugin-pom.xml")
public void testCleanLockedFileWithNoError(CleanMojo mojo) throws Exception {
setVariableValueToObject(mojo, "failOnError", Boolean.FALSE);
assertNotNull(mojo);
File f = new File(getBasedir(), "target/test-classes/unit/locked-file-test/buildDirectory/file.txt");
try (FileChannel channel = new RandomAccessFile(f, "rw").getChannel();
FileLock ignored = channel.lock()) {
mojo.execute();
assertTrue(true);
} catch (MojoExecutionException expected) {
fail("Should display a warning when deleting a file that is locked");
}
}
/**
* Test the followLink option with windows junctions
*
* @throws Exception
*/
@Test
@EnabledOnOs(OS.WINDOWS)
public void testFollowLinksWithWindowsJunction() throws Exception {
testSymlink((link, target) -> {
Process process = new ProcessBuilder()
.directory(link.getParent().toFile())
.command("cmd", "/c", "mklink", "/j", link.getFileName().toString(), target.toString())
.start();
process.waitFor();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
copy(process.getInputStream(), baos);
copy(process.getErrorStream(), baos);
if (!Files.exists(link)) {
throw new IOException("Unable to create junction: " + baos);
}
});
}
/**
* Test the followLink option with sym link
*
* @throws Exception
*/
@Test
@DisabledOnOs(OS.WINDOWS)
public void testFollowLinksWithSymLinkOnPosix() throws Exception {
testSymlink((link, target) -> {
try {
Files.createSymbolicLink(link, target);
} catch (IOException e) {
throw new IOException("Unable to create symbolic link", e);
}
});
}
private void testSymlink(LinkCreator linkCreator) throws Exception {
// We use the SystemStreamLog() as the AbstractMojo class, because from there the Log is always provided
Cleaner cleaner = new Cleaner(null, new SystemStreamLog(), false, null, null);
Path testDir = Paths.get("target/test-classes/unit/test-dir").toAbsolutePath();
Path dirWithLnk = testDir.resolve("dir");
Path orgDir = testDir.resolve("org-dir");
Path jctDir = dirWithLnk.resolve("jct-dir");
Path file = orgDir.resolve("file.txt");
// create directories, links and file
Files.createDirectories(dirWithLnk);
Files.createDirectories(orgDir);
Files.write(file, Collections.singleton("Hello world"));
linkCreator.createLink(jctDir, orgDir);
// delete
cleaner.delete(dirWithLnk.toFile(), null, false, true, false);
// verify
assertTrue(Files.exists(file));
assertFalse(Files.exists(jctDir));
assertTrue(Files.exists(orgDir));
assertFalse(Files.exists(dirWithLnk));
// create directories, links and file
Files.createDirectories(dirWithLnk);
Files.createDirectories(orgDir);
Files.write(file, Collections.singleton("Hello world"));
linkCreator.createLink(jctDir, orgDir);
// delete
cleaner.delete(dirWithLnk.toFile(), null, true, true, false);
// verify
assertFalse(Files.exists(file));
assertFalse(Files.exists(jctDir));
assertTrue(Files.exists(orgDir));
assertFalse(Files.exists(dirWithLnk));
}
@Provides
@Singleton
@SuppressWarnings("unused")
private MavenSession createSession(PlexusContainer container) {
MavenExecutionRequest request = new DefaultMavenExecutionRequest();
MavenExecutionResult result = new DefaultMavenExecutionResult();
MavenSession session = new MavenSession(container, MavenRepositorySystemUtils.newSession(), request, result);
return session;
}
@Provides
@Singleton
@SuppressWarnings("unused")
private MojoExecution createMojoExecution() {
return new MojoExecution(null);
}
/**
* @param dir a dir or a file
* @return true if a file/dir exists, false otherwise
*/
private boolean checkExists(String dir) {
return new File(new File(dir).getAbsolutePath()).exists();
}
/**
* @param dir a directory
* @return true if a dir is empty, false otherwise
*/
private boolean checkEmpty(String dir) {
File[] files = new File(dir).listFiles();
return files == null || files.length == 0;
}
@FunctionalInterface
interface LinkCreator {
void createLink(Path link, Path target) throws Exception;
}
}