diff --git a/.idea/misc.xml b/.idea/misc.xml index fde27ea..c696fb6 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -3,6 +3,7 @@ + diff --git a/examples/lib/bld/bld-wrapper.properties b/examples/lib/bld/bld-wrapper.properties index a440f1e..ad61b54 100644 --- a/examples/lib/bld/bld-wrapper.properties +++ b/examples/lib/bld/bld-wrapper.properties @@ -1,7 +1,7 @@ bld.downloadExtensionJavadoc=false bld.downloadExtensionSources=true bld.extensions=com.uwyn.rife2:bld-testng:0.9.0-SNAPSHOT -bld.repositories=MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES,MAVEN_LOCAL +bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.downloadLocation= bld.sourceDirectories= bld.version=1.7.0 diff --git a/src/main/java/rife/bld/extension/TestNgOperation.java b/src/main/java/rife/bld/extension/TestNgOperation.java index 062e9fe..af57ffa 100644 --- a/src/main/java/rife/bld/extension/TestNgOperation.java +++ b/src/main/java/rife/bld/extension/TestNgOperation.java @@ -40,12 +40,29 @@ import java.util.stream.Collectors; public class TestNgOperation extends AbstractProcessOperation { public static final String TEST_CLASS_ARG = "-testclass"; private static final Logger LOGGER = Logger.getLogger(TestNgOperation.class.getName()); + /** + * The run options. + */ + protected final Map options = new ConcurrentHashMap<>(); + /** + * The suite packages to run. + */ + protected final List packages = new ArrayList<>(); + /** + * THe suites to run. + */ + protected final List suites = new ArrayList<>(); private final List args = new ArrayList<>(); - private final Map options = new ConcurrentHashMap<>(); - private final List packages = new ArrayList<>(); - private final List suites = new ArrayList<>(); private BaseProject project; + /** + * Should MethodInvocation Listeners be run even for skipped methods. Default is {@code true}. + */ + public TestNgOperation alwaysRunListeners(Boolean isAlwaysRunListeners) { + options.put("-alwaysrunlisteners", String.valueOf(isAlwaysRunListeners)); + return this; + } + /** * This sets the default maximum number of threads to use for data providers when running tests in parallel. * It will only take effect if the parallel mode has been selected (for example, with the parallel option). @@ -56,6 +73,14 @@ public class TestNgOperation extends AbstractProcessOperation { return this; } + /** + * The dependency injector factory implementation that TestNG should use. + */ + public TestNgOperation dependencyInjectorFactory(String injectorFactory) { + options.put("-dependencyinjectorfactory", injectorFactory); + return this; + } + /** * The directory where the reports will be generated (defaults to {@code build/test-output}). */ @@ -73,17 +98,14 @@ public class TestNgOperation extends AbstractProcessOperation { } /** - * Part of the {@link #execute} operation, constructs the command list - * to use for building the process. - * - * @since 1.5 + * Part of the {@link #execute} operation, constructs the command list to use for building the process. */ @Override protected List executeConstructProcessCommandList() { if (project == null) { LOGGER.severe("A project must be specified."); } else if (packages.isEmpty() && suites.isEmpty()) { - LOGGER.severe("At least one suite or package is required."); + LOGGER.severe("At least one package or XML suite is required."); } if (!options.containsKey("-d")) { @@ -102,7 +124,9 @@ public class TestNgOperation extends AbstractProcessOperation { args.add(v); }); - if (!options.containsKey(TEST_CLASS_ARG)) { + if (!suites.isEmpty()) { + args.addAll(suites); + } else if (!options.containsKey(TEST_CLASS_ARG)) { try { var temp = tempFile(); try (var bufWriter = Files.newBufferedWriter(Paths.get(temp.getPath()))) { @@ -137,13 +161,22 @@ public class TestNgOperation extends AbstractProcessOperation { } /** - * Whether TestNG should continue to execute the remaining tests in the suite or skip them if an @Before* method + * Whether TestNG should continue to execute the remaining tests in the suite or skip them if in a {@code @Before*} + * method. */ public TestNgOperation failurePolicy(FailurePolicy policy) { options.put("-configfailurepolicy", policy.name().toLowerCase(Locale.getDefault())); return this; } + /** + * Should TestNG consider failures in Data Providers as test failures. Default is {@code false}. + */ + public TestNgOperation generateResultsPerSuite(Boolean resultsPerSuite) { + options.put("-generateResultsPerSuite", String.valueOf(resultsPerSuite)); + return this; + } + /** * The list of groups you want to run (e.g. "{@code "windows", "linux", "regression}"). */ @@ -152,6 +185,41 @@ public class TestNgOperation extends AbstractProcessOperation { return this; } + /** + * Ignore missed test names given by '-testnames' and continue to run existing tests, if any. + * Default is {@code false}. + */ + public TestNgOperation ignoreMissedTestName(Boolean isIgnoreMissedTestNames) { + options.put("-ignoreMissedTestNames", String.valueOf(isIgnoreMissedTestNames)); + return this; + } + + /** + * Should TestNG report all iterations of a data driven test as individual skips, in-case of upstream failures. + * Default is {@code false}. + */ + public TestNgOperation includeAllDataDrivenTestsWhenSkipping(Boolean isIncludeDrivenTestsWhenSkipping) { + options.put("-includeAllDataDrivenTestsWhenSkipping", String.valueOf(isIncludeDrivenTestsWhenSkipping)); + return this; + } + + /** + * Specified the JUnit mode. Default is {@code false}. + */ + public TestNgOperation jUnit(Boolean isJunit) { + options.put("-junit", String.valueOf(isJunit)); + return this; + } + + /** + * The list of {@code .class} files or list of class names implementing {@code ITestListener} or + * {@code ISuiteListener} + */ + public TestNgOperation listener(String... listener) { + options.put("-listener", String.join(",", listener)); + return this; + } + /** * Lets you specify method selectors on the command line. * For example: {@code "com.example.Selector1:3", "com.example.Selector2:2"} @@ -170,8 +238,30 @@ public class TestNgOperation extends AbstractProcessOperation { return this; } - protected Map options() { - return options; + /** + * Mixed mode autodetects the type of current test and run it with appropriate runner. + * Default is {@code false} + */ + public TestNgOperation mixed(Boolean isMixed) { + options.put("-mixed", String.valueOf(isMixed)); + return this; + } + + /** + * The list of {@code .class} files or list of class names implementing {@code ITestRunnerFactory}. + */ + public TestNgOperation objectFactory(String... factory) { + options.put("-objectfactory", String.join(",", factory)); + return this; + } + + /** + * The list of fully qualified class names of listeners that should be skipped from being wired in via + * Service Loaders. + */ + public TestNgOperation overrideIncludedMethods(String... method) { + options.put("-overrideincludedmethods", String.join(",", method)); + return this; } /** @@ -194,6 +284,30 @@ public class TestNgOperation extends AbstractProcessOperation { return this; } + /** + * Specifies the port number. + */ + public TestNgOperation port(int port) { + options.put("-port", String.valueOf(port)); + return this; + } + + /** + * Should TestNG consider failures in Data Providers as test failures. Default is {@code false}. + */ + public TestNgOperation propagateDataProviderFailureAsTestFailure(Boolean isPropagateDataProviderFailure) { + options.put("-propagateDataProviderFailureAsTestFailure", String.valueOf(isPropagateDataProviderFailure)); + return this; + } + + /** + * Specifies the extended configuration for custom report listener. + */ + public TestNgOperation reporter(String reporter) { + options.put("-reporter", reporter); + return this; + } + /** * The directories where your javadoc annotated test sources are. This option is only necessary * if you are using javadoc type annotations. (e.g. {@code "src/test"} or @@ -204,6 +318,14 @@ public class TestNgOperation extends AbstractProcessOperation { return this; } + /** + * List fully qualified class names of listeners that should be skipped from being wired in via Service Loaders. + */ + public TestNgOperation spiListenersToSkip(String... listenerToSkip) { + options.put("-spilistenerstoskip", String.join(",", listenerToSkip)); + return this; + } + /** * This specifies the suite name for a test suite defined on the command line. This option is ignored if the * suite.xml file or the source code specifies a different suite name. @@ -213,11 +335,20 @@ public class TestNgOperation extends AbstractProcessOperation { return this; } + /** + * Specifies the size of the thread pool to use to run suites. + */ + public TestNgOperation suiteThreadPoolSize(int poolSize) { + options.put("-suitethreadpoolsize", String.valueOf(poolSize)); + return this; + } + /** * Specifies the suites to run. For example: {@code "testng.xml", "testng2.xml"} */ - public void suites(String... suite) { + public TestNgOperation suites(String... suite) { suites.addAll(Arrays.stream(suite).toList()); + return this; } private File tempFile() throws IOException { @@ -260,6 +391,14 @@ public class TestNgOperation extends AbstractProcessOperation { return this; } + /** + * Specifies the factory used to create tests. + */ + public TestNgOperation testRunFactory(String factory) { + options.put("-testrunfactory", factory); + return this; + } + /** * This sets the default maximum number of threads to use for running tests in parallel. It will only take effect * if the parallel mode has been selected (for example, with the -parallel option). This can be overridden in the @@ -270,9 +409,25 @@ public class TestNgOperation extends AbstractProcessOperation { return this; } + /** + * Specifies the thread pool executor factory implementation that TestNG should use. + */ + public TestNgOperation threadPoolFactoryClass(String factoryClass) { + options.put("-threadpoolfactoryclass", factoryClass); + return this; + } + + /** + * Whether to use the default listeners. Default is {@code true}. + */ + public TestNgOperation useDefaultListeners(Boolean isDefaultListener) { + options.put("-usedefaultlisteners", String.valueOf(isDefaultListener)); + return this; + } + /** * This attribute should contain the path to a valid XML file inside the test jar - * (e.g. {@code "resources/testng.xml"|). The default is {@code testng.xml}, which means a file called + * (e.g. {@code "resources/testng.xml"}). The default is {@code testng.xml}, which means a file called * {@code testng.xml} at the root of the jar file. This option will be ignored unless a test jar is specified. */ public TestNgOperation xmlPathInJar(String path) { diff --git a/src/test/java/rife/bld/extension/TestNgOperationTest.java b/src/test/java/rife/bld/extension/TestNgOperationTest.java index 399c971..1099633 100644 --- a/src/test/java/rife/bld/extension/TestNgOperationTest.java +++ b/src/test/java/rife/bld/extension/TestNgOperationTest.java @@ -20,8 +20,7 @@ import org.junit.jupiter.api.Test; import rife.bld.Project; import rife.bld.operations.exceptions.ExitStatusException; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.Assertions.*; /** * Implements the TestNgOperationTest class. @@ -34,116 +33,267 @@ class TestNgOperationTest { private static final String BAR = "bar"; private static final String FOO = "foo"; + @Test + void testAlwaysRunListeners() { + var op = new TestNgOperation().alwaysRunListeners(false); + assertThat(op.options.get("-alwaysrunlisteners")).isEqualTo("false"); + + op = new TestNgOperation().alwaysRunListeners(true); + assertThat(op.options.get("-alwaysrunlisteners")).isEqualTo("true"); + } + @Test void testClass() { var op = new TestNgOperation().testClass(FOO, BAR); - assertThat(op.options().get(TestNgOperation.TEST_CLASS_ARG)).isEqualTo(String.format("%s,%s", FOO, BAR)); + assertThat(op.options.get(TestNgOperation.TEST_CLASS_ARG)).isEqualTo(String.format("%s,%s", FOO, BAR)); } @Test void testDataProviderThreadCount() { var op = new TestNgOperation().dataProviderThreadCount(1); - assertThat(op.options().get("-dataproviderthreadcount")).isEqualTo("1"); + assertThat(op.options.get("-dataproviderthreadcount")).isEqualTo("1"); + } + + @Test + void testDependencyInjectorFactory() { + var op = new TestNgOperation().dependencyInjectorFactory(FOO); + assertThat(op.options.get("-dependencyinjectorfactory")).isEqualTo(FOO); } @Test void testDirectory() { var op = new TestNgOperation().directory(FOO); - assertThat(op.options().get("-d")).isEqualTo(FOO); + assertThat(op.options.get("-d")).isEqualTo(FOO); } @Test void testExcludeGroups() { var op = new TestNgOperation().excludeGroups(FOO, BAR); - assertThat(op.options().get("-excludegroups")).isEqualTo(String.format("%s,%s", FOO, BAR)); + assertThat(op.options.get("-excludegroups")).isEqualTo(String.format("%s,%s", FOO, BAR)); } @Test void testExecute() { assertThatThrownBy(() -> - new TestNgOperation().fromProject(new Project()).packages("rife.bld.extension") + new TestNgOperation().fromProject(new Project()) .testClass("rife.bld.extension.TestNGSimpleTest") .execute()).isInstanceOf(ExitStatusException.class); + + assertThatCode(() -> + new TestNgOperation().fromProject(new Project()) + .testClass("rife.bld.extension.TestNGSimpleTest") + .methods("rife.bld.extension.TestNGSimpleTest.verifyHello") + .execute()) + .doesNotThrowAnyException(); } @Test void testFailurePolicy() { var op = new TestNgOperation().failurePolicy(TestNgOperation.FailurePolicy.CONTINUE); - assertThat(op.options().get("-configfailurepolicy")).isEqualTo("continue"); + assertThat(op.options.get("-configfailurepolicy")).isEqualTo("continue"); op = new TestNgOperation().failurePolicy(TestNgOperation.FailurePolicy.SKIP); - assertThat(op.options().get("-configfailurepolicy")).isEqualTo("skip"); + assertThat(op.options.get("-configfailurepolicy")).isEqualTo("skip"); + } + + @Test + void testGenerateResultsPerSuite() { + var op = new TestNgOperation().generateResultsPerSuite(false); + assertThat(op.options.get("-generateResultsPerSuite")).isEqualTo("false"); + + op = new TestNgOperation().generateResultsPerSuite(true); + assertThat(op.options.get("-generateResultsPerSuite")).isEqualTo("true"); } @Test void testGroups() { var op = new TestNgOperation().groups(FOO, BAR); - assertThat(op.options().get("-groups")).isEqualTo(String.format("%s,%s", FOO, BAR)); + assertThat(op.options.get("-groups")).isEqualTo(String.format("%s,%s", FOO, BAR)); + } + + @Test + void testIgnoreMissedTestName() { + var op = new TestNgOperation().ignoreMissedTestName(false); + assertThat(op.options.get("-ignoreMissedTestNames")).isEqualTo("false"); + + op = new TestNgOperation().ignoreMissedTestName(true); + assertThat(op.options.get("-ignoreMissedTestNames")).isEqualTo("true"); + } + + @Test + void testIncludeAllDataDrivenTestsWhenSkipping() { + var op = new TestNgOperation().includeAllDataDrivenTestsWhenSkipping(false); + assertThat(op.options.get("-includeAllDataDrivenTestsWhenSkipping")).isEqualTo("false"); + + op = new TestNgOperation().includeAllDataDrivenTestsWhenSkipping(true); + assertThat(op.options.get("-includeAllDataDrivenTestsWhenSkipping")).isEqualTo("true"); } @Test void testJar() { var op = new TestNgOperation().testJar(FOO); - assertThat(op.options().get("-testjar")).isEqualTo(FOO); + assertThat(op.options.get("-testjar")).isEqualTo(FOO); + } + + @Test + void testJunit() { + var op = new TestNgOperation().jUnit(false); + assertThat(op.options.get("-junit")).isEqualTo("false"); + + op = new TestNgOperation().jUnit(true); + assertThat(op.options.get("-junit")).isEqualTo("true"); + } + + @Test + void testListener() { + var ops = new TestNgOperation().listener(FOO, BAR); + assertThat(ops.options.get("-listener")).isEqualTo(String.format("%s,%s", FOO, BAR)); } @Test void testMethodDetectors() { var op = new TestNgOperation().methodSelectors(FOO, BAR); - assertThat(op.options().get("-methodselectors")).isEqualTo(String.format("%s,%s", FOO, BAR)); + assertThat(op.options.get("-methodselectors")).isEqualTo(String.format("%s,%s", FOO, BAR)); } @Test void testMethods() { var op = new TestNgOperation().methods(FOO, BAR); - assertThat(op.options().get("-methods")).isEqualTo(String.format("%s,%s", FOO, BAR)); + assertThat(op.options.get("-methods")).isEqualTo(String.format("%s,%s", FOO, BAR)); + } + + @Test + void testMixed() { + var op = new TestNgOperation().mixed(false); + assertThat(op.options.get("-mixed")).isEqualTo("false"); + + op = new TestNgOperation().mixed(true); + assertThat(op.options.get("-mixed")).isEqualTo("true"); } @Test void testName() { var op = new TestNgOperation().testName(FOO); - assertThat(op.options().get("-testname")).isEqualTo("\"" + FOO + '\"'); + assertThat(op.options.get("-testname")).isEqualTo("\"" + FOO + '\"'); } @Test void testNames() { var ops = new TestNgOperation().testNames(FOO, BAR); - assertThat(ops.options().get("-testnames")).isEqualTo(String.format("\"%s\",\"%s\"", FOO, BAR)); + assertThat(ops.options.get("-testnames")).isEqualTo(String.format("\"%s\",\"%s\"", FOO, BAR)); + } + + @Test + void testObjectFactory() { + var ops = new TestNgOperation().objectFactory(FOO, BAR); + assertThat(ops.options.get("-objectfactory")).isEqualTo(String.format("%s,%s", FOO, BAR)); + } + + @Test + void testOverrideIncludedMethods() { + var ops = new TestNgOperation().overrideIncludedMethods(FOO, BAR); + assertThat(ops.options.get("-overrideincludedmethods")).isEqualTo(String.format("%s,%s", FOO, BAR)); + } + + @Test + void testPackages() { + var op = new TestNgOperation().packages(FOO, BAR); + assertThat(op.packages).contains(FOO).contains(BAR); } @Test void testParallel() { var op = new TestNgOperation().parallel(TestNgOperation.Parallel.TESTS); - assertThat(op.options().get("-parallel")).isEqualTo("tests"); + assertThat(op.options.get("-parallel")).isEqualTo("tests"); op = new TestNgOperation().parallel(TestNgOperation.Parallel.METHODS); - assertThat(op.options().get("-parallel")).isEqualTo("methods"); + assertThat(op.options.get("-parallel")).isEqualTo("methods"); op = new TestNgOperation().parallel(TestNgOperation.Parallel.CLASSES); - assertThat(op.options().get("-parallel")).isEqualTo("classes"); + assertThat(op.options.get("-parallel")).isEqualTo("classes"); + } + + @Test + void testPort() { + var op = new TestNgOperation().port(1); + assertThat(op.options.get("-port")).isEqualTo("1"); + } + + @Test + void testPropagateDataProviderFailureAsTestFailure() { + var op = new TestNgOperation().propagateDataProviderFailureAsTestFailure(false); + assertThat(op.options.get("-propagateDataProviderFailureAsTestFailure")).isEqualTo("false"); + + op = new TestNgOperation().propagateDataProviderFailureAsTestFailure(true); + assertThat(op.options.get("-propagateDataProviderFailureAsTestFailure")).isEqualTo("true"); + } + + @Test + void testReported() { + var op = new TestNgOperation().reporter(FOO); + assertThat(op.options.get("-reporter")).isEqualTo(FOO); + } + + @Test + void testRunFactory() { + var op = new TestNgOperation().testRunFactory(FOO); + assertThat(op.options.get("-testrunfactory")).isEqualTo(FOO); } @Test void testSourceDir() { var op = new TestNgOperation().sourceDir(FOO, BAR); - assertThat(op.options().get("-sourcedir")).isEqualTo(String.format("%s;%s", FOO, BAR)); + assertThat(op.options.get("-sourcedir")).isEqualTo(String.format("%s;%s", FOO, BAR)); + } + + @Test + void testSpiListenersToSkip() { + var ops = new TestNgOperation().spiListenersToSkip(FOO, BAR); + assertThat(ops.options.get("-spilistenerstoskip")).isEqualTo(String.format("%s,%s", FOO, BAR)); } @Test void testSuiteName() { var op = new TestNgOperation().suiteName(FOO); - assertThat(op.options().get("-suitename")).isEqualTo("\"" + FOO + '\"'); + assertThat(op.options.get("-suitename")).isEqualTo("\"" + FOO + '\"'); + } + + @Test + void testSuiteThreadPoolSize() { + var op = new TestNgOperation().suiteThreadPoolSize(1); + assertThat(op.options.get("-suitethreadpoolsize")).isEqualTo("1"); + } + + @Test + void testSuites() { + var op = new TestNgOperation().suites(FOO, BAR); + assertThat(op.suites).contains(FOO).contains(BAR); } @Test void testThreadCount() { var op = new TestNgOperation().threadCount(1); - assertThat(op.options().get("-threadcount")).isEqualTo("1"); + assertThat(op.options.get("-threadcount")).isEqualTo("1"); + } + + @Test + void testThreadPoolFactoryClass() { + var op = new TestNgOperation().threadPoolFactoryClass(FOO); + assertThat(op.options.get("-threadpoolfactoryclass")).isEqualTo(FOO); + } + + @Test + void testUseDefaultListeners() { + var op = new TestNgOperation().useDefaultListeners(false); + assertThat(op.options.get("-usedefaultlisteners")).isEqualTo("false"); + + op = new TestNgOperation().useDefaultListeners(true); + assertThat(op.options.get("-usedefaultlisteners")).isEqualTo("true"); } @Test void testXmlPathInJar() { var op = new TestNgOperation().xmlPathInJar(FOO); - assertThat(op.options().get("-xmlpathinjar")).isEqualTo(FOO); + assertThat(op.options.get("-xmlpathinjar")).isEqualTo(FOO); } }