diff --git a/pmd-dist/pom.xml b/pmd-dist/pom.xml index 1681c20758..64c71eff1e 100644 --- a/pmd-dist/pom.xml +++ b/pmd-dist/pom.xml @@ -163,6 +163,12 @@ 4.11 test + + org.apache.commons + commons-compress + 1.13 + test + diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistTest.java b/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistTest.java index 26578c88ec..dab1a5b441 100644 --- a/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistTest.java +++ b/pmd-dist/src/test/java/net/sourceforge/pmd/it/BinaryDistTest.java @@ -8,12 +8,15 @@ import static org.junit.Assert.assertTrue; import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Enumeration; import java.util.HashSet; import java.util.Set; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; +import org.apache.commons.io.FileUtils; import org.junit.Test; import net.sourceforge.pmd.PMD; @@ -24,10 +27,14 @@ public class BinaryDistTest { return new File(".", "target/pmd-bin-" + PMD.VERSION + ".zip"); } + private File getSourceDistribution() { + return new File(".", "target/pmd-src-" + PMD.VERSION + ".zip"); + } + @Test public void testFileExistence() { - File file = getBinaryDistribution(); - assertTrue(file.exists()); + assertTrue(getBinaryDistribution().exists()); + assertTrue(getSourceDistribution().exists()); } private Set getExpectedFileNames() { @@ -58,4 +65,22 @@ public class BinaryDistTest { assertTrue(expectedFileNames.isEmpty()); } + + @Test + public void runPMD() throws Exception { + Path tempDir = Files.createTempDirectory("pmd-it-test-"); + String srcDir = new File(".", "src/test/resources/sample-source/").getAbsolutePath(); + + try { + ZipFileExtractor.extractZipFile(getBinaryDistribution().toPath(), tempDir); + + PMDExecutionResult result = PMDExecutor.runPMD(tempDir, srcDir, "java-basic"); + result.assertPMDExecutionResult(4, "JumbledIncrementer.java:8:"); + + result = PMDExecutor.runPMD(tempDir, srcDir, "java-design"); + result.assertPMDExecutionResult(0, ""); + } finally { + FileUtils.forceDelete(tempDir.toFile()); + } + } } diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/it/PMDExecutionResult.java b/pmd-dist/src/test/java/net/sourceforge/pmd/it/PMDExecutionResult.java new file mode 100644 index 0000000000..6a22e66552 --- /dev/null +++ b/pmd-dist/src/test/java/net/sourceforge/pmd/it/PMDExecutionResult.java @@ -0,0 +1,39 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.it; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +/** + * Collects the result of a PMD execution in order to verify it. + * + * @author Andreas Dangel + */ +public class PMDExecutionResult { + private final int exitCode; + private final String output; + + PMDExecutionResult(int theExitCode, String theOutput) { + this.exitCode = theExitCode; + this.output = theOutput; + } + + /** + * Asserts that PMD exited with the expected exit code and that the given expected + * output is contained in the actual PMD output. + * + * @param expectedExitCode the exit code, e.g. 0 if no rule violations are expected, or 4 if violations are found + * @param expectedOutput the output to search for + */ + public void assertPMDExecutionResult(int expectedExitCode, String expectedOutput) { + assertEquals("PMD exited with wrong code", expectedExitCode, exitCode); + assertNotNull("No output found", output); + if (!output.contains(expectedOutput)) { + fail("Expected output '" + expectedOutput + "' not present.\nComplete output:\n\n" + output); + } + } +} diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/it/PMDExecutor.java b/pmd-dist/src/test/java/net/sourceforge/pmd/it/PMDExecutor.java new file mode 100644 index 0000000000..43d16e0643 --- /dev/null +++ b/pmd-dist/src/test/java/net/sourceforge/pmd/it/PMDExecutor.java @@ -0,0 +1,68 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.it; + +import java.nio.file.Path; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.SystemUtils; + +import net.sourceforge.pmd.PMD; + +/** + * Executes PMD from command line. Deals with the differences, when PMD is run on Windows or on Linux. + * + * @author Andreas Dangel + */ +public class PMDExecutor { + private static final String PMD_BIN_PREFIX = "pmd-bin-"; + private static final String SOURCE_DIRECTORY_FLAG = "-d"; + private static final String RULESET_FLAG = "-R"; + private static final String FORMAT_FLAG = "-f"; + private static final String FORMATTER = "text"; + + private PMDExecutor() { + // this is a helper class only + } + + private static PMDExecutionResult runPMDUnix(Path tempDir, String sourceDirectory, String ruleset) throws Exception { + String cmd = tempDir.resolve(PMD_BIN_PREFIX + PMD.VERSION + "/bin/run.sh").toAbsolutePath().toString(); + ProcessBuilder pb = new ProcessBuilder(cmd, "pmd", SOURCE_DIRECTORY_FLAG, sourceDirectory, RULESET_FLAG, ruleset, FORMAT_FLAG, FORMATTER); + pb.redirectErrorStream(true); + Process process = pb.start(); + String output = IOUtils.toString(process.getInputStream()); + + int result = process.waitFor(); + return new PMDExecutionResult(result, output); + } + + private static PMDExecutionResult runPMDWindows(Path tempDir, String sourceDirectory, String ruleset) throws Exception { + String cmd = tempDir.resolve(PMD_BIN_PREFIX + PMD.VERSION + "/bin/pmd.bat").toAbsolutePath().toString(); + ProcessBuilder pb = new ProcessBuilder(cmd, SOURCE_DIRECTORY_FLAG, sourceDirectory, RULESET_FLAG, ruleset, FORMAT_FLAG, FORMATTER); + pb.redirectErrorStream(true); + Process process = pb.start(); + String output = IOUtils.toString(process.getInputStream()); + + int result = process.waitFor(); + return new PMDExecutionResult(result, output); + } + + /** + * Executes the PMD found in tempDir against the given sourceDirectory path with the given ruleset. + * + * @param tempDir the directory, to which the binary distribution has been extracted + * @param sourceDirectory the source directory, that PMD should analyze + * @param ruleset the ruleset, that PMD should execute + * @return collected result of the execution + * @throws Exception if the execution fails for any reason (executable not found, ...) + */ + public static PMDExecutionResult runPMD(Path tempDir, String sourceDirectory, String ruleset) throws Exception { + if (SystemUtils.IS_OS_WINDOWS) { + return runPMDWindows(tempDir, sourceDirectory, ruleset); + } else { + return runPMDUnix(tempDir, sourceDirectory, ruleset); + } + } +} diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/it/ZipFileExtractor.java b/pmd-dist/src/test/java/net/sourceforge/pmd/it/ZipFileExtractor.java new file mode 100644 index 0000000000..0e024dda14 --- /dev/null +++ b/pmd-dist/src/test/java/net/sourceforge/pmd/it/ZipFileExtractor.java @@ -0,0 +1,62 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.it; + +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Path; +import java.util.Enumeration; + +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipFile; +import org.apache.commons.io.IOUtils; + +/** + * Extracts a zip file with preserving the unix file permissions. + * + * @author Andreas Dangel + */ +public class ZipFileExtractor { + // unix file permission for executable flag by owner + private static final int OWNER_EXECUTABLE = 0x40; + + private ZipFileExtractor() { + // Helper class + } + + /** + * Extracts the given zip file into the tempDir. + * @param zipPath the zip file to extract + * @param tempDir the target directory + * @throws Exception if any error happens during extraction + */ + public static void extractZipFile(Path zipPath, Path tempDir) throws Exception { + ZipFile zip = new ZipFile(zipPath.toFile()); + try { + Enumeration entries = zip.getEntries(); + while (entries.hasMoreElements()) { + ZipArchiveEntry entry = entries.nextElement(); + File file = tempDir.resolve(entry.getName()).toFile(); + if (entry.isDirectory()) { + assertTrue(file.mkdirs()); + } else { + try (InputStream data = zip.getInputStream(entry); + OutputStream fileOut = new FileOutputStream(file);) { + IOUtils.copy(data, fileOut); + } + if ((entry.getUnixMode() & OWNER_EXECUTABLE) == OWNER_EXECUTABLE) { + file.setExecutable(true); + } + } + } + } finally { + zip.close(); + } + } +} diff --git a/pmd-dist/src/test/resources/sample-source/JumbledIncrementer.java b/pmd-dist/src/test/resources/sample-source/JumbledIncrementer.java new file mode 100644 index 0000000000..39cbd303af --- /dev/null +++ b/pmd-dist/src/test/resources/sample-source/JumbledIncrementer.java @@ -0,0 +1,13 @@ +/** + * Sample file which triggers the JumbledIncrementerRule on line 8. + * Used in the integration tests. + */ +public class JumbledIncrementer { + public void foo() { + for (int i = 0; i < 10; i++) { // only references 'i' + for (int k = 0; k < 20; i++) { // references both 'i' and 'k' + System.out.println("Hello"); + } + } + } +}