From 9455947111d9c091e0a9b0741908943d9603be47 Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Wed, 28 Aug 2024 16:12:44 -0700 Subject: [PATCH] Cleaned up API to match bld operations aand options APIs --- .idea/bld.xml | 6 + examples/lib/bld/bld-wrapper.properties | 2 +- .../bld/extension/PitestOperationBuild.java | 2 +- .../rife/bld/extension/PitestOperation.java | 364 +++++++++++++++++- .../bld/extension/PitestOperationTest.java | 171 ++++++++ 5 files changed, 534 insertions(+), 11 deletions(-) create mode 100644 .idea/bld.xml diff --git a/.idea/bld.xml b/.idea/bld.xml new file mode 100644 index 0000000..6600cee --- /dev/null +++ b/.idea/bld.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/examples/lib/bld/bld-wrapper.properties b/examples/lib/bld/bld-wrapper.properties index bfb37e0..65e3751 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.downloadLocation= -bld.extension-pitest=com.uwyn.rife2:bld-pitest:0.9.9 +bld.extension-pitest=com.uwyn.rife2:bld-pitest:1.0.0-SNAPSHOT bld.repositories=MAVEN_CENTRAL,RIFE2_RELEASES,MAVEN_LOCAL,RIFE2_SNAPSHOTS bld.sourceDirectories= bld.version=2.0.1 diff --git a/src/bld/java/rife/bld/extension/PitestOperationBuild.java b/src/bld/java/rife/bld/extension/PitestOperationBuild.java index 4b2987a..6c13a33 100644 --- a/src/bld/java/rife/bld/extension/PitestOperationBuild.java +++ b/src/bld/java/rife/bld/extension/PitestOperationBuild.java @@ -33,7 +33,7 @@ public class PitestOperationBuild extends Project { public PitestOperationBuild() { pkg = "rife.bld.extension"; name = "PitestExtension"; - version = version(0, 9, 9); + version = version(1, 0, 0, "SNAPSHOT"); javaRelease = 17; diff --git a/src/main/java/rife/bld/extension/PitestOperation.java b/src/main/java/rife/bld/extension/PitestOperation.java index 36758ef..3c5b2b3 100644 --- a/src/main/java/rife/bld/extension/PitestOperation.java +++ b/src/main/java/rife/bld/extension/PitestOperation.java @@ -22,6 +22,7 @@ import rife.bld.operations.exceptions.ExitStatusException; import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; @@ -60,6 +61,7 @@ public class PitestOperation extends AbstractProcessOperation { return this; } + /** * List of packages and classes which are to be considered outside the scope of mutation. Any lines of code * containing calls to these classes will not be mutated. @@ -129,8 +131,31 @@ public class PitestOperation extends AbstractProcessOperation { * @see #classPath(Collection) */ public PitestOperation classPath(String... path) { - options_.put("--classPath", String.join(",", Arrays.stream(path).filter(this::isNotBlank).toList())); - return this; + return classPath(List.of(path)); + } + + /** + * List of packages and classes which are to be considered outside the scope of mutation. Any lines of code + * containing calls to these classes will not be mutated. + *

+ * If a list is not explicitly supplied then PIT will default to a list of common logging packages as follows + *

+ *

    + *
  • java.util.logging
  • + *
  • org.apache.log4j
  • + *
  • org.slf4j
  • + *
  • org.apache.commons.logging
  • + *
+ *

+ * If the feature {@code FLOGCALL} is disabled, this parameter is ignored and logging calls are also mutated. + * Additional classpath entries to use when looking for tests and mutable code. + * + * @param path one or more paths + * @return this operation instance + * @see #classPathPaths(Collection) + */ + public PitestOperation classPath(Path... path) { + return classPathPaths(List.of(path)); } /** @@ -145,6 +170,30 @@ public class PitestOperation extends AbstractProcessOperation { return this; } + /** + * List of packages and classes which are to be considered outside the scope of mutation. Any lines of code + * containing calls to these classes will not be mutated. + *

+ * If a list is not explicitly supplied then PIT will default to a list of common logging packages as follows + *

+ *

    + *
  • java.util.logging
  • + *
  • org.apache.log4j
  • + *
  • org.slf4j
  • + *
  • org.apache.commons.logging
  • + *
+ *

+ * If the feature {@code FLOGCALL} is disabled, this parameter is ignored and logging calls are also mutated. + * Additional classpath entries to use when looking for tests and mutable code. + * + * @param path one or more paths + * @return this operation instance + * @see #classPathFiles(Collection) + */ + public PitestOperation classPath(File... path) { + return classPathFiles(List.of(path)); + } + /** * File with a list of additional classpath elements (one per line). * @@ -158,6 +207,28 @@ public class PitestOperation extends AbstractProcessOperation { return this; } + /** + * Additional classpath entries to use when looking for tests and mutable code. + * + * @param path the list of paths + * @return this operation instance + * @see #classPath(File...) + */ + public PitestOperation classPathFiles(Collection path) { + return classPath(path.stream().map(File::getAbsolutePath).toList()); + } + + /** + * Additional classpath entries to use when looking for tests and mutable code. + * + * @param path the list of paths + * @return this operation instance + * @see #classPath(Path...) + */ + public PitestOperation classPathPaths(Collection path) { + return classPath(path.stream().map(Path::toFile).map(File::getAbsolutePath).toList()); + } + /** * Line coverage threshold below which the build will fail. This is an integer percent (0-100) that represents the * fraction of the project covered by the tests. @@ -470,6 +541,26 @@ public class PitestOperation extends AbstractProcessOperation { return this; } + /** + * Path to a file containing history information for incremental analysis. + * + * @param path the path + * @return this operation instance + */ + public PitestOperation historyInputLocation(File path) { + return historyInputLocation(path.getAbsolutePath()); + } + + /** + * Path to a file containing history information for incremental analysis. + * + * @param path the path + * @return this operation instance + */ + public PitestOperation historyInputLocation(Path path) { + return historyInputLocation(path.toFile()); + } + /** * Path to write history information for incremental analysis. May be the same as * {@link #historyInputLocation(String) @@ -484,6 +575,28 @@ public class PitestOperation extends AbstractProcessOperation { return this; } + /** + * Path to write history information for incremental analysis. May be the same as + * {@link #historyInputLocation(String) + * + * @param path the path + * @return this operation instance + */ + public PitestOperation historyOutputLocation(File path) { + return historyOutputLocation(path.getAbsolutePath()); + } + + /** + * Path to write history information for incremental analysis. May be the same as + * {@link #historyInputLocation(String) + * + * @param path the path + * @return this operation instance + */ + public PitestOperation historyOutputLocation(Path path) { + return historyOutputLocation(path.toFile()); + } + /** * Indicates if the PIT should try to mutate classes on the classpath with which it was launched. If not supplied * this flag defaults to {@code true}. If set to {@code false} only classes found on the paths specified by the @@ -603,6 +716,28 @@ public class PitestOperation extends AbstractProcessOperation { return this; } + /** + * The path to the java executable to be used to launch test with. If none is supplied defaults to the one + * pointed to by {@code JAVA_HOME}. + * + * @param path the path + * @return this operation instance + */ + public PitestOperation jvmPath(File path) { + return jvmPath(path.getAbsolutePath()); + } + + /** + * The path to the java executable to be used to launch test with. If none is supplied defaults to the one + * pointed to by {@code JAVA_HOME}. + * + * @param path the path + * @return this operation instance + */ + public PitestOperation jvmPath(Path path) { + return jvmPath(path.toFile()); + } + /** * Maximum number of surviving mutants to allow without throwing an error. * @@ -640,8 +775,43 @@ public class PitestOperation extends AbstractProcessOperation { * @see #mutableCodePaths(Collection) */ public PitestOperation mutableCodePaths(String... path) { - options_.put("--mutableCodePaths", String.join(",", Arrays.stream(path).filter(this::isNotBlank).toList())); - return this; + return mutableCodePaths(List.of(path)); + } + + /** + * List of classpaths which should be considered to contain mutable code. If your build maintains separate output + * directories for tests and production classes this parameter should be set to your code output directory in order + * to avoid mutating test helper classes etc. + *

+ * If no mutableCodePath is supplied PIT will default to considering anything not defined within a jar or zip file + * as being a candidate for mutation. + *

+ * PIT will always attempt not to mutate test classes even if they are defined on a mutable path. + * + * @param path one or one paths + * @return this operation instance + * @see #mutableCodePathsPaths(Collection) + */ + public PitestOperation mutableCodePaths(Path... path) { + return mutableCodePathsPaths(List.of(path)); + } + + /** + * List of classpaths which should be considered to contain mutable code. If your build maintains separate output + * directories for tests and production classes this parameter should be set to your code output directory in order + * to avoid mutating test helper classes etc. + *

+ * If no mutableCodePath is supplied PIT will default to considering anything not defined within a jar or zip file + * as being a candidate for mutation. + *

+ * PIT will always attempt not to mutate test classes even if they are defined on a mutable path. + * + * @param path one or one paths + * @return this operation instance + * @see #mutableCodePathsFiles(Collection) + */ + public PitestOperation mutableCodePaths(File... path) { + return mutableCodePathsFiles(List.of(path)); } /** @@ -663,6 +833,42 @@ public class PitestOperation extends AbstractProcessOperation { return this; } + /** + * List of classpaths which should be considered to contain mutable code. If your build maintains separate output + * directories for tests and production classes this parameter should be set to your code output directory in order + * to avoid mutating test helper classes etc. + *

+ * If no mutableCodePath is supplied PIT will default to considering anything not defined within a jar or zip file + * as being a candidate for mutation. + *

+ * PIT will always attempt not to mutate test classes even if they are defined on a mutable path. + * + * @param paths the list of paths + * @return this operation instance + * @see #mutableCodePaths(File...) + */ + public PitestOperation mutableCodePathsFiles(Collection paths) { + return mutableCodePaths(paths.stream().map(File::getAbsolutePath).toList()); + } + + /** + * List of classpaths which should be considered to contain mutable code. If your build maintains separate output + * directories for tests and production classes this parameter should be set to your code output directory in order + * to avoid mutating test helper classes etc. + *

+ * If no mutableCodePath is supplied PIT will default to considering anything not defined within a jar or zip file + * as being a candidate for mutation. + *

+ * PIT will always attempt not to mutate test classes even if they are defined on a mutable path. + * + * @param paths the list of paths + * @return this operation instance + * @see #mutableCodePaths(Path...) + */ + public PitestOperation mutableCodePathsPaths(Collection paths) { + return mutableCodePaths(paths.stream().map(Path::toFile).map(File::getAbsolutePath).toList()); + } + /** * Mutation engine to use. *

@@ -752,6 +958,34 @@ public class PitestOperation extends AbstractProcessOperation { return this; } + /** + * A list of formats in which to write mutation results as the mutations are analysed. + * Supported formats are {@code HTML}, {@code XML}, {@code CSV}. + *

+ * Defaults to {@code HTML}. + * + * @param outputFormat one or more output formats + * @return this operation instance + * @see #outputFormatsFiles(Collection) + */ + public PitestOperation outputFormats(File... outputFormat) { + return outputFormatsFiles(List.of(outputFormat)); + } + + /** + * A list of formats in which to write mutation results as the mutations are analysed. + * Supported formats are {@code HTML}, {@code XML}, {@code CSV}. + *

+ * Defaults to {@code HTML}. + * + * @param outputFormat one or more output formats + * @return this operation instance + * @see #outputFormatsPaths(Collection) + */ + public PitestOperation outputFormats(Path... outputFormat) { + return outputFormatsPaths(List.of(outputFormat)); + } + /** * A list of formats in which to write mutation results as the mutations are analysed. * Supported formats are {@code HTML}, {@code XML}, {@code CSV}. @@ -763,9 +997,7 @@ public class PitestOperation extends AbstractProcessOperation { * @see #outputFormats(Collection) */ public PitestOperation outputFormats(String... outputFormat) { - options_.put("--outputFormats", - String.join(",", Arrays.stream(outputFormat).filter(this::isNotBlank).toList())); - return this; + return outputFormats(List.of(outputFormat)); } /** @@ -783,6 +1015,35 @@ public class PitestOperation extends AbstractProcessOperation { return this; } + /** + * A list of formats in which to write mutation results as the mutations are analysed. + * Supported formats are {@code HTML}, {@code XML}, {@code CSV}. + *

+ * Defaults to {@code HTML}. + * + * @param outputFormats the list of output formats + * @return this operation instance + * @see #outputFormats(File...) + */ + public PitestOperation outputFormatsFiles(Collection outputFormats) { + return outputFormats(outputFormats.stream().map(File::getAbsolutePath).toList()); + } + + /** + * A list of formats in which to write mutation results as the mutations are analysed. + * Supported formats are {@code HTML}, {@code XML}, {@code CSV}. + *

+ * Defaults to {@code HTML}. + * + * @param outputFormats the list of output formats + * @return this operation instance + * @see #outputFormats(Path...) + */ + public PitestOperation outputFormatsPaths(Collection outputFormats) { + return outputFormats(outputFormats.stream().map(Path::toFile).map(File::getAbsolutePath).toList()); + + } + /** * Custom plugin properties. * @@ -806,6 +1067,27 @@ public class PitestOperation extends AbstractProcessOperation { return this; } + /** + * Project base. + * + * @param file the file + * @return this operations instance + */ + public PitestOperation projectBase(File file) { + return projectBase(file.getAbsolutePath()); + } + + /** + * Project base. + * + * @param file the file + * @return this operations instance + */ + public PitestOperation projectBase(Path file) { + return projectBase(file.toFile()); + } + + /** * Output directory for the reports. * @@ -819,6 +1101,26 @@ public class PitestOperation extends AbstractProcessOperation { return this; } + /** + * Output directory for the reports. + * + * @param dir the directory + * @return this operation instance + */ + public PitestOperation reportDir(File dir) { + return reportDir(dir.getAbsolutePath()); + } + + /** + * Output directory for the reports. + * + * @param dir the directory + * @return this operation instance + */ + public PitestOperation reportDir(Path dir) { + return reportDir(dir.toFile()); + } + /** * whether to ignore failing tests when computing coverage. *

@@ -844,8 +1146,29 @@ public class PitestOperation extends AbstractProcessOperation { * @see #sourceDirs(Collection) */ public PitestOperation sourceDirs(String... dir) { - options_.put(SOURCE_DIRS, String.join(",", Arrays.stream(dir).filter(this::isNotBlank).toList())); - return this; + return sourceDirs(List.of(dir)); + } + + /** + * The folder(s) containing the source code. + * + * @param dir one or more directories + * @return this operation instance + * @see #sourceDirsFiles(Collection) + */ + public PitestOperation sourceDirs(File... dir) { + return sourceDirsFiles(List.of(dir)); + } + + /** + * The folder(s) containing the source code. + * + * @param dir one or more directories + * @return this operation instance + * @see #sourceDirsPaths(Collection) + */ + public PitestOperation sourceDirs(Path... dir) { + return sourceDirsPaths(List.of(dir)); } /** @@ -860,6 +1183,29 @@ public class PitestOperation extends AbstractProcessOperation { return this; } + /** + * The folder(s) containing the source code. + * + * @param dirs the list of directories + * @return this operation instance + * @see #sourceDirs(File...) + */ + public PitestOperation sourceDirsFiles(Collection dirs) { + return sourceDirs(dirs.stream().map(File::getAbsolutePath).toList()); + } + + /** + * The folder(s) containing the source code. + * + * @param dirs the list of directories + * @return this operation instance + * @see #sourceDirs(Path...) + */ + public PitestOperation sourceDirsPaths(Collection dirs) { + return sourceDirs(dirs.stream().map(Path::toFile).map(File::getAbsolutePath).toList()); + + } + /** * The classes to be mutated. This is expressed as a list of globs. *

diff --git a/src/test/java/rife/bld/extension/PitestOperationTest.java b/src/test/java/rife/bld/extension/PitestOperationTest.java index 0fb9902..82f446c 100644 --- a/src/test/java/rife/bld/extension/PitestOperationTest.java +++ b/src/test/java/rife/bld/extension/PitestOperationTest.java @@ -22,6 +22,7 @@ import rife.bld.Project; import rife.bld.WebProject; import rife.bld.operations.exceptions.ExitStatusException; +import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; @@ -33,6 +34,7 @@ import static org.assertj.core.api.Assertions.assertThatCode; import static rife.bld.extension.PitestOperation.FALSE; import static rife.bld.extension.PitestOperation.TRUE; +@SuppressWarnings("PMD.AvoidDuplicateLiterals") class PitestOperationTest { private static final String AS_LIST = "as list"; private final static String BAR = "bar"; @@ -589,6 +591,175 @@ class PitestOperationTest { assertThat(op.options().get("--targetTests")).as(AS_LIST).isEqualTo(FOOBAR); } + @Test + void testClassPath() { + var foo = new File(FOO); + var bar = new File(BAR); + + var foobar = String.format("%s,%s", FOO, BAR); + var op = new PitestOperation().classPath(FOO, BAR); + assertThat(op.options().get("--classPath")).as("String...").isEqualTo(foobar); + + op = new PitestOperation().classPath(List.of(FOO, BAR)); + assertThat(op.options().get("--classPath")).as("List(String...)").isEqualTo(foobar); + + foobar = String.format("%s,%s", foo.getAbsolutePath(), bar.getAbsolutePath()); + op = new PitestOperation().classPath(foo, bar); + assertThat(op.options().get("--classPath")).as("File...").isEqualTo(foobar); + + op = new PitestOperation().classPathFiles(List.of(foo, bar)); + assertThat(op.options().get("--classPath")).as("List(String...)").isEqualTo(foobar); + + op = new PitestOperation().classPath(foo.toPath(), bar.toPath()); + assertThat(op.options().get("--classPath")).as("Path...").isEqualTo(foobar); + + op = new PitestOperation().classPathPaths(List.of(foo.toPath(), bar.toPath())); + assertThat(op.options().get("--classPath")).as("List(Path...)").isEqualTo(foobar); + } + + @Test + void testHistoryInputLocation() { + var foo = new File(FOO); + var op = new PitestOperation().historyInputLocation(FOO); + assertThat(op.options().get("--historyInputLocation")).as("as string").isEqualTo(FOO); + + op = new PitestOperation().historyInputLocation(foo); + assertThat(op.options().get("--historyInputLocation")).as("as file").isEqualTo(foo.getAbsolutePath()); + + op = new PitestOperation().historyInputLocation(foo.toPath()); + assertThat(op.options().get("--historyInputLocation")).as("as path").isEqualTo(foo.getAbsolutePath()); + } + + @Test + void testHistoryOutputLocation() { + var foo = new File(FOO); + var op = new PitestOperation().historyOutputLocation(FOO); + assertThat(op.options().get("--historyOutputLocation")).as("as string").isEqualTo(FOO); + + op = new PitestOperation().historyOutputLocation(foo); + assertThat(op.options().get("--historyOutputLocation")).as("as file").isEqualTo(foo.getAbsolutePath()); + + op = new PitestOperation().historyOutputLocation(foo.toPath()); + assertThat(op.options().get("--historyOutputLocation")).as("as path").isEqualTo(foo.getAbsolutePath()); + } + + @Test + void testJvmPath() { + var foo = new File(FOO); + var op = new PitestOperation().jvmPath(FOO); + assertThat(op.options().get("--jvmPath")).as("as string").isEqualTo(FOO); + + op = new PitestOperation().jvmPath(foo); + assertThat(op.options().get("--jvmPath")).as("as file").isEqualTo(foo.getAbsolutePath()); + + op = new PitestOperation().jvmPath(foo.toPath()); + assertThat(op.options().get("--jvmPath")).as("as path").isEqualTo(foo.getAbsolutePath()); + } + + @Test + void testMutableCodePaths() { + var foo = new File(FOO); + var bar = new File(BAR); + + var foobar = String.format("%s,%s", FOO, BAR); + var op = new PitestOperation().mutableCodePaths(FOO, BAR); + assertThat(op.options().get("--mutableCodePaths")).as("String...").isEqualTo(foobar); + + op = new PitestOperation().mutableCodePaths(List.of(FOO, BAR)); + assertThat(op.options().get("--mutableCodePaths")).as("List(String...)").isEqualTo(foobar); + + foobar = String.format("%s,%s", foo.getAbsolutePath(), bar.getAbsolutePath()); + op = new PitestOperation().mutableCodePaths(foo, bar); + assertThat(op.options().get("--mutableCodePaths")).as("File...").isEqualTo(foobar); + + op = new PitestOperation().mutableCodePathsFiles(List.of(foo, bar)); + assertThat(op.options().get("--mutableCodePaths")).as("List(String...)").isEqualTo(foobar); + + op = new PitestOperation().mutableCodePaths(foo.toPath(), bar.toPath()); + assertThat(op.options().get("--mutableCodePaths")).as("Path...").isEqualTo(foobar); + + op = new PitestOperation().mutableCodePathsPaths(List.of(foo.toPath(), bar.toPath())); + assertThat(op.options().get("--mutableCodePaths")).as("List(Path...)").isEqualTo(foobar); + } + + @Test + void testOutputFormats() { + var foo = new File(FOO); + var bar = new File(BAR); + + var foobar = String.format("%s,%s", FOO, BAR); + var op = new PitestOperation().outputFormats(FOO, BAR); + assertThat(op.options().get("--outputFormats")).as("String...").isEqualTo(foobar); + + op = new PitestOperation().outputFormats(List.of(FOO, BAR)); + assertThat(op.options().get("--outputFormats")).as("List(String...)").isEqualTo(foobar); + + foobar = String.format("%s,%s", foo.getAbsolutePath(), bar.getAbsolutePath()); + op = new PitestOperation().outputFormats(foo, bar); + assertThat(op.options().get("--outputFormats")).as("File...").isEqualTo(foobar); + + op = new PitestOperation().outputFormatsFiles(List.of(foo, bar)); + assertThat(op.options().get("--outputFormats")).as("List(String...)").isEqualTo(foobar); + + op = new PitestOperation().outputFormats(foo.toPath(), bar.toPath()); + assertThat(op.options().get("--outputFormats")).as("Path...").isEqualTo(foobar); + + op = new PitestOperation().outputFormatsPaths(List.of(foo.toPath(), bar.toPath())); + assertThat(op.options().get("--outputFormats")).as("List(Path...)").isEqualTo(foobar); + } + + @Test + void testProjectBase() { + var foo = new File(FOO); + var op = new PitestOperation().projectBase(FOO); + assertThat(op.options().get("--projectBase")).as("as string").isEqualTo(FOO); + + op = new PitestOperation().projectBase(foo); + assertThat(op.options().get("--projectBase")).as("as file").isEqualTo(foo.getAbsolutePath()); + + op = new PitestOperation().projectBase(foo.toPath()); + assertThat(op.options().get("--projectBase")).as("as path").isEqualTo(foo.getAbsolutePath()); + } + + @Test + void testReportDir() { + var foo = new File(FOO); + var op = new PitestOperation().reportDir(FOO); + assertThat(op.options().get("--reportDir")).as("as string").isEqualTo(FOO); + + op = new PitestOperation().reportDir(foo); + assertThat(op.options().get("--reportDir")).as("as file").isEqualTo(foo.getAbsolutePath()); + + op = new PitestOperation().reportDir(foo.toPath()); + assertThat(op.options().get("--reportDir")).as("as path").isEqualTo(foo.getAbsolutePath()); + } + + @Test + void testSourceDirs() { + var foo = new File(FOO); + var bar = new File(BAR); + + var foobar = String.format("%s,%s", FOO, BAR); + var op = new PitestOperation().sourceDirs(FOO, BAR); + assertThat(op.options().get("--sourceDirs")).as("String...").isEqualTo(foobar); + + op = new PitestOperation().sourceDirs(List.of(FOO, BAR)); + assertThat(op.options().get("--sourceDirs")).as("List(String...)").isEqualTo(foobar); + + foobar = String.format("%s,%s", foo.getAbsolutePath(), bar.getAbsolutePath()); + op = new PitestOperation().sourceDirs(foo, bar); + assertThat(op.options().get("--sourceDirs")).as("File...").isEqualTo(foobar); + + op = new PitestOperation().sourceDirsFiles(List.of(foo, bar)); + assertThat(op.options().get("--sourceDirs")).as("List(String...)").isEqualTo(foobar); + + op = new PitestOperation().sourceDirs(foo.toPath(), bar.toPath()); + assertThat(op.options().get("--sourceDirs")).as("Path...").isEqualTo(foobar); + + op = new PitestOperation().sourceDirsPaths(List.of(foo.toPath(), bar.toPath())); + assertThat(op.options().get("--sourceDirs")).as("List(Path...)").isEqualTo(foobar); + } + @Test void testStrengthThreshold() { var op = new PitestOperation()