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");
+ }
+ }
+ }
+}