[core] Internalize CPDCommandLineInterface

Fixes #3835
This commit is contained in:
Andreas Dangel
2022-05-26 20:13:22 +02:00
parent 51fa5e400f
commit e530bff3f9
7 changed files with 153 additions and 88 deletions

View File

@ -39,6 +39,7 @@ the CPD GUI. See [#3974](https://github.com/pmd/pmd/pull/3974) for details.
* cli * cli
* [#1445](https://github.com/pmd/pmd/issues/1445): \[core] Allow CLI to take globs as parameters * [#1445](https://github.com/pmd/pmd/issues/1445): \[core] Allow CLI to take globs as parameters
* core * core
* [#3835](https://github.com/pmd/pmd/issues/3835): \[core] Deprecate system properties of CPDCommandLineInterface
* [#3942](https://github.com/pmd/pmd/issues/3942): \[core] common-io path traversal vulnerability (CVE-2021-29425) * [#3942](https://github.com/pmd/pmd/issues/3942): \[core] common-io path traversal vulnerability (CVE-2021-29425)
* cs (c#) * cs (c#)
* [#3974](https://github.com/pmd/pmd/pull/3974): \[cs] Add option to ignore C# attributes (annotations) * [#3974](https://github.com/pmd/pmd/pull/3974): \[cs] Add option to ignore C# attributes (annotations)
@ -69,6 +70,15 @@ the CPD GUI. See [#3974](https://github.com/pmd/pmd/pull/3974) for details.
{% jdoc core::PMDConfiguration#setInputPaths(java.lang.String) %} are now deprecated. {% jdoc core::PMDConfiguration#setInputPaths(java.lang.String) %} are now deprecated.
A new set of methods have been added, which use lists and do not rely on comma splitting. A new set of methods have been added, which use lists and do not rely on comma splitting.
#### Internal API
Those APIs are not intended to be used by clients, and will be hidden or removed with PMD 7.0.0.
You can identify them with the `@InternalApi` annotation. You'll also get a deprecation warning.
- {% jdoc core::cpd.CPDCommandLineInterface %} has been internalized. In order to execute CPD either
{% jdoc !!core::cpd.CPD#run(String...) %} or {% jdoc !!core::cpd.CPD#main(String[]) %} should be used.
- Several members of {% jdoc test::cli.BaseCPDCLITest %} have been deprecated with replacements.
### External Contributions ### External Contributions
* [#3961](https://github.com/pmd/pmd/pull/3961): \[java] Fix #3954 - NPE in UseCollectionIsEmptyRule with record - [@flyhard](https://github.com/flyhard) * [#3961](https://github.com/pmd/pmd/pull/3961): \[java] Fix #3954 - NPE in UseCollectionIsEmptyRule with record - [@flyhard](https://github.com/flyhard)

View File

@ -4,9 +4,11 @@
package net.sourceforge.pmd.cpd; package net.sourceforge.pmd.cpd;
import java.io.BufferedWriter;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
@ -18,6 +20,7 @@ import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import net.sourceforge.pmd.annotation.Experimental; import net.sourceforge.pmd.annotation.Experimental;
import net.sourceforge.pmd.cli.internal.CliMessages;
import net.sourceforge.pmd.lang.ast.TokenMgrError; import net.sourceforge.pmd.lang.ast.TokenMgrError;
import net.sourceforge.pmd.util.FileFinder; import net.sourceforge.pmd.util.FileFinder;
import net.sourceforge.pmd.util.IOUtil; import net.sourceforge.pmd.util.IOUtil;
@ -172,7 +175,75 @@ public class CPD {
return new ArrayList<>(source.values()); return new ArrayList<>(source.values());
} }
/**
* Entry to invoke CPD as command line tool. Note that this will
* invoke {@link System#exit(int)}.
*
* @param args command line arguments
*/
public static void main(String[] args) { public static void main(String[] args) {
CPDCommandLineInterface.main(args); StatusCode statusCode = runCpd(args);
CPDCommandLineInterface.setStatusCodeOrExit(statusCode.toInt());
}
/**
* Parses the command line and executes CPD. Returns the status code
* without exiting the VM.
*
* @param args command line arguments
*
* @return the status code
*/
public static StatusCode runCpd(String... args) {
CPDConfiguration arguments = new CPDConfiguration();
CPD.StatusCode statusCode = CPDCommandLineInterface.parseArgs(arguments, args);
if (statusCode != null) {
return statusCode;
}
CPD cpd = new CPD(arguments);
try {
CPDCommandLineInterface.addSourceFilesToCPD(cpd, arguments);
cpd.go();
if (arguments.getCPDRenderer() == null) {
// legacy writer
System.out.println(arguments.getRenderer().render(cpd.getMatches()));
} else {
arguments.getCPDRenderer().render(cpd.getMatches(), new BufferedWriter(new OutputStreamWriter(System.out)));
}
if (cpd.getMatches().hasNext()) {
if (arguments.isFailOnViolation()) {
statusCode = StatusCode.DUPLICATE_CODE_FOUND;
} else {
statusCode = StatusCode.OK;
}
} else {
statusCode = StatusCode.OK;
}
} catch (IOException | RuntimeException e) {
e.printStackTrace();
LOGGER.severe(CliMessages.errorDetectedMessage(1, CPDCommandLineInterface.PROGRAM_NAME));
statusCode = StatusCode.ERROR;
}
return statusCode;
}
public enum StatusCode {
OK(0),
ERROR(1),
DUPLICATE_CODE_FOUND(4);
private final int code;
StatusCode(int code) {
this.code = code;
}
/** Returns the exit code as used in CLI. */
public int toInt() {
return this.code;
}
} }
} }

View File

@ -4,11 +4,9 @@
package net.sourceforge.pmd.cpd; package net.sourceforge.pmd.cpd;
import java.io.BufferedWriter;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -17,32 +15,49 @@ import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Logger; import java.util.logging.Logger;
import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.PMDVersion; import net.sourceforge.pmd.PMDVersion;
import net.sourceforge.pmd.annotation.InternalApi;
import net.sourceforge.pmd.cli.internal.CliMessages; import net.sourceforge.pmd.cli.internal.CliMessages;
import net.sourceforge.pmd.cpd.CPD.StatusCode;
import net.sourceforge.pmd.util.FileUtil; import net.sourceforge.pmd.util.FileUtil;
import net.sourceforge.pmd.util.database.DBURI; import net.sourceforge.pmd.util.database.DBURI;
import com.beust.jcommander.JCommander; import com.beust.jcommander.JCommander;
import com.beust.jcommander.ParameterException; import com.beust.jcommander.ParameterException;
/**
* @deprecated Internal API. Use {@link CPD#runCpd(String...)} or {@link CPD#main(String[])}
* in order to execute CPD.
*/
@Deprecated
@InternalApi
public final class CPDCommandLineInterface { public final class CPDCommandLineInterface {
private static final Logger LOGGER = Logger.getLogger(CPDCommandLineInterface.class.getName()); private static final Logger LOGGER = Logger.getLogger(CPDCommandLineInterface.class.getName());
private static final int NO_ERRORS_STATUS = 0; /**
private static final int ERROR_STATUS = 1; * @deprecated This is used for testing, but support for it will be removed in PMD 7.
private static final int DUPLICATE_CODE_FOUND = 4; * Use {@link CPD#runCpd(String...)} to avoid exiting the VM. In PMD 7,
* {@link CPD#main(String[])} will call {@link System#exit(int)} always.
*/
@Deprecated
public static final String NO_EXIT_AFTER_RUN = "net.sourceforge.pmd.cli.noExit"; public static final String NO_EXIT_AFTER_RUN = "net.sourceforge.pmd.cli.noExit";
/**
* @deprecated This is used for testing, but support for it will be removed in PMD 7.
* Use {@link CPD#runCpd(String...)} to avoid exiting the VM. In PMD 7,
* {@link CPD#main(String[])} will call {@link System#exit(int)} always.
*/
@Deprecated
public static final String STATUS_CODE_PROPERTY = "net.sourceforge.pmd.cli.status"; public static final String STATUS_CODE_PROPERTY = "net.sourceforge.pmd.cli.status";
private static final String PROGRAM_NAME = "cpd"; static final String PROGRAM_NAME = "cpd";
private CPDCommandLineInterface() { } private CPDCommandLineInterface() { }
@Deprecated
public static void setStatusCodeOrExit(int status) { public static void setStatusCodeOrExit(int status) {
if (isExitAfterRunSet()) { if (isExitAfterRunSet()) {
System.exit(status); System.exit(status);
@ -63,8 +78,7 @@ public final class CPDCommandLineInterface {
System.setProperty(STATUS_CODE_PROPERTY, Integer.toString(statusCode)); System.setProperty(STATUS_CODE_PROPERTY, Integer.toString(statusCode));
} }
public static void main(String[] args) { static StatusCode parseArgs(CPDConfiguration arguments, String... args) {
CPDConfiguration arguments = new CPDConfiguration();
JCommander jcommander = new JCommander(arguments); JCommander jcommander = new JCommander(arguments);
jcommander.setProgramName(PROGRAM_NAME); jcommander.setProgramName(PROGRAM_NAME);
@ -73,19 +87,17 @@ public final class CPDCommandLineInterface {
if (arguments.isHelp()) { if (arguments.isHelp()) {
jcommander.usage(); jcommander.usage();
System.out.println(buildUsageText()); System.out.println(buildUsageText());
setStatusCodeOrExit(NO_ERRORS_STATUS); return StatusCode.OK;
return;
} }
} catch (ParameterException e) { } catch (ParameterException e) {
System.err.println(e.getMessage()); System.err.println(e.getMessage());
System.err.println(CliMessages.runWithHelpFlagMessage()); System.err.println(CliMessages.runWithHelpFlagMessage());
setStatusCodeOrExit(ERROR_STATUS); return StatusCode.ERROR;
return;
} }
Map<String, String> deprecatedOptions = filterDeprecatedOptions(args); Map<String, String> deprecatedOptions = filterDeprecatedOptions(args);
if (!deprecatedOptions.isEmpty()) { if (!deprecatedOptions.isEmpty()) {
Entry<String, String> first = deprecatedOptions.entrySet().iterator().next(); Map.Entry<String, String> first = deprecatedOptions.entrySet().iterator().next();
LOGGER.warning("Some deprecated options were used on the command-line, including " + first.getKey()); LOGGER.warning("Some deprecated options were used on the command-line, including " + first.getKey());
LOGGER.warning("Consider replacing it with " + first.getValue()); LOGGER.warning("Consider replacing it with " + first.getValue());
} }
@ -94,32 +106,16 @@ public final class CPDCommandLineInterface {
// Pass extra parameters as System properties to allow language // Pass extra parameters as System properties to allow language
// implementation to retrieve their associate values... // implementation to retrieve their associate values...
CPDConfiguration.setSystemProperties(arguments); CPDConfiguration.setSystemProperties(arguments);
CPD cpd = new CPD(arguments);
try { return null;
addSourceFilesToCPD(cpd, arguments); }
cpd.go(); /**
if (arguments.getCPDRenderer() == null) { * @deprecated Use {@link CPD#main(String[])}
// legacy writer */
System.out.println(arguments.getRenderer().render(cpd.getMatches())); @Deprecated
} else { public static void main(String[] args) {
arguments.getCPDRenderer().render(cpd.getMatches(), new BufferedWriter(new OutputStreamWriter(System.out))); setStatusCodeOrExit(CPD.runCpd(args).toInt());
}
if (cpd.getMatches().hasNext()) {
if (arguments.isFailOnViolation()) {
setStatusCodeOrExit(DUPLICATE_CODE_FOUND);
} else {
setStatusCodeOrExit(NO_ERRORS_STATUS);
}
} else {
setStatusCodeOrExit(NO_ERRORS_STATUS);
}
} catch (IOException | RuntimeException e) {
e.printStackTrace();
LOGGER.severe(CliMessages.errorDetectedMessage(1, "CPD"));
setStatusCodeOrExit(ERROR_STATUS);
}
} }
private static Map<String, String> filterDeprecatedOptions(String... args) { private static Map<String, String> filterDeprecatedOptions(String... args) {
@ -211,6 +207,8 @@ public final class CPDCommandLineInterface {
} }
} }
@Deprecated
@InternalApi
public static String buildUsageText() { public static String buildUsageText() {
String helpText = " For example on Windows:" + PMD.EOL; String helpText = " For example on Windows:" + PMD.EOL;

View File

@ -14,13 +14,10 @@ import java.nio.file.Files;
import java.util.Arrays; import java.util.Arrays;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.contrib.java.lang.system.RestoreSystemProperties;
import org.junit.contrib.java.lang.system.SystemOutRule; import org.junit.contrib.java.lang.system.SystemOutRule;
import org.junit.rules.TemporaryFolder; import org.junit.rules.TemporaryFolder;
import org.junit.rules.TestRule;
import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.junit.JavaUtilLoggingRule; import net.sourceforge.pmd.junit.JavaUtilLoggingRule;
@ -28,8 +25,6 @@ import net.sourceforge.pmd.junit.JavaUtilLoggingRule;
public class CPDCommandLineInterfaceTest { public class CPDCommandLineInterfaceTest {
private static final String SRC_DIR = "src/test/resources/net/sourceforge/pmd/cpd/files/"; private static final String SRC_DIR = "src/test/resources/net/sourceforge/pmd/cpd/files/";
@Rule
public final TestRule restoreSystemProperties = new RestoreSystemProperties();
@Rule @Rule
public final SystemOutRule log = new SystemOutRule().enableLog().muteForSuccessfulTests(); public final SystemOutRule log = new SystemOutRule().enableLog().muteForSuccessfulTests();
@Rule @Rule
@ -38,15 +33,11 @@ public class CPDCommandLineInterfaceTest {
public TemporaryFolder tempDir = new TemporaryFolder(); public TemporaryFolder tempDir = new TemporaryFolder();
@Before
public void setup() {
System.setProperty(CPDCommandLineInterface.NO_EXIT_AFTER_RUN, "true");
}
@Test @Test
public void testEmptyResultRendering() { public void testEmptyResultRendering() {
CPDCommandLineInterface.main(new String[] { "--minimum-tokens", "340", "--language", "java", "--files", CPD.StatusCode statusCode = CPD.runCpd("--minimum-tokens", "340", "--language", "java", "--files",
SRC_DIR, "--format", "xml", }); SRC_DIR, "--format", "xml");
Assert.assertEquals(CPD.StatusCode.OK, statusCode);
Assert.assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "\n" + "<pmd-cpd/>", log.getLog()); Assert.assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "\n" + "<pmd-cpd/>", log.getLog());
} }
@ -57,14 +48,14 @@ public class CPDCommandLineInterfaceTest {
new File(SRC_DIR, "dup1.java").getAbsolutePath(), new File(SRC_DIR, "dup1.java").getAbsolutePath(),
new File(SRC_DIR, "dup2.java").getAbsolutePath()), StandardCharsets.UTF_8); new File(SRC_DIR, "dup2.java").getAbsolutePath()), StandardCharsets.UTF_8);
CPDCommandLineInterface.main(new String[] { "--minimum-tokens", "340", "--language", "java", "--filelist", CPD.StatusCode statusCode = CPD.runCpd("--minimum-tokens", "340", "--language", "java", "--filelist",
filelist.getAbsolutePath(), "--format", "xml", "-failOnViolation", "true" }); filelist.getAbsolutePath(), "--format", "xml", "-failOnViolation", "true");
Assert.assertEquals(CPD.StatusCode.OK, statusCode);
Assert.assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "\n" + "<pmd-cpd/>", log.getLog()); Assert.assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "\n" + "<pmd-cpd/>", log.getLog());
assertTrue(loggingRule.getLog().contains("Some deprecated options were used on the command-line, including -failOnViolation")); assertTrue(loggingRule.getLog().contains("Some deprecated options were used on the command-line, including -failOnViolation"));
assertTrue(loggingRule.getLog().contains("Consider replacing it with --fail-on-violation")); assertTrue(loggingRule.getLog().contains("Consider replacing it with --fail-on-violation"));
// only one parameter is logged // only one parameter is logged
assertFalse(loggingRule.getLog().contains("Some deprecated options were used on the command-line, including --filelist")); assertFalse(loggingRule.getLog().contains("Some deprecated options were used on the command-line, including --filelist"));
assertFalse(loggingRule.getLog().contains("Consider replacing it with --file-list")); assertFalse(loggingRule.getLog().contains("Consider replacing it with --file-list"));
} }
} }

View File

@ -21,13 +21,10 @@ public class CPDCommandLineInterfaceTest extends BaseCPDCLITest {
* Test ignore identifiers argument. * Test ignore identifiers argument.
*/ */
@Test @Test
public void testIgnoreIdentifiers() throws Exception { public void testIgnoreIdentifiers() {
runCPD("--minimum-tokens", "34", "--language", "java", "--files", String out = runTest(CPD.StatusCode.DUPLICATE_CODE_FOUND, "--minimum-tokens", "34", "--language", "java", "--files",
"src/test/resources/net/sourceforge/pmd/cpd/clitest/", "--ignore-identifiers"); "src/test/resources/net/sourceforge/pmd/cpd/clitest/", "--ignore-identifiers");
String out = getOutput();
Assert.assertTrue(out.contains("Found a 7 line (36 tokens) duplication")); Assert.assertTrue(out.contains("Found a 7 line (36 tokens) duplication"));
Assert.assertEquals(4, Integer.parseInt(System.getProperty(CPDCommandLineInterface.STATUS_CODE_PROPERTY)));
} }
/** /**
@ -35,13 +32,10 @@ public class CPDCommandLineInterfaceTest extends BaseCPDCLITest {
*/ */
@Test @Test
public void testIgnoreIdentifiersFailOnViolationFalse() throws Exception { public void testIgnoreIdentifiersFailOnViolationFalse() throws Exception {
runCPD("--minimum-tokens", "34", "--language", "java", "--files", String out = runTest(CPD.StatusCode.OK, "--minimum-tokens", "34", "--language", "java", "--files",
"src/test/resources/net/sourceforge/pmd/cpd/clitest/", "--ignore-identifiers", "--failOnViolation", "src/test/resources/net/sourceforge/pmd/cpd/clitest/", "--ignore-identifiers", "--failOnViolation",
"false"); "false");
String out = getOutput();
Assert.assertTrue(out.contains("Found a 7 line (36 tokens) duplication")); Assert.assertTrue(out.contains("Found a 7 line (36 tokens) duplication"));
Assert.assertEquals(0, Integer.parseInt(System.getProperty(CPDCommandLineInterface.STATUS_CODE_PROPERTY)));
} }
/** /**
@ -49,13 +43,10 @@ public class CPDCommandLineInterfaceTest extends BaseCPDCLITest {
*/ */
@Test @Test
public void testIgnoreIdentifiersFailOnViolationFalseLongOption() throws Exception { public void testIgnoreIdentifiersFailOnViolationFalseLongOption() throws Exception {
runCPD("--minimum-tokens", "34", "--language", "java", "--files", String out = runTest(CPD.StatusCode.OK, "--minimum-tokens", "34", "--language", "java", "--files",
"src/test/resources/net/sourceforge/pmd/cpd/clitest/", "--ignore-identifiers", "--fail-on-violation", "src/test/resources/net/sourceforge/pmd/cpd/clitest/", "--ignore-identifiers", "--fail-on-violation",
"false"); "false");
String out = getOutput();
Assert.assertTrue(out.contains("Found a 7 line (36 tokens) duplication")); Assert.assertTrue(out.contains("Found a 7 line (36 tokens) duplication"));
Assert.assertEquals(0, Integer.parseInt(System.getProperty(CPDCommandLineInterface.STATUS_CODE_PROPERTY)));
} }
/** /**
@ -63,13 +54,10 @@ public class CPDCommandLineInterfaceTest extends BaseCPDCLITest {
*/ */
@Test @Test
public void testExcludes() throws Exception { public void testExcludes() throws Exception {
runCPD("--minimum-tokens", "34", "--language", "java", "--ignore-identifiers", "--files", String out = runTest(CPD.StatusCode.OK, "--minimum-tokens", "34", "--language", "java", "--ignore-identifiers", "--files",
"src/test/resources/net/sourceforge/pmd/cpd/clitest/", "--exclude", "src/test/resources/net/sourceforge/pmd/cpd/clitest/", "--exclude",
"src/test/resources/net/sourceforge/pmd/cpd/clitest/File2.java"); "src/test/resources/net/sourceforge/pmd/cpd/clitest/File2.java");
String out = getOutput();
Assert.assertFalse(out.contains("Found a 7 line (34 tokens) duplication")); Assert.assertFalse(out.contains("Found a 7 line (34 tokens) duplication"));
Assert.assertEquals(0, Integer.parseInt(System.getProperty(CPDCommandLineInterface.STATUS_CODE_PROPERTY)));
} }
/** /**
@ -82,17 +70,15 @@ public class CPDCommandLineInterfaceTest extends BaseCPDCLITest {
// set the default encoding under Windows // set the default encoding under Windows
System.setProperty("file.encoding", "Cp1252"); System.setProperty("file.encoding", "Cp1252");
runCPD("--minimum-tokens", "34", "--language", "java", "--files", String out = runTest(CPD.StatusCode.DUPLICATE_CODE_FOUND, "--minimum-tokens", "34", "--language", "java", "--files",
"src/test/resources/net/sourceforge/pmd/cpd/clitest/", "--ignore-identifiers", "--format", "xml", "src/test/resources/net/sourceforge/pmd/cpd/clitest/", "--ignore-identifiers", "--format", "xml",
// request UTF-8 for CPD // request UTF-8 for CPD
"--encoding", "UTF-8"); "--encoding", "UTF-8");
// reset default encoding // reset default encoding
System.setProperty("file.encoding", origEncoding); System.setProperty("file.encoding", origEncoding);
String out = getOutput();
Assert.assertTrue(out.startsWith("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")); Assert.assertTrue(out.startsWith("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"));
Assert.assertTrue(Pattern.compile("System\\.out\\.println\\([ij] \\+ \"ä\"\\);").matcher(out).find()); Assert.assertTrue(Pattern.compile("System\\.out\\.println\\([ij] \\+ \"ä\"\\);").matcher(out).find());
Assert.assertEquals(4, Integer.parseInt(System.getProperty(CPDCommandLineInterface.STATUS_CODE_PROPERTY)));
} }
/** /**
@ -103,30 +89,24 @@ public class CPDCommandLineInterfaceTest extends BaseCPDCLITest {
*/ */
@Test @Test
public void testBrokenAndValidFile() throws IOException { public void testBrokenAndValidFile() throws IOException {
runCPD("--minimum-tokens", "10", "--language", "java", "--files", String out = runTest(CPD.StatusCode.DUPLICATE_CODE_FOUND, "--minimum-tokens", "10", "--language", "java", "--files",
"src/test/resources/net/sourceforge/pmd/cpd/badandgood/", "--format", "text", "--skip-lexical-errors"); "src/test/resources/net/sourceforge/pmd/cpd/badandgood/", "--format", "text", "--skip-lexical-errors");
String out = getOutput();
Assert.assertTrue( Assert.assertTrue(
Pattern.compile("Skipping .*?BadFile\\.java\\. Reason: Lexical error in file").matcher(out).find()); Pattern.compile("Skipping .*?BadFile\\.java\\. Reason: Lexical error in file").matcher(out).find());
Assert.assertTrue(out.contains("Found a 5 line (13 tokens) duplication")); Assert.assertTrue(out.contains("Found a 5 line (13 tokens) duplication"));
Assert.assertEquals(4, Integer.parseInt(System.getProperty(CPDCommandLineInterface.STATUS_CODE_PROPERTY)));
} }
@Test @Test
public void testFormatXmlWithoutEncoding() throws Exception { public void testFormatXmlWithoutEncoding() throws Exception {
runCPD("--minimum-tokens", "10", "--language", "java", "--files", String out = runTest(CPD.StatusCode.DUPLICATE_CODE_FOUND, "--minimum-tokens", "10", "--language", "java", "--files",
"src/test/resources/net/sourceforge/pmd/cpd/clitest/", "--format", "xml"); "src/test/resources/net/sourceforge/pmd/cpd/clitest/", "--format", "xml");
String out = getOutput();
Assert.assertTrue(out.contains("<duplication lines=\"3\" tokens=\"10\">")); Assert.assertTrue(out.contains("<duplication lines=\"3\" tokens=\"10\">"));
Assert.assertEquals(4, Integer.parseInt(System.getProperty(CPDCommandLineInterface.STATUS_CODE_PROPERTY)));
} }
@Test @Test
public void testCSVFormat() throws Exception { public void testCSVFormat() throws Exception {
runCPD("--minimum-tokens", "100", "--files", "src/test/resources/net/sourceforge/pmd/cpd/badandgood/", String out = runTest(CPD.StatusCode.OK, "--minimum-tokens", "100", "--files", "src/test/resources/net/sourceforge/pmd/cpd/badandgood/",
"--language", "c", "--format", "csv"); "--language", "c", "--format", "csv");
String out = getOutput();
Assert.assertFalse(out.contains("Couldn't instantiate renderer")); Assert.assertFalse(out.contains("Couldn't instantiate renderer"));
Assert.assertEquals(0, Integer.parseInt(System.getProperty(CPDCommandLineInterface.STATUS_CODE_PROPERTY)));
} }
} }

View File

@ -15,18 +15,18 @@ import net.sourceforge.pmd.cli.BaseCPDCLITest;
public class CPDCommandLineInterfaceTest extends BaseCPDCLITest { public class CPDCommandLineInterfaceTest extends BaseCPDCLITest {
@Test @Test
public void shouldFindDuplicatesWithDifferentFileExtensions() { public void shouldFindDuplicatesWithDifferentFileExtensions() {
runCPD("--minimum-tokens", "5", "--language", "js", "--files", String out = runTest(CPD.StatusCode.DUPLICATE_CODE_FOUND, "--minimum-tokens", "5", "--language", "js", "--files",
"src/test/resources/net/sourceforge/pmd/cpd/ts/File1.ts", "src/test/resources/net/sourceforge/pmd/cpd/ts/File1.ts",
"src/test/resources/net/sourceforge/pmd/cpd/ts/File2.ts"); "src/test/resources/net/sourceforge/pmd/cpd/ts/File2.ts");
assertThat(getOutput(), containsString("Found a 9 line (32 tokens) duplication in the following files")); assertThat(out, containsString("Found a 9 line (32 tokens) duplication in the following files"));
} }
@Test @Test
public void shouldFindNoDuplicatesWithDifferentFileExtensions() { public void shouldFindNoDuplicatesWithDifferentFileExtensions() {
runCPD("--minimum-tokens", "5", "--language", "js", "--files", String out = runTest(CPD.StatusCode.OK, "--minimum-tokens", "5", "--language", "js", "--files",
"src/test/resources/net/sourceforge/pmd/cpd/ts/"); "src/test/resources/net/sourceforge/pmd/cpd/ts/");
assertThat(getOutput().trim(), emptyString()); assertThat(out.trim(), emptyString());
} }
} }

View File

@ -9,6 +9,7 @@ import java.io.PrintStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import org.junit.After; import org.junit.After;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import net.sourceforge.pmd.cpd.CPD; import net.sourceforge.pmd.cpd.CPD;
@ -34,6 +35,10 @@ public abstract class BaseCPDCLITest {
System.setErr(originalStderr); System.setErr(originalStderr);
} }
/**
* @deprecated Use {@link #runTest(CPD.StatusCode, String...)} which returns the output.
*/
@Deprecated
public final String getOutput() { public final String getOutput() {
try { try {
return bufferStdout.toString("UTF-8"); return bufferStdout.toString("UTF-8");
@ -42,8 +47,18 @@ public abstract class BaseCPDCLITest {
} }
} }
/**
* @deprecated Use {@link #runTest(CPD.StatusCode, String...)}
*/
@Deprecated
protected void runCPD(String... args) { protected void runCPD(String... args) {
System.setProperty(CPDCommandLineInterface.NO_EXIT_AFTER_RUN, "true"); System.setProperty(CPDCommandLineInterface.NO_EXIT_AFTER_RUN, "true");
CPD.main(args); CPD.main(args);
} }
protected String runTest(CPD.StatusCode expectedStatusCode, String... args) {
CPD.StatusCode statusCode = CPD.runCpd(args);
Assert.assertEquals("Unexpected status code", expectedStatusCode, statusCode);
return getOutput();
}
} }