From e530bff3f938eb226e1bffa5cd33c549739be5ed Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 26 May 2022 20:13:22 +0200 Subject: [PATCH] [core] Internalize CPDCommandLineInterface Fixes #3835 --- docs/pages/release_notes.md | 10 +++ .../java/net/sourceforge/pmd/cpd/CPD.java | 73 +++++++++++++++++- .../pmd/cpd/CPDCommandLineInterface.java | 76 +++++++++---------- .../pmd/cpd/CPDCommandLineInterfaceTest.java | 21 ++--- .../pmd/cpd/CPDCommandLineInterfaceTest.java | 38 +++------- .../pmd/cpd/CPDCommandLineInterfaceTest.java | 8 +- .../sourceforge/pmd/cli/BaseCPDCLITest.java | 15 ++++ 7 files changed, 153 insertions(+), 88 deletions(-) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 9cf3b8ec60..f0faefbaad 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -39,6 +39,7 @@ the CPD GUI. See [#3974](https://github.com/pmd/pmd/pull/3974) for details. * cli * [#1445](https://github.com/pmd/pmd/issues/1445): \[core] Allow CLI to take globs as parameters * 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) * cs (c#) * [#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. 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 * [#3961](https://github.com/pmd/pmd/pull/3961): \[java] Fix #3954 - NPE in UseCollectionIsEmptyRule with record - [@flyhard](https://github.com/flyhard) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java index 130791c123..47bc1c021c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPD.java @@ -4,9 +4,11 @@ package net.sourceforge.pmd.cpd; +import java.io.BufferedWriter; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.OutputStreamWriter; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; @@ -18,6 +20,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import net.sourceforge.pmd.annotation.Experimental; +import net.sourceforge.pmd.cli.internal.CliMessages; import net.sourceforge.pmd.lang.ast.TokenMgrError; import net.sourceforge.pmd.util.FileFinder; import net.sourceforge.pmd.util.IOUtil; @@ -172,7 +175,75 @@ public class CPD { 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) { - 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; + } } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDCommandLineInterface.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDCommandLineInterface.java index 24eef6eea1..e43e8f8366 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDCommandLineInterface.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDCommandLineInterface.java @@ -4,11 +4,9 @@ package net.sourceforge.pmd.cpd; -import java.io.BufferedWriter; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; -import java.io.OutputStreamWriter; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; @@ -17,32 +15,49 @@ import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.logging.Logger; import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.PMDVersion; +import net.sourceforge.pmd.annotation.InternalApi; 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.database.DBURI; import com.beust.jcommander.JCommander; 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 { 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; - private static final int DUPLICATE_CODE_FOUND = 4; - + /** + * @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 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"; - private static final String PROGRAM_NAME = "cpd"; + static final String PROGRAM_NAME = "cpd"; private CPDCommandLineInterface() { } + @Deprecated public static void setStatusCodeOrExit(int status) { if (isExitAfterRunSet()) { System.exit(status); @@ -63,8 +78,7 @@ public final class CPDCommandLineInterface { System.setProperty(STATUS_CODE_PROPERTY, Integer.toString(statusCode)); } - public static void main(String[] args) { - CPDConfiguration arguments = new CPDConfiguration(); + static StatusCode parseArgs(CPDConfiguration arguments, String... args) { JCommander jcommander = new JCommander(arguments); jcommander.setProgramName(PROGRAM_NAME); @@ -73,19 +87,17 @@ public final class CPDCommandLineInterface { if (arguments.isHelp()) { jcommander.usage(); System.out.println(buildUsageText()); - setStatusCodeOrExit(NO_ERRORS_STATUS); - return; + return StatusCode.OK; } } catch (ParameterException e) { System.err.println(e.getMessage()); System.err.println(CliMessages.runWithHelpFlagMessage()); - setStatusCodeOrExit(ERROR_STATUS); - return; + return StatusCode.ERROR; } Map deprecatedOptions = filterDeprecatedOptions(args); if (!deprecatedOptions.isEmpty()) { - Entry first = deprecatedOptions.entrySet().iterator().next(); + Map.Entry first = deprecatedOptions.entrySet().iterator().next(); LOGGER.warning("Some deprecated options were used on the command-line, including " + first.getKey()); 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 // implementation to retrieve their associate values... CPDConfiguration.setSystemProperties(arguments); - CPD cpd = new CPD(arguments); - try { - addSourceFilesToCPD(cpd, arguments); + return null; + } - 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()) { - 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); - } + /** + * @deprecated Use {@link CPD#main(String[])} + */ + @Deprecated + public static void main(String[] args) { + setStatusCodeOrExit(CPD.runCpd(args).toInt()); } private static Map filterDeprecatedOptions(String... args) { @@ -211,6 +207,8 @@ public final class CPDCommandLineInterface { } } + @Deprecated + @InternalApi public static String buildUsageText() { String helpText = " For example on Windows:" + PMD.EOL; diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java index 4195438252..2a04e0120b 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java @@ -14,13 +14,10 @@ import java.nio.file.Files; import java.util.Arrays; import org.junit.Assert; -import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.junit.contrib.java.lang.system.RestoreSystemProperties; import org.junit.contrib.java.lang.system.SystemOutRule; import org.junit.rules.TemporaryFolder; -import org.junit.rules.TestRule; import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.junit.JavaUtilLoggingRule; @@ -28,8 +25,6 @@ import net.sourceforge.pmd.junit.JavaUtilLoggingRule; public class CPDCommandLineInterfaceTest { private static final String SRC_DIR = "src/test/resources/net/sourceforge/pmd/cpd/files/"; - @Rule - public final TestRule restoreSystemProperties = new RestoreSystemProperties(); @Rule public final SystemOutRule log = new SystemOutRule().enableLog().muteForSuccessfulTests(); @Rule @@ -38,15 +33,11 @@ public class CPDCommandLineInterfaceTest { public TemporaryFolder tempDir = new TemporaryFolder(); - @Before - public void setup() { - System.setProperty(CPDCommandLineInterface.NO_EXIT_AFTER_RUN, "true"); - } - @Test public void testEmptyResultRendering() { - CPDCommandLineInterface.main(new String[] { "--minimum-tokens", "340", "--language", "java", "--files", - SRC_DIR, "--format", "xml", }); + CPD.StatusCode statusCode = CPD.runCpd("--minimum-tokens", "340", "--language", "java", "--files", + SRC_DIR, "--format", "xml"); + Assert.assertEquals(CPD.StatusCode.OK, statusCode); Assert.assertEquals("" + "\n" + "", log.getLog()); } @@ -57,14 +48,14 @@ public class CPDCommandLineInterfaceTest { new File(SRC_DIR, "dup1.java").getAbsolutePath(), new File(SRC_DIR, "dup2.java").getAbsolutePath()), StandardCharsets.UTF_8); - CPDCommandLineInterface.main(new String[] { "--minimum-tokens", "340", "--language", "java", "--filelist", - filelist.getAbsolutePath(), "--format", "xml", "-failOnViolation", "true" }); + CPD.StatusCode statusCode = CPD.runCpd("--minimum-tokens", "340", "--language", "java", "--filelist", + filelist.getAbsolutePath(), "--format", "xml", "-failOnViolation", "true"); + Assert.assertEquals(CPD.StatusCode.OK, statusCode); Assert.assertEquals("" + "\n" + "", log.getLog()); 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")); // only one parameter is logged 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")); - } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java index 3bbf718134..a43be3d6d9 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java @@ -21,13 +21,10 @@ public class CPDCommandLineInterfaceTest extends BaseCPDCLITest { * Test ignore identifiers argument. */ @Test - public void testIgnoreIdentifiers() throws Exception { - runCPD("--minimum-tokens", "34", "--language", "java", "--files", + public void testIgnoreIdentifiers() { + String out = runTest(CPD.StatusCode.DUPLICATE_CODE_FOUND, "--minimum-tokens", "34", "--language", "java", "--files", "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.assertEquals(4, Integer.parseInt(System.getProperty(CPDCommandLineInterface.STATUS_CODE_PROPERTY))); } /** @@ -35,13 +32,10 @@ public class CPDCommandLineInterfaceTest extends BaseCPDCLITest { */ @Test 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", "false"); - - String out = getOutput(); 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 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", "false"); - - String out = getOutput(); 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 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/File2.java"); - - String out = getOutput(); 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 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", // request UTF-8 for CPD "--encoding", "UTF-8"); // reset default encoding System.setProperty("file.encoding", origEncoding); - String out = getOutput(); Assert.assertTrue(out.startsWith("")); 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 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"); - String out = getOutput(); Assert.assertTrue( 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.assertEquals(4, Integer.parseInt(System.getProperty(CPDCommandLineInterface.STATUS_CODE_PROPERTY))); } @Test 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"); - String out = getOutput(); Assert.assertTrue(out.contains("")); - Assert.assertEquals(4, Integer.parseInt(System.getProperty(CPDCommandLineInterface.STATUS_CODE_PROPERTY))); } @Test 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"); - String out = getOutput(); Assert.assertFalse(out.contains("Couldn't instantiate renderer")); - Assert.assertEquals(0, Integer.parseInt(System.getProperty(CPDCommandLineInterface.STATUS_CODE_PROPERTY))); } } diff --git a/pmd-javascript/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java b/pmd-javascript/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java index 4b92f726e5..c6b4df8f27 100644 --- a/pmd-javascript/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java +++ b/pmd-javascript/src/test/java/net/sourceforge/pmd/cpd/CPDCommandLineInterfaceTest.java @@ -15,18 +15,18 @@ import net.sourceforge.pmd.cli.BaseCPDCLITest; public class CPDCommandLineInterfaceTest extends BaseCPDCLITest { @Test 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/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 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/"); - assertThat(getOutput().trim(), emptyString()); + assertThat(out.trim(), emptyString()); } } diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/cli/BaseCPDCLITest.java b/pmd-test/src/main/java/net/sourceforge/pmd/cli/BaseCPDCLITest.java index 5fb8ad8335..3380d7acc4 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/cli/BaseCPDCLITest.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/cli/BaseCPDCLITest.java @@ -9,6 +9,7 @@ import java.io.PrintStream; import java.io.UnsupportedEncodingException; import org.junit.After; +import org.junit.Assert; import org.junit.Before; import net.sourceforge.pmd.cpd.CPD; @@ -34,6 +35,10 @@ public abstract class BaseCPDCLITest { System.setErr(originalStderr); } + /** + * @deprecated Use {@link #runTest(CPD.StatusCode, String...)} which returns the output. + */ + @Deprecated public final String getOutput() { try { 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) { System.setProperty(CPDCommandLineInterface.NO_EXIT_AFTER_RUN, "true"); 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(); + } }