diff --git a/.github/workflows/bld.yml b/.github/workflows/bld.yml index 2b0ba38..f4aa3a2 100644 --- a/.github/workflows/bld.yml +++ b/.github/workflows/bld.yml @@ -4,29 +4,36 @@ on: [ push, pull_request, workflow_dispatch ] jobs: build-bld-project: - runs-on: ubuntu-latest - strategy: matrix: - java-version: [ 17, 20 ] + java-version: [ 17, 21, 24 ] + kotlin-version: [ 1.9.25, 2.0.21, 2.1.20 ] + os: [ ubuntu-latest, windows-latest, macos-latest ] + + runs-on: ${{ matrix.os }} steps: - name: Checkout source repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up JDK ${{ matrix.java-version }} - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: - distribution: 'zulu' + distribution: "zulu" java-version: ${{ matrix.java-version }} - - name: Grant execute permission for bld - run: chmod +x bld - - - name: Download the dependencies + - name: Download dependencies [examples] + working-directory: examples run: ./bld download - - name: Run tests with bld - run: ./bld compile test + - name: Run PIT tests [examples] + working-directory: examples + run: ./bld compile pit + + - name: Download dependencies + run: ./bld download + + - name: Run tests + run: ./bld compile test \ No newline at end of file diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index f6122cd..508f6a5 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -30,14 +30,14 @@ jobs: steps: - name: Checkout source repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: - distribution: 'zulu' + distribution: "zulu" java-version: 17 - name: Build Javadocs @@ -47,11 +47,11 @@ jobs: uses: actions/configure-pages@v3 - name: Upload artifact - uses: actions/upload-pages-artifact@v1 + uses: actions/upload-pages-artifact@v3 with: # Upload generated Javadocs repository - path: 'build/javadoc/' + path: "build/javadoc/" - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v1 \ No newline at end of file + uses: actions/deploy-pages@v4 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/.idea/copyright/Apache_License.xml b/.idea/copyright/Apache_License.xml index 15687f4..4446c15 100644 --- a/.idea/copyright/Apache_License.xml +++ b/.idea/copyright/Apache_License.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/.idea/icon.svg b/.idea/icon.svg new file mode 100644 index 0000000..81220b4 --- /dev/null +++ b/.idea/icon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/.idea/intellij-javadocs-4.0.1.xml b/.idea/intellij-javadocs-4.0.1.xml new file mode 100644 index 0000000..90d3347 --- /dev/null +++ b/.idea/intellij-javadocs-4.0.1.xml @@ -0,0 +1,204 @@ + + + + + UPDATE + false + true + + FIELD + METHOD + TYPE + + + PUBLIC + PROTECTED + DEFAULT + + + + + + ^.*(public|protected|private)*.+interface\s+\w+.* + /**\n + * The interface ${name}.\n +<#if element.typeParameters?has_content> * \n +</#if> +<#list element.typeParameters as typeParameter> + * @param <${typeParameter.name}> the type parameter\n +</#list> + */ + + + ^.*(public|protected|private)*.+enum\s+\w+.* + /**\n + * The enum ${name}.\n + */ + + + ^.*(public|protected|private)*.+class\s+\w+.* + /**\n + * The type ${name}.\n +<#if element.typeParameters?has_content> * \n +</#if> +<#list element.typeParameters as typeParameter> + * @param <${typeParameter.name}> the type parameter\n +</#list> + */ + + + .+ + /**\n + * The type ${name}.\n + */ + + + + + .+ + /**\n + * Instantiates a new ${name}.\n +<#if element.parameterList.parameters?has_content> + *\n +</#if> +<#list element.parameterList.parameters as parameter> + * @param ${parameter.name} the ${paramNames[parameter.name]}\n +</#list> +<#if element.throwsList.referenceElements?has_content> + *\n +</#if> +<#list element.throwsList.referenceElements as exception> + * @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n +</#list> + */ + + + + + ^.*(public|protected|private)*\s*.*(\w(\s*<.+>)*)+\s+get\w+\s*\(.*\).+ + /**\n + * Gets ${partName}.\n +<#if element.typeParameters?has_content> * \n +</#if> +<#list element.typeParameters as typeParameter> + * @param <${typeParameter.name}> the type parameter\n +</#list> +<#if element.parameterList.parameters?has_content> + *\n +</#if> +<#list element.parameterList.parameters as parameter> + * @param ${parameter.name} the ${paramNames[parameter.name]}\n +</#list> +<#if isNotVoid> + *\n + * @return the ${partName}\n +</#if> +<#if element.throwsList.referenceElements?has_content> + *\n +</#if> +<#list element.throwsList.referenceElements as exception> + * @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n +</#list> + */ + + + ^.*(public|protected|private)*\s*.*(void|\w(\s*<.+>)*)+\s+set\w+\s*\(.*\).+ + /**\n + * Sets ${partName}.\n +<#if element.typeParameters?has_content> * \n +</#if> +<#list element.typeParameters as typeParameter> + * @param <${typeParameter.name}> the type parameter\n +</#list> +<#if element.parameterList.parameters?has_content> + *\n +</#if> +<#list element.parameterList.parameters as parameter> + * @param ${parameter.name} the ${paramNames[parameter.name]}\n +</#list> +<#if isNotVoid> + *\n + * @return the ${partName}\n +</#if> +<#if element.throwsList.referenceElements?has_content> + *\n +</#if> +<#list element.throwsList.referenceElements as exception> + * @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n +</#list> + */ + + + ^.*((public\s+static)|(static\s+public))\s+void\s+main\s*\(\s*String\s*(\[\s*\]|\.\.\.)\s+\w+\s*\).+ + /**\n + * The entry point of application.\n + + <#if element.parameterList.parameters?has_content> + *\n +</#if> + * @param ${element.parameterList.parameters[0].name} the input arguments\n +<#if element.throwsList.referenceElements?has_content> + *\n +</#if> +<#list element.throwsList.referenceElements as exception> + * @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n +</#list> + */ + + + .+ + /**\n + * ${name}<#if isNotVoid> ${return}</#if>.\n +<#if element.typeParameters?has_content> * \n +</#if> +<#list element.typeParameters as typeParameter> + * @param <${typeParameter.name}> the type parameter\n +</#list> +<#if element.parameterList.parameters?has_content> + *\n +</#if> +<#list element.parameterList.parameters as parameter> + * @param ${parameter.name} the ${paramNames[parameter.name]}\n +</#list> +<#if isNotVoid> + *\n + * @return the ${return}\n +</#if> +<#if element.throwsList.referenceElements?has_content> + *\n +</#if> +<#list element.throwsList.referenceElements as exception> + * @throws ${exception.referenceName} the ${exceptionNames[exception.referenceName]}\n +</#list> + */ + + + + + ^.*(public|protected|private)*.+static.*(\w\s\w)+.+ + /**\n + * The constant ${element.getName()}.\n + */ + + + ^.*(public|protected|private)*.*(\w\s\w)+.+ + /**\n + <#if element.parent.isInterface()> + * The constant ${element.getName()}.\n +<#else> + * The ${name}.\n +</#if> */ + + + .+ + /**\n + <#if element.parent.isEnum()> + *${name} ${typeName}.\n +<#else> + * The ${name}.\n +</#if>*/ + + + + + \ No newline at end of file diff --git a/.idea/libraries/bld.xml b/.idea/libraries/bld.xml index cf75013..153a060 100644 --- a/.idea/libraries/bld.xml +++ b/.idea/libraries/bld.xml @@ -2,11 +2,12 @@ - + - + + @@ -14,4 +15,4 @@ - \ No newline at end of file + diff --git a/.idea/libraries/compile.xml b/.idea/libraries/compile.xml index 9bd86aa..99cc0c0 100644 --- a/.idea/libraries/compile.xml +++ b/.idea/libraries/compile.xml @@ -7,7 +7,7 @@ - - + + \ No newline at end of file diff --git a/.idea/libraries/runtime.xml b/.idea/libraries/runtime.xml index 2ae5c4b..d4069f2 100644 --- a/.idea/libraries/runtime.xml +++ b/.idea/libraries/runtime.xml @@ -8,7 +8,7 @@ - - + + \ No newline at end of file diff --git a/.idea/libraries/test.xml b/.idea/libraries/test.xml index b80486a..57ed5ef 100644 --- a/.idea/libraries/test.xml +++ b/.idea/libraries/test.xml @@ -8,7 +8,7 @@ - - + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 8b276da..192c134 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -4,12 +4,18 @@ + \ No newline at end of file + diff --git a/examples/.idea/libraries/compile.xml b/examples/.idea/libraries/compile.xml index 9bd86aa..99cc0c0 100644 --- a/examples/.idea/libraries/compile.xml +++ b/examples/.idea/libraries/compile.xml @@ -7,7 +7,7 @@ - - + + \ No newline at end of file diff --git a/examples/.idea/libraries/runtime.xml b/examples/.idea/libraries/runtime.xml index 2ae5c4b..d4069f2 100644 --- a/examples/.idea/libraries/runtime.xml +++ b/examples/.idea/libraries/runtime.xml @@ -8,7 +8,7 @@ - - + + \ No newline at end of file diff --git a/examples/.idea/libraries/test.xml b/examples/.idea/libraries/test.xml index b80486a..57ed5ef 100644 --- a/examples/.idea/libraries/test.xml +++ b/examples/.idea/libraries/test.xml @@ -8,7 +8,7 @@ - - + + \ No newline at end of file diff --git a/examples/.idea/misc.xml b/examples/.idea/misc.xml index fafd394..f6cca2c 100644 --- a/examples/.idea/misc.xml +++ b/examples/.idea/misc.xml @@ -1,4 +1,3 @@ - diff --git a/examples/.vscode/launch.json b/examples/.vscode/launch.json index 71f6bb3..2d63b46 100644 --- a/examples/.vscode/launch.json +++ b/examples/.vscode/launch.json @@ -5,7 +5,14 @@ "type": "java", "name": "Run Tests", "request": "launch", - "mainClass": "com.example.ExamplesTest" + "mainClass": "org.junit.platform.console.ConsoleLauncher", + "args": [ + "--details=verbose", + "--scan-classpath", + "--disable-banner", + "--disable-ansi-colors", + "--exclude-engine=junit-platform-suite", + "--exclude-engine=junit-vintage"] } ] } diff --git a/examples/.vscode/settings.json b/examples/.vscode/settings.json index 133aa45..ba429d0 100644 --- a/examples/.vscode/settings.json +++ b/examples/.vscode/settings.json @@ -3,13 +3,13 @@ "src/main/java", "src/main/resources", "src/test/java", - "src/bld/java" + "src/test/resources", + "src/bld/java", + "src/bld/resources" ], "java.configuration.updateBuildConfiguration": "automatic", "java.project.referencedLibraries": [ - "${HOME}/.bld/dist/bld-1.7.5.jar", - "lib/compile/*.jar", - "lib/runtime/*.jar", - "lib/test/*.jar" + "${HOME}/.bld/dist/bld-2.2.1.jar", + "lib/**/*.jar" ] } diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..cf98409 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,5 @@ +# Run and Generate Pit Test Coverage Report + +```console +./bld compile pit +``` diff --git a/examples/lib/bld/bld-wrapper.jar b/examples/lib/bld/bld-wrapper.jar index 9b4ee52..7731211 100644 Binary files a/examples/lib/bld/bld-wrapper.jar and b/examples/lib/bld/bld-wrapper.jar differ diff --git a/examples/lib/bld/bld-wrapper.properties b/examples/lib/bld/bld-wrapper.properties index b4d7d23..c84c8d7 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-pitest:0.9.0 -bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.downloadLocation= +bld.extension-pitest=com.uwyn.rife2:bld-pitest:1.0.10 +bld.repositories=MAVEN_CENTRAL,RIFE2_RELEASES,MAVEN_LOCAL,RIFE2_SNAPSHOTS bld.sourceDirectories= -bld.version=1.7.5 +bld.version=2.2.1 diff --git a/examples/reports/mutations/com.example/ExamplesLib.java.html b/examples/reports/mutations/com.example/ExamplesLib.java.html deleted file mode 100644 index b3df60e..0000000 --- a/examples/reports/mutations/com.example/ExamplesLib.java.html +++ /dev/null @@ -1,166 +0,0 @@ - - - - - - - - - -

ExamplesLib.java

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -1 - - - - - - -
package com.example;
- -2 - - - - - - -
- -3 - - - - - - -
public class ExamplesLib {
- -4 - - - - - - -
    public String getMessage() {
- -5 - - -1 - -1. getMessage : replaced return value with "" for com/example/ExamplesLib::getMessage → KILLED
- -
-
-
        return "Hello World!";
- -6 - - - - - - -
    }
- -7 - - - - - - -
}

Mutations

5 - - - -

1.1
Location : getMessage
Killed by : com.example.ExamplesTest.[engine:junit-jupiter]/[class:com.example.ExamplesTest]/[method:verifyHello()]
replaced return value with "" for com/example/ExamplesLib::getMessage → KILLED

-
- - -

Active mutators

-
    -
  • CONDITIONALS_BOUNDARY
  • -
  • EMPTY_RETURNS
  • -
  • FALSE_RETURNS
  • -
  • INCREMENTS
  • -
  • INVERT_NEGS
  • -
  • MATH
  • -
  • NEGATE_CONDITIONALS
  • -
  • NULL_RETURNS
  • -
  • PRIMITIVE_RETURNS
  • -
  • TRUE_RETURNS
  • -
  • VOID_METHOD_CALLS
  • - -
- -

Tests examined

-
    -
  • com.example.ExamplesTest.[engine:junit-jupiter]/[class:com.example.ExamplesTest]/[method:verifyHello()] (8 ms)
  • -
- -
- -Report generated by PIT 1.15.1 - - - \ No newline at end of file diff --git a/examples/reports/mutations/com.example/ExamplesTest.java.html b/examples/reports/mutations/com.example/ExamplesTest.java.html deleted file mode 100644 index a623b82..0000000 --- a/examples/reports/mutations/com.example/ExamplesTest.java.html +++ /dev/null @@ -1,241 +0,0 @@ - - - - - - - - - -

ExamplesTest.java

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -1 - - - - - - -
package com.example;
- -2 - - - - - - -
- -3 - - - - - - -
import org.junit.jupiter.api.Test;
- -4 - - - - - - -
- -5 - - - - - - -
import static org.junit.jupiter.api.Assertions.assertEquals;
- -6 - - - - - - -
- -7 - - - - - - -
public class ExamplesTest {
- -8 - - - - - - -
    @Test
- -9 - - - - - - -
    void verifyHello() {
- -10 - - -1 - -1. verifyHello : removed call to org/junit/jupiter/api/Assertions::assertEquals → SURVIVED
- -
-
-
        assertEquals("Hello World!", new ExamplesLib().getMessage());
- -11 - - - - - - -
    }
- -12 - - - - - - -
}

Mutations

10 - - - -

1.1
Location : verifyHello
Killed by : none
removed call to org/junit/jupiter/api/Assertions::assertEquals → SURVIVED

-
- - -

Active mutators

-
    -
  • CONDITIONALS_BOUNDARY
  • -
  • EMPTY_RETURNS
  • -
  • FALSE_RETURNS
  • -
  • INCREMENTS
  • -
  • INVERT_NEGS
  • -
  • MATH
  • -
  • NEGATE_CONDITIONALS
  • -
  • NULL_RETURNS
  • -
  • PRIMITIVE_RETURNS
  • -
  • TRUE_RETURNS
  • -
  • VOID_METHOD_CALLS
  • - -
- -

Tests examined

-
    -
  • com.example.ExamplesTest.[engine:junit-jupiter]/[class:com.example.ExamplesTest]/[method:verifyHello()] (8 ms)
  • -
- -
- -Report generated by PIT 1.15.1 - - - \ No newline at end of file diff --git a/examples/reports/mutations/com.example/index.html b/examples/reports/mutations/com.example/index.html deleted file mode 100644 index 805a8c3..0000000 --- a/examples/reports/mutations/com.example/index.html +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - -

Pit Test Coverage Report

-

Package Summary

-

com.example

- - - - - - - - - - - - - - - - - -
Number of ClassesLine CoverageMutation CoverageTest Strength
2100%
5/5
50%
1/2
50%
1/2
- - -

Breakdown by Class

- - - - - - - - - - - - - - - - - - - - - - - - - - -
NameLine CoverageMutation CoverageTest Strength
ExamplesLib.java
100%
2/2
100%
1/1
100%
1/1
ExamplesTest.java
100%
3/3
0%
0/1
0%
0/1
-
- - - -
- -Report generated by PIT 1.15.1 - - - \ No newline at end of file diff --git a/examples/reports/mutations/index.html b/examples/reports/mutations/index.html deleted file mode 100644 index dd8241b..0000000 --- a/examples/reports/mutations/index.html +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - -

Pit Test Coverage Report

- -

Project Summary

- - - - - - - - - - - - - - - - - -
Number of ClassesLine CoverageMutation CoverageTest Strength
2100%
5/5
50%
1/2
50%
1/2
- - -

Breakdown by Package

- - - - - - - - - - - - - - - - - - - - - -
NameNumber of ClassesLine CoverageMutation CoverageTest Strength
com.example2
100%
5/5
50%
1/2
50%
1/2
-
- - - -
- -Report generated by PIT 1.15.1 - -
-
- -Enhanced functionality available at arcmutate.com - - - \ No newline at end of file diff --git a/examples/reports/mutations/style.css b/examples/reports/mutations/style.css deleted file mode 100644 index 303bfba..0000000 --- a/examples/reports/mutations/style.css +++ /dev/null @@ -1,563 +0,0 @@ -html, body, div, span, p, blockquote, pre { - margin: 0; - padding: 0; - border: 0; - outline: 0; - font-weight: inherit; - font-style: inherit; - font-size: 100%; - font-family: inherit; - vertical-align: baseline; -} - -body{ - line-height: 1; - color: black; - background: white; - margin-left: 20px; -} - -.src { - border: 1px solid #dddddd; - padding-top: 10px; - padding-right: 5px; - padding-left: 5px; - font-family: Consolas, Courier, monospace; -} - -.covered, .COVERED { - background-color: #ddffdd; -} - -.uncovered, .UNCOVERED { - background-color: #ffdddd; -} - -.killed, .KILLED { - background-color: #aaffaa; -} - -.survived, .SURVIVED { - background-color: #ffaaaa; -} - -.uncertain, .UNCERTAIN { - background-color: #dde7ef; -} - -.run_error, .RUN_ERROR { - background-color: #dde7ef; -} - -.na { - background-color: #eeeeee; -} - -.timed_out, .TIMED_OUT { - background-color: #dde7ef; -} - -.non_viable, .NON_VIABLE { - background-color: #aaffaa; -} - -.memory_error, .MEMORY_ERROR { - background-color: #dde7ef; -} - -.not_started, .NO_STARTED { - background-color: #dde7ef; color : red -} - -.no_coverage, .NO_COVERAGE { - background-color: #ffaaaa; -} - -.tests { - width: 50%; - float: left; -} - -.mutees { - float: right; - width: 50%; -} - -.unit { - padding-top: 20px; - clear: both; -} - -.coverage_bar { - display: inline-block; - height: 1.1em; - width: 130px; - background: #FAA; - margin: 0 5px; - vertical-align: middle; - border: 1px solid #AAA; - position: relative; -} - -.coverage_complete { - display: inline-block; - height: 100%; - background: #DFD; - float: left; -} - -.coverage_legend { - position: absolute; - height: 100%; - width: 100%; - left: 0; - top: 0; - text-align: center; -} - -.line, .mut { - vertical-align: middle; -} - -.coverage_percentage { - display: inline-block; - width: 3em; - text-align: right; -} - -.pop { - outline:none; -} - -.pop strong { - line-height: 30px; -} - -.pop { - text-decoration: none; -} - -.pop span { - z-index: 10; - display: none; - padding: 14px 20px; - margin-top: -30px; - margin-left: 28px; - width: 800px; - line-height: 16px; - word-wrap: break-word; - border-radius: 4px; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - -moz-box-shadow: 5px 5px 8px #CCC; - -webkit-box-shadow: 5px 5px 8px #CCC; - box-shadow: 5px 5px 8px #CCC; -} - -.pop:hover span { - display: inline; - position: absolute; - color: #111; - border: 1px solid #DCA; - background: #fffAF0; -} - -.width-1 { - width: 1%; -} - -.width-2 { - width: 2%; -} - -.width-3 { - width: 3%; -} - -.width-4 { - width: 4%; -} - -.width-5 { - width: 5%; -} - -.width-6 { - width: 6%; -} - -.width-7 { - width: 7%; -} - -.width-8 { - width: 8%; -} - -.width-9 { - width: 9%; -} - -.width-10 { - width: 10%; -} - -.width-11 { - width: 11%; -} - -.width-12 { - width: 12%; -} - -.width-13 { - width: 13%; -} - -.width-14 { - width: 14%; -} - -.width-15 { - width: 15%; -} - -.width-16 { - width: 16%; -} - -.width-17 { - width: 17%; -} - -.width-18 { - width: 18%; -} - -.width-19 { - width: 19%; -} - -.width-20 { - width: 20%; -} - -.width-21 { - width: 21%; -} - -.width-22 { - width: 22%; -} - -.width-23 { - width: 23%; -} - -.width-24 { - width: 24%; -} - -.width-25 { - width: 25%; -} - -.width-26 { - width: 26%; -} - -.width-27 { - width: 27%; -} - -.width-28 { - width: 28%; -} - -.width-29 { - width: 29%; -} - -.width-30 { - width: 30%; -} - -.width-31 { - width: 31%; -} - -.width-32 { - width: 32%; -} - -.width-33 { - width: 33%; -} - -.width-34 { - width: 34%; -} - -.width-35 { - width: 35%; -} - -.width-36 { - width: 36%; -} - -.width-37 { - width: 37%; -} - -.width-38 { - width: 38%; -} - -.width-39 { - width: 39%; -} - -.width-40 { - width: 40%; -} - -.width-41 { - width: 41%; -} - -.width-42 { - width: 42%; -} - -.width-43 { - width: 43%; -} - -.width-44 { - width: 44%; -} - -.width-45 { - width: 45%; -} - -.width-46 { - width: 46%; -} - -.width-47 { - width: 47%; -} - -.width-48 { - width: 48%; -} - -.width-49 { - width: 49%; -} - -.width-50 { - width: 50%; -} - -.width-51 { - width: 51%; -} - -.width-52 { - width: 52%; -} - -.width-53 { - width: 53%; -} - -.width-54 { - width: 54%; -} - -.width-55 { - width: 55%; -} - -.width-56 { - width: 56%; -} - -.width-57 { - width: 57%; -} - -.width-58 { - width: 58%; -} - -.width-59 { - width: 59%; -} - -.width-60 { - width: 60%; -} - -.width-61 { - width: 61%; -} - -.width-62 { - width: 62%; -} - -.width-63 { - width: 63%; -} - -.width-64 { - width: 64%; -} - -.width-65 { - width: 65%; -} - -.width-66 { - width: 66%; -} - -.width-67 { - width: 67%; -} - -.width-68 { - width: 68%; -} - -.width-69 { - width: 69%; -} - -.width-70 { - width: 70%; -} - -.width-71 { - width: 71%; -} - -.width-72 { - width: 72%; -} - -.width-73 { - width: 73%; -} - -.width-74 { - width: 74%; -} - -.width-75 { - width: 75%; -} - -.width-76 { - width: 76%; -} - -.width-77 { - width: 77%; -} - -.width-78 { - width: 78%; -} - -.width-79 { - width: 79%; -} - -.width-80 { - width: 80%; -} - -.width-81 { - width: 81%; -} - -.width-82 { - width: 82%; -} - -.width-83 { - width: 83%; -} - -.width-84 { - width: 84%; -} - -.width-85 { - width: 85%; -} - -.width-86 { - width: 86%; -} - -.width-87 { - width: 87%; -} - -.width-88 { - width: 88%; -} - -.width-89 { - width: 89%; -} - -.width-90 { - width: 90%; -} - -.width-91 { - width: 91%; -} - -.width-92 { - width: 92%; -} - -.width-93 { - width: 93%; -} - -.width-94 { - width: 94%; -} - -.width-95 { - width: 95%; -} - -.width-96 { - width: 96%; -} - -.width-97 { - width: 97%; -} - -.width-98 { - width: 98%; -} - -.width-99 { - width: 99%; -} - -.width-100 { - width: 100%; -} \ No newline at end of file diff --git a/examples/src/bld/java/com/example/ExamplesBuild.java b/examples/src/bld/java/com/example/ExamplesBuild.java index 2f32901..aa1c8a7 100644 --- a/examples/src/bld/java/com/example/ExamplesBuild.java +++ b/examples/src/bld/java/com/example/ExamplesBuild.java @@ -7,6 +7,9 @@ import rife.tools.FileUtils; import java.nio.file.Path; import java.util.List; +import java.util.logging.ConsoleHandler; +import java.util.logging.Level; +import java.util.logging.Logger; import static rife.bld.dependencies.Scope.test; @@ -19,17 +22,33 @@ public class ExamplesBuild extends Project { name = "Examples"; version = version(0, 1, 0); + javaRelease = 17; + + downloadSources = true; + autoDownloadPurge = true; + repositories = List.of(MAVEN_CENTRAL, RIFE2_RELEASES); + var pitest = version(1, 19, 1); scope(test) - .include(dependency("org.pitest", "pitest", version(1, 15, 1))) - .include(dependency("org.pitest", "pitest-command-line", version(1, 15, 1))) - .include(dependency("org.pitest", "pitest-junit5-plugin", version(1, 2, 0))) - .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 10, 0))) - .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 10, 0))); + .include(dependency("org.pitest", "pitest", pitest)) + .include(dependency("org.pitest", "pitest-command-line", pitest)) + .include(dependency("org.pitest", "pitest-junit5-plugin", version(1, 2, 2))) + .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 12, 1))) + .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 12, 1))); } public static void main(String[] args) { + // Enable detailed logging for the extensions + var level = Level.ALL; + var logger = Logger.getLogger("rife.bld.extension"); + var consoleHandler = new ConsoleHandler(); + + consoleHandler.setLevel(level); + logger.addHandler(consoleHandler); + logger.setLevel(level); + logger.setUseParentHandlers(false); + new ExamplesBuild().start(args); } @@ -37,10 +56,10 @@ public class ExamplesBuild extends Project { public void pit() throws Exception { new PitestOperation() .fromProject(this) - .reportDir(Path.of("reports", "mutations").toString()) + .reportDir(Path.of("reports", "mutations")) .targetClasses(pkg + ".*") .targetTests(pkg + ".*") .verbose(true) .execute(); } -} \ No newline at end of file +} diff --git a/lib/bld/bld-wrapper.jar b/lib/bld/bld-wrapper.jar index 9b4ee52..1b14a16 100644 Binary files a/lib/bld/bld-wrapper.jar and b/lib/bld/bld-wrapper.jar differ diff --git a/lib/bld/bld-wrapper.properties b/lib/bld/bld-wrapper.properties index 80e04df..2b5c0ac 100644 --- a/lib/bld/bld-wrapper.properties +++ b/lib/bld/bld-wrapper.properties @@ -1,8 +1,8 @@ bld.downloadExtensionJavadoc=false bld.downloadExtensionSources=true -bld.extension-pmd=com.uwyn.rife2:bld-pmd:0.9.3 -bld.extension-jacoco=com.uwyn.rife2:bld-jacoco-report:0.9.1 -bld.repositories=MAVEN_LOCAL,MAVEN_CENTRAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.downloadLocation= +bld.extension-exec=com.uwyn.rife2:bld-exec:1.0.5 +bld.extension-pmd=com.uwyn.rife2:bld-pmd:1.2.2 +bld.repositories=MAVEN_CENTRAL,MAVEN_LOCAL,RIFE2_SNAPSHOTS,RIFE2_RELEASES bld.sourceDirectories= -bld.version=1.7.5 +bld.version=2.2.1 diff --git a/scripts/checkcliargs.sh b/scripts/checkcliargs.sh new file mode 100755 index 0000000..446c562 --- /dev/null +++ b/scripts/checkcliargs.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +main="org.pitest.mutationtest.commandline.MutationCoverageReport" +new=/tmp/checkcliargs-new +old=/tmp/checkcliargs-old + +java -cp "lib/test/*" $main --help >$new +java -cp "examples/lib/test/*" $main --help >$old + +diff $old $new + +rm -rf $new $old diff --git a/scripts/cliargs.sh b/scripts/cliargs.sh new file mode 100755 index 0000000..2f86041 --- /dev/null +++ b/scripts/cliargs.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +java -cp "lib/test/*" org.pitest.mutationtest.commandline.MutationCoverageReport --help |\ +grep "^.*--.*" |\ +sed -e "s/\* --/--/" -e "s/ .*//" |\ +sort |\ +sed -e '/testPlugin/d' -e '/--help/d' -e '/---/d' > src/test/resources/pitest-args.txt \ No newline at end of file diff --git a/src/bld/java/rife/bld/extension/PitestOperationBuild.java b/src/bld/java/rife/bld/extension/PitestOperationBuild.java index 6735411..67f664f 100644 --- a/src/bld/java/rife/bld/extension/PitestOperationBuild.java +++ b/src/bld/java/rife/bld/extension/PitestOperationBuild.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,57 +22,72 @@ import rife.bld.publish.PublishDeveloper; import rife.bld.publish.PublishLicense; import rife.bld.publish.PublishScm; -import java.io.IOException; import java.util.List; -import static rife.bld.dependencies.Repository.MAVEN_CENTRAL; -import static rife.bld.dependencies.Repository.RIFE2_RELEASES; +import static rife.bld.dependencies.Repository.*; import static rife.bld.dependencies.Scope.compile; import static rife.bld.dependencies.Scope.test; import static rife.bld.operations.JavadocOptions.DocLinkOption.NO_MISSING; public class PitestOperationBuild extends Project { + final PmdOperation pmdOp = new PmdOperation() + .fromProject(this) + .failOnViolation(true) + .ruleSets("config/pmd.xml"); + public PitestOperationBuild() { pkg = "rife.bld.extension"; name = "PitestExtension"; - version = version(0, 9, 0); + version = version(1, 0, 11, "SNAPSHOT"); javaRelease = 17; + downloadSources = true; autoDownloadPurge = true; - repositories = List.of(MAVEN_CENTRAL, RIFE2_RELEASES); - var pitest = version(1, 15, 1); + repositories = List.of(MAVEN_LOCAL, MAVEN_CENTRAL, RIFE2_RELEASES, RIFE2_SNAPSHOTS); + + var pitest = version(1, 19, 1); scope(compile) - .include(dependency("com.uwyn.rife2", "bld", version(1, 7, 5))); + .include(dependency("com.uwyn.rife2", "bld", version(2, 2, 1))); scope(test) .include(dependency("org.pitest", "pitest", pitest)) .include(dependency("org.pitest", "pitest-command-line", pitest)) - .include(dependency("org.pitest", "pitest-junit5-plugin", version(1,2, 0))) - .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 10, 0))) - .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 10, 0))) - .include(dependency("org.assertj", "assertj-core", version(3, 24, 2))); - + .include(dependency("org.pitest", "pitest-junit5-plugin", version(1, 2, 2))) + .include(dependency("org.junit.jupiter", "junit-jupiter", version(5, 12, 1))) + .include(dependency("org.junit.platform", "junit-platform-console-standalone", version(1, 12, 1))) + .include(dependency("org.assertj", "assertj-core", version(3, 27, 3))); + javadocOperation() .javadocOptions() + .author() .docLint(NO_MISSING) .link("https://rife2.github.io/bld/") .link("https://rife2.github.io/rife2/"); publishOperation() .repository(version.isSnapshot() ? repository("rife2-snapshot") : repository("rife2")) + .repository(repository("github")) .info() .groupId("com.uwyn.rife2") .artifactId("bld-pitest") .description("PIT Mutation Testing Extension for bld") .url("https://github.com/rife2/bld-pitest") - .developer(new PublishDeveloper().id("ethauvin").name("Erik C. Thauvin").email("erik@thauvin.net") - .url("https://erik.thauvin.net/")) - .license(new PublishLicense().name("The Apache License, Version 2.0") - .url("http://www.apache.org/licenses/LICENSE-2.0.txt")) - .scm(new PublishScm().connection("scm:git:https://github.com/rife2/bld-pitest.git") + .developer(new PublishDeveloper() + .id("ethauvin") + .name("Erik C. Thauvin") + .email("erik@thauvin.net") + .url("https://erik.thauvin.net/") + ) + .license(new PublishLicense() + .name("The Apache License, Version 2.0") + .url("https://www.apache.org/licenses/LICENSE-2.0.txt") + ) + .scm(new PublishScm() + .connection("scm:git:https://github.com/rife2/bld-pitest.git") .developerConnection("scm:git:git@github.com:rife2/bld-pitest.git") - .url("https://github.com/rife2/bld-pitest")) + .url("https://github.com/rife2/bld-pitest") + ) .signKey(property("sign.key")) .signPassphrase(property("sign.passphrase")); } @@ -81,19 +96,25 @@ public class PitestOperationBuild extends Project { new PitestOperationBuild().start(args); } - @BuildCommand(summary = "Generates JaCoCo Reports") - public void jacoco() throws IOException { - new JacocoReportOperation() - .fromProject(this) - .execute(); + @BuildCommand(summary = "Runs PMD analysis") + public void pmd() throws Exception { + pmdOp.execute(); } - @BuildCommand(summary = "Runs PMD analysis") - public void pmd() { - new PmdOperation() - .fromProject(this) - .failOnViolation(true) - .ruleSets("config/pmd.xml") - .execute(); + @BuildCommand(value = "pmd-cli", summary = "Runs PMD analysis (CLI)") + public void pmdCli() throws Exception { + pmdOp.includeLineNumber(false).execute(); + } + + @Override + public void test() throws Exception { + var os = System.getProperty("os.name"); + if (os != null && os.toLowerCase().contains("linux")) { + new ExecOperation() + .fromProject(this) + .command("scripts/cliargs.sh") + .execute(); + } + super.test(); } } diff --git a/src/main/java/rife/bld/extension/PitestOperation.java b/src/main/java/rife/bld/extension/PitestOperation.java index b8f0ada..632c58a 100644 --- a/src/main/java/rife/bld/extension/PitestOperation.java +++ b/src/main/java/rife/bld/extension/PitestOperation.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,13 +18,14 @@ package rife.bld.extension; import rife.bld.BaseProject; import rife.bld.operations.AbstractProcessOperation; +import rife.bld.operations.exceptions.ExitStatusException; +import java.io.File; +import java.io.IOException; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; import java.util.logging.Logger; /** @@ -38,29 +39,29 @@ public class PitestOperation extends AbstractProcessOperation { * False constant. */ protected static final String FALSE = "false"; - /** - * Source directories command line option. - */ - protected static final String SOURCE_DIRS = "--sourceDirs"; /** * True constant. */ protected static final String TRUE = "true"; private static final Logger LOGGER = Logger.getLogger(PitestOperation.class.getName()); - /** - * The PIT options. - */ - protected final Map options = new ConcurrentHashMap<>(); + private static final String SOURCE_DIRS = "--sourceDirs"; + private final Map options_ = new ConcurrentHashMap<>(); private BaseProject project_; /** * Line arguments for child JVMs. + * + * @param line the line arguments + * @return this operation instance */ public PitestOperation argLine(String line) { - options.put("--argLine", line); + if (isNotBlank(line)) { + options_.put("--argLine", line); + } 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. @@ -76,10 +77,12 @@ public class PitestOperation extends AbstractProcessOperation { *

* If the feature {@code FLOGCALL} is disabled, this parameter is ignored and logging calls are also mutated. * + * @param avoidCallsTo the list of packages + * @return this operation instance * @see #avoidCallsTo(String...) */ public PitestOperation avoidCallsTo(Collection avoidCallsTo) { - options.put("--avoidCallsTo", String.join(",", avoidCallsTo)); + options_.put("--avoidCallsTo", String.join(",", avoidCallsTo.stream().filter(this::isNotBlank).toList())); return this; } @@ -98,10 +101,84 @@ public class PitestOperation extends AbstractProcessOperation { *

* If the feature {@code FLOGCALL} is disabled, this parameter is ignored and logging calls are also mutated. * + * @param avoidCallTo one or more packages + * @return this operation instance * @see #avoidCallsTo(Collection) */ public PitestOperation avoidCallsTo(String... avoidCallTo) { - options.put("--avoidCallsTo", String.join(",", avoidCallTo)); + return avoidCallsTo(List.of(avoidCallTo)); + } + + private String buildClassPath(String... path) { + var classpath = new StringBuilder(); + for (var p : path) { + if (!p.isBlank()) { + if (!classpath.isEmpty()) { + classpath.append(File.pathSeparator); + } + classpath.append(p); + } + } + return classpath.toString(); + } + + /** + * 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 #classPath(Collection) + */ + public PitestOperation classPath(String... path) { + 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)); + } + + /** + * Additional classpath entries to use when looking for tests and mutable code. + * + * @param path the list of paths + * @return this operation instance + * @see #classPath(String...) + */ + public PitestOperation classPath(Collection path) { + options_.put("--classPath", String.join(",", path.stream().filter(this::isNotBlank).toList())); return this; } @@ -121,38 +198,59 @@ public class PitestOperation extends AbstractProcessOperation { * 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. * - * @see #classPath(Collection) + * @param path one or more paths + * @return this operation instance + * @see #classPathFiles(Collection) */ - public PitestOperation classPath(String... path) { - options.put("--classPath", String.join(",", path)); - return this; - } - - /** - * Additional classpath entries to use when looking for tests and mutable code. - * - * @see #classPath(String...) - */ - public PitestOperation classPath(Collection paths) { - options.put("--classPath", String.join(",", paths)); - return this; + public PitestOperation classPath(File... path) { + return classPathFiles(List.of(path)); } /** * File with a list of additional classpath elements (one per line). + * + * @param file the file + * @return this operation instance */ public PitestOperation classPathFile(String file) { - options.put("--classPathFile", file); + if (isNotBlank(file)) { + options_.put("--classPathFile", file); + } 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. + * + * @param threshold the threshold + * @return this operation instance */ public PitestOperation coverageThreshold(int threshold) { if (threshold >= 0 && threshold <= 100) { - options.put("--coverageThreshold", String.valueOf(threshold)); + options_.put("--coverageThreshold", String.valueOf(threshold)); } return this; } @@ -185,12 +283,32 @@ public class PitestOperation extends AbstractProcessOperation { * to produce multiple similar instructions on the same line. *

* Defaults to {@code true} + * + * @param isDetectInlinedCode {@code true} or {@code false} + * @return this operation instance */ public PitestOperation detectInlinedCode(boolean isDetectInlinedCode) { if (isDetectInlinedCode) { - options.put("--detectInlinedCode", TRUE); + options_.put("--detectInlinedCode", TRUE); } else { - options.put("--detectInlinedCode", FALSE); + options_.put("--detectInlinedCode", FALSE); + } + return this; + } + + /** + * Whether to run in dry run mode. + *

+ * Defaults to {@code false} + * + * @param isDryRun {@code true} or {@code false} + * @return this operation instance + */ + public PitestOperation dryRun(boolean isDryRun) { + if (isDryRun) { + options_.put("--dryRun", TRUE); + } else { + options_.put("--dryRun", FALSE); } return this; } @@ -198,20 +316,23 @@ public class PitestOperation extends AbstractProcessOperation { /** * List of globs to match against class names. Matching classes will be excluded from mutation. * + * @param excludedClass the excluded classws + * @return this operation instance * @see #excludedClasses(Collection) */ public PitestOperation excludedClasses(String... excludedClass) { - options.put("--excludedClasses", String.join(",", excludedClass)); - return this; + return excludedClasses(List.of(excludedClass)); } /** * List of globs to match against class names. Matching classes will be excluded from mutation. * + * @param excludedClasses the excluded classes + * @return this operation instance * @see #excludedClasses(String...) */ public PitestOperation excludedClasses(Collection excludedClasses) { - options.put("--excludedClasses", String.join(",", excludedClasses)); + options_.put("--excludedClasses", String.join(",", excludedClasses.stream().filter(this::isNotBlank).toList())); return this; } @@ -219,41 +340,59 @@ public class PitestOperation extends AbstractProcessOperation { * List of TestNG groups/JUnit categories to include in mutation analysis. Note that only class level categories * are supported. * + * @param excludedGroup one or more excluded groups + * @return this operation instance * @see #excludedGroups(Collection) */ public PitestOperation excludedGroups(String... excludedGroup) { - options.put("--excludedGroups", String.join(",", excludedGroup)); - return this; + return excludedGroups(List.of(excludedGroup)); } /** * List of TestNG groups/JUnit categories to include in mutation analysis. Note that only class level categories * are supported. * + * @param excludedGroups the excluded groups + * @return this operation instance * @see #excludedGroups(String...) */ public PitestOperation excludedGroups(Collection excludedGroups) { - options.put("--excludedGroups", String.join(",", excludedGroups)); + options_.put("--excludedGroups", String.join(",", excludedGroups.stream().filter(this::isNotBlank).toList())); return this; } /** * List of globs to match against method names. Methods matching the globs will be excluded from mutation. * + * @param excludedMethod one or more excluded methods + * @return this operation instance * @see #excludedMethods(Collection) */ public PitestOperation excludedMethods(String... excludedMethod) { - options.put("--excludedMethods", String.join(",", excludedMethod)); - return this; + return excludedMethods(List.of(excludedMethod)); } /** * List of globs to match against method names. Methods matching the globs will be excluded from mutation. * + * @param excludedMethods the excluded methods + * @return this operation instance * @see #excludedMethods(String...) */ public PitestOperation excludedMethods(Collection excludedMethods) { - options.put("--excludedMethods", String.join(",", excludedMethods)); + options_.put("--excludedMethods", + String.join(",", excludedMethods.stream().filter(this::isNotBlank).toList())); + return this; + } + + /** + * JUnit4 runners to exclude. + * + * @param runners the runners + * @return this operation instance + */ + public PitestOperation excludedRunners(String runners) { + options_.put("--excludedRunners", runners); return this; } @@ -261,51 +400,72 @@ public class PitestOperation extends AbstractProcessOperation { * List of globs to match against test class names. Matching tests will not be run (note if a test suite includes * an excluded class, then it will “leak” back in). * - * @see #excludedTests(Collection) + * @param testClasses one or more excluded tests + * @return this operation instance + * @see #excludedTestClasses(Collection) */ - public PitestOperation excludedTests(String... excludedTest) { - options.put("--excludedTests", String.join(",", excludedTest)); - return this; + public PitestOperation excludedTestClasses(String... testClasses) { + return excludedTestClasses(List.of(testClasses)); } /** * List of globs to match against test class names. Matching tests will not be run (note if a test suite includes * an excluded class, then it will “leak” back in). * - * @see #excludedTests(String...) + * @param testClasses the excluded tests + * @return this operation instance + * @see #excludedTestClasses(String...) */ - public PitestOperation excludedTests(Collection excludedTests) { - options.put("--excludedTests", String.join(",", excludedTests)); + public PitestOperation excludedTestClasses(Collection testClasses) { + options_.put("--excludedTestClasses", + String.join(",", testClasses.stream().filter(this::isNotBlank).toList())); return this; } + @Override + public void execute() throws IOException, InterruptedException, ExitStatusException { + if (project_ == null) { + if (LOGGER.isLoggable(Level.SEVERE) && !silent()) { + LOGGER.severe("A project must be specified."); + } + throw new ExitStatusException(ExitStatusException.EXIT_FAILURE); + } else { + super.execute(); + } + } + /** * 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 (!options.containsKey(SOURCE_DIRS)) { - options.put(SOURCE_DIRS, project_.srcDirectory().getPath()); + final List args = new ArrayList<>(); + + if (project_ != null) { + args.add(javaTool()); + args.add("-cp"); + args.add(buildClassPath(joinClasspathJar(project_.testClasspathJars()), + joinClasspathJar(project_.compileClasspathJars()), + joinClasspathJar(project_.providedClasspathJars()), + project_.buildMainDirectory().getAbsolutePath(), project_.buildTestDirectory().getAbsolutePath())); + args.add("org.pitest.mutationtest.commandline.MutationCoverageReport"); + + if (!options_.containsKey(SOURCE_DIRS)) { + options_.put(SOURCE_DIRS, project_.srcDirectory().getPath()); + } + + options_.forEach((k, v) -> { + args.add(k); + if (!v.isEmpty()) { + args.add(v); + } + }); } - final List args = new ArrayList<>(); - args.add(javaTool()); - - args.add("-cp"); - args.add(String.format("%s:%s:%s:%s", Path.of(project_.libTestDirectory().getPath(), "*"), - Path.of(project_.libCompileDirectory().getPath(), "*"), project_.buildMainDirectory(), - project_.buildTestDirectory())); - args.add("org.pitest.mutationtest.commandline.MutationCoverageReport"); - - options.forEach((k, v) -> { - args.add(k); - if (!v.isEmpty()) { - args.add(v); - } - }); + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.fine(String.join(" ", args)); + } return args; } @@ -326,12 +486,15 @@ public class PitestOperation extends AbstractProcessOperation { * Whether or not to dump per test line coverage data to disk. *

* Defaults to {@code false} + * + * @param jsExport {@code true} or {@code false} + * @return this operation instance */ public PitestOperation exportLineCoverage(boolean jsExport) { if (jsExport) { - options.put("--exportLineCoverage", TRUE); + options_.put("--exportLineCoverage", TRUE); } else { - options.put("--exportLineCoverage", FALSE); + options_.put("--exportLineCoverage", FALSE); } return this; } @@ -340,12 +503,15 @@ public class PitestOperation extends AbstractProcessOperation { * Whether to throw an error when no mutations found. *

* Defaults to {@code true} + * + * @param isFail {@code true} or {@code false} + * @return this operation instance */ public PitestOperation failWhenNoMutations(boolean isFail) { if (isFail) { - options.put("--failWhenNoMutations", TRUE); + options_.put("--failWhenNoMutations", TRUE); } else { - options.put("--failWhenNoMutations", FALSE); + options_.put("--failWhenNoMutations", FALSE); } return this; } @@ -353,52 +519,125 @@ public class PitestOperation extends AbstractProcessOperation { /** * List of features to enable/disable * + * @param feature the list of features + * @return this operation instance * @see #features(String...) */ public PitestOperation features(Collection feature) { - options.put("--features", String.join(",", feature)); + options_.put("--features", String.join(",", feature.stream().filter(this::isNotBlank).toList())); return this; } /** * List of features to enable/disable * + * @param feature one or more features + * @return this operation instance * @see #features(Collection) */ public PitestOperation features(String... feature) { - options.put("--features", String.join(",", feature)); + return features(List.of(feature)); + } + + /** + * Whether to create a full mutation matrix + * + * @param isFullMutationMatrix {@code true} or {@code false} + * @return this operation instance + */ + public PitestOperation fullMutationMatrix(boolean isFullMutationMatrix) { + if (isFullMutationMatrix) { + options_.put("--fullMutationMatrix", TRUE); + } else { + options_.put("--fullMutationMatrix", FALSE); + } return this; } /** * Path to a file containing history information for incremental analysis. + * + * @param path the path + * @return this operation instance */ public PitestOperation historyInputLocation(String path) { - options.put("--historyInputLocation", path); + if (isNotBlank(path)) { + options_.put("--historyInputLocation", path); + } + 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) + * + * @param path the path + * @return this operation instance + */ + public PitestOperation historyOutputLocation(String path) { + if (isNotBlank(path)) { + options_.put("--historyOutputLocation", path); + } return this; } /** * Path to write history information for incremental analysis. May be the same as - * {@link #historyInputLocation(String) historyInputLocation}. + * {@link #historyInputLocation(String) + * + * @param path the path + * @return this operation instance */ - public PitestOperation historyOutputLocation(String path) { - options.put("--historyOutputLocation", path); - return this; + 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 - * {@link #classPath(String...)} classPath} option will be considered. + * {@link #classPath(String...) classPath} *

* Defaults to {@code true} + * + * @param isLaunchClasspath {@code true} or {@code false} + * @return this operation instance */ public PitestOperation includeLaunchClasspath(boolean isLaunchClasspath) { if (isLaunchClasspath) { - options.put("--includeLaunchClasspath", TRUE); + options_.put("--includeLaunchClasspath", TRUE); } else { - options.put("--includeLaunchClasspath", FALSE); + options_.put("--includeLaunchClasspath", FALSE); } return this; } @@ -407,52 +646,148 @@ public class PitestOperation extends AbstractProcessOperation { * list of TestNG groups/JUnit categories to include in mutation analysis. Note that only class level categories * are supported. * + * @param includedGroup one or more included groups + * @return this operation instance * @see #includedGroups(Collection) */ public PitestOperation includedGroups(String... includedGroup) { - options.put("--includedGroups", String.join(",", includedGroup)); - return this; + return includedGroups(List.of(includedGroup)); } /** * list of TestNG groups/JUnit categories to include in mutation analysis. Note that only class level categories are * supported. * + * @param includedGroups the list of included groups + * @return this operation instance * @see #includedGroups(String...) */ public PitestOperation includedGroups(Collection includedGroups) { - options.put("--includedGroups", String.join(",", includedGroups)); + options_.put("--includedGroups", String.join(",", includedGroups.stream().filter(this::isNotBlank).toList())); return this; } + /** + * Test methods that should be included for challenging the mutants. + * + * @param testMethod the test method + * @return this operation instance + */ + public PitestOperation includedTestMethods(String testMethod) { + options_.put("--includedTestMethods", testMethod); + return this; + } + + /** + * Input encoding. + *

+ * Default is {@code UTF-8}. + * + * @param encoding the encoding + * @return this operation instance + */ + public PitestOperation inputEncoding(String encoding) { + if (isNotBlank(encoding)) { + options_.put("--inputEncoding", encoding); + } + return this; + } + + /* + * Determines if a string is not blank. + */ + private boolean isNotBlank(String s) { + return s != null && !s.isBlank(); + } + + private String joinClasspathJar(List jars) { + if (!jars.isEmpty()) { + return String.join(File.pathSeparator, jars.stream().map(File::getAbsolutePath).toList()); + } else { + return ""; + } + } + /** * Argument string to use when PIT launches child processes. This is most commonly used to increase the amount of * memory available to the process, but may be used to pass any valid JVM argument. * + * @param args one or moe args + * @return this operation instance * @see #jvmArgs(Collection) */ public PitestOperation jvmArgs(String... args) { - options.put("--jvmArgs", String.join(",", args)); - return this; + return jvmArgs(List.of(args)); } /** * Argument string to use when PIT launches child processes. This is most commonly used to increase the amount of * memory available to the process, but may be used to pass any valid JVM argument. * + * @param args the list of args + * @return this operation instance * @see #jvmArgs(String...) */ public PitestOperation jvmArgs(Collection args) { - options.put("--jvmArgs", String.join(",", args)); + options_.put("--jvmArgs", String.join(",", args.stream().filter(this::isNotBlank).toList())); 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(String path) { - options.put("--jvmPath", path); + if (isNotBlank(path)) { + options_.put("--jvmPath", path); + } + 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. + * + * @param maxMutationsPerClass the max number + * @return this operation instance + */ + public PitestOperation maxMutationsPerClass(int maxMutationsPerClass) { + options_.put("--maxMutationsPerClass", String.valueOf(maxMutationsPerClass)); + return this; + } + + /** + * Maximum number of surviving mutants to allow without throwing an error. + * + * @param maxSurviving the maximin number + * @return this operation instance + */ + public PitestOperation maxSurviving(int maxSurviving) { + options_.put("--maxSurviving", String.valueOf(maxSurviving)); return this; } @@ -466,10 +801,66 @@ public class PitestOperation extends AbstractProcessOperation { *

* 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 #mutableCodePaths(Collection) */ public PitestOperation mutableCodePaths(String... path) { - options.put("--mutableCodePaths", String.join(",", path)); + 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)); + } + + /** + * 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(String...) + */ + public PitestOperation mutableCodePaths(Collection paths) { + options_.put("--mutableCodePaths", String.join(",", paths.stream().filter(this::isNotBlank).toList())); return this; } @@ -483,10 +874,42 @@ public class PitestOperation extends AbstractProcessOperation { *

* PIT will always attempt not to mutate test classes even if they are defined on a mutable path. * - * @see #mutableCodePaths(String...) + * @param paths the list of paths + * @return this operation instance + * @see #mutableCodePaths(File...) */ - public PitestOperation mutableCodePaths(Collection paths) { - options.put("--mutableCodePaths", String.join(",", paths)); + 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. + *

+ * Defaults to {@code gregor} + * + * @param engine the engine + * @return this operation instance + */ + public PitestOperation mutationEngine(String engine) { + options_.put("--mutationEngine", engine); return this; } @@ -496,88 +919,252 @@ public class PitestOperation extends AbstractProcessOperation { *

* Please bear in mind that your build may contain equivalent mutations. Careful thought must therefore be given * when selecting a threshold. + * + * @param threshold the threshold + * @return this operation instance */ public PitestOperation mutationThreshold(int threshold) { if (threshold >= 0 && threshold <= 100) { - options.put("--mutationThreshold", String.valueOf(threshold)); + options_.put("--mutationThreshold", String.valueOf(threshold)); } return this; } /** - * List of mutation operators. + * Maximum number of mutations to include. * - * @see #mutators(Collection) + * @param size the size + * @return this operation instance */ - public PitestOperation mutators(String... mutator) { - options.put("--mutators", String.join(",", mutator)); + public PitestOperation mutationUnitSize(int size) { + options_.put("--mutationUnitSize", String.valueOf(size)); return this; } /** * List of mutation operators. * + * @param mutator one or more mutators + * @return this operation instance + * @see #mutators(Collection) + */ + public PitestOperation mutators(String... mutator) { + options_.put("--mutators", String.join(",", Arrays.stream(mutator).filter(this::isNotBlank).toList())); + return this; + } + + /** + * List of mutation operators. + * + * @param mutators the list of mutators + * @return this operation instance * @see #mutators(String...) */ public PitestOperation mutators(Collection mutators) { - options.put("--mutators", String.join(",", mutators)); + options_.put("--mutators", String.join(",", mutators.stream().filter(this::isNotBlank).toList())); return this; } + /** + * Returns the PIT options. + * + * @return the map of options + */ + public Map options() { + return options_; + } + /** * Output encoding. *

* Default is {@code UTF-8}. + * + * @param encoding the encoding + * @return this operation instance */ public PitestOperation outputEncoding(String encoding) { - options.put("--outputEncoding", encoding); + if (isNotBlank(encoding)) { + options_.put("--outputEncoding", encoding); + } return this; } /** - * Comma separated list of formats in which to write mutation results as the mutations are analysed. + * 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}. + *

+ * Defaults to {@code HTML}. + * + * @param outputFormat one or more output formats + * @return this operation instance * @see #outputFormats(Collection) */ public PitestOperation outputFormats(String... outputFormat) { - options.put("--outputFormats", String.join(",", outputFormat)); - return this; + return outputFormats(List.of(outputFormat)); } /** - * Comma separated list of formats in which to write mutation results as the mutations are analysed. + * 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(String...) */ public PitestOperation outputFormats(Collection outputFormats) { - options.put("--outputFormats", String.join(",", outputFormats)); + options_.put("--outputFormats", String.join(",", outputFormats.stream().filter(this::isNotBlank).toList())); + 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. + * + * @param key the key + * @param value the value + * @return this operation instance + */ + public PitestOperation pluginConfiguration(String key, String value) { + options_.put("--pluginConfiguration", key + '=' + value); + return this; + } + + /** + * Project base. + * + * @param file the file + * @return this operations instance + */ + public PitestOperation projectBase(String file) { + options_.put("--projectBase", file); + 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. + * + * @param dir the directory + * @return this operation instance + */ + public PitestOperation reportDir(String dir) { + if (isNotBlank(dir)) { + options_.put("--reportDir", dir); + } return this; } /** * Output directory for the reports. + * + * @param dir the directory + * @return this operation instance */ - public PitestOperation reportDir(String dir) { - options.put("--reportDir", dir); - return this; + 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. *

* Default is {@code false} + * + * @param isSkipFail {@code true} or {@code false} + * @return this operation instance */ public PitestOperation skipFailingTests(boolean isSkipFail) { if (isSkipFail) { - options.put("--skipFailingTests", TRUE); + options_.put("--skipFailingTests", TRUE); } else { - options.put("--skipFailingTests", FALSE); + options_.put("--skipFailingTests", FALSE); } return this; } @@ -585,21 +1172,69 @@ public class PitestOperation extends AbstractProcessOperation { /** * The folder(s) containing the source code. * + * @param dir one or more directories + * @return this operation instance * @see #sourceDirs(Collection) */ public PitestOperation sourceDirs(String... dir) { - options.put(SOURCE_DIRS, String.join(",", dir)); + 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)); + } + + /** + * The folder(s) containing the source code. + * + * @param dirs the list of directories + * @return this operation instance + * @see #sourceDirs(String...) + */ + public PitestOperation sourceDirs(Collection dirs) { + options_.put(SOURCE_DIRS, String.join(",", dirs.stream().filter(this::isNotBlank).toList())); return this; } /** * The folder(s) containing the source code. * - * @see #sourceDirs(String...) + * @param dirs the list of directories + * @return this operation instance + * @see #sourceDirs(File...) */ - public PitestOperation sourceDirs(Collection dirs) { - options.put(SOURCE_DIRS, String.join(",", dirs)); - return this; + 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()); + } /** @@ -611,10 +1246,12 @@ public class PitestOperation extends AbstractProcessOperation { *

  • {@code com.mycompany.package.*, com.mycompany.packageB.Foo, com.partner.*}
  • *

    * + * @param targetClass the list of target classes + * @return this operation instance * @see #targetClasses(Collection) */ public PitestOperation targetClasses(Collection targetClass) { - options.put("--targetClasses", String.join(",", targetClass)); + options_.put("--targetClasses", String.join(",", targetClass.stream().filter(this::isNotBlank).toList())); return this; } @@ -627,48 +1264,66 @@ public class PitestOperation extends AbstractProcessOperation { *
  • {@code com.mycompany.package.*, com.mycompany.packageB.Foo, com.partner.*}
  • *

    * + * @param targetClass one or more target classes + * @return this operation instance * @see #targetClasses(String...) */ public PitestOperation targetClasses(String... targetClass) { - options.put("--targetClasses", String.join(",", targetClass)); - return this; + return targetClasses(List.of(targetClass)); } /** - * A comma separated list of globs can be supplied to this parameter to limit the tests available to be run. + * A list of globs can be supplied to this parameter to limit the tests available to be run. * If this parameter is not supplied then any test fixture that matched targetClasses may be used, it is however * recommended that this parameter is always explicitly set. *

    * This parameter can be used to point PIT to a top level suite or suites. Custom suites such as * ClassPathSuite are supported. * + * @param test one or more tests + * @return this operation instance * @see #targetTests(Collection) */ public PitestOperation targetTests(String... test) { - options.put("--targetTests", String.join(",", test)); - return this; + return targetTests(List.of(test)); } /** - * A comma separated list of globs can be supplied to this parameter to limit the tests available to be run. + * A list of globs can be supplied to this parameter to limit the tests available to be run. * If this parameter is not supplied then any test fixture that matched targetClasses may be used, it is however * recommended that this parameter is always explicitly set. *

    * This parameter can be used to point PIT to a top level suite or suites. Custom suites such as * ClassPathSuite are supported. * + * @param tests the list of tests + * @return this operation instance * @see #targetTests(String...) */ public PitestOperation targetTests(Collection tests) { - options.put("--targetTests", String.join(",", tests)); + options_.put("--targetTests", String.join(",", tests.stream().filter(this::isNotBlank).toList())); + return this; + } + + /** + * Test strength score below which to throw an error. + * + * @param threshold the threshold + * @return this operation instance + */ + public PitestOperation testStrengthThreshold(int threshold) { + options_.put("--testStrengthThreshold", String.valueOf(threshold)); return this; } /** * The number of threads to use when mutation testing. + * + * @param threads the threads count + * @return this operation instance */ public PitestOperation threads(int threads) { - options.put("--threads", String.valueOf(threads)); + options_.put("--threads", String.valueOf(threads)); return this; } @@ -677,9 +1332,12 @@ public class PitestOperation extends AbstractProcessOperation { * before considering it to be stuck in an infinite loop. *

    * Defaults to {@code 4000} + * + * @param factor the factor amount + * @return this operation instance */ public PitestOperation timeoutConst(int factor) { - options.put("--timeoutConst", String.valueOf(factor)); + options_.put("--timeoutConst", String.valueOf(factor)); return this; } @@ -687,23 +1345,29 @@ public class PitestOperation extends AbstractProcessOperation { * A factor to apply to the normal runtime of a test when considering if it is stuck in an infinite loop. *

    * Defaults to {@code 1.25} + * + * @param factor the factor + * @return this operation instance */ public PitestOperation timeoutFactor(double factor) { - options.put("--timeoutFactor", String.valueOf(factor)); + options_.put("--timeoutFactor", String.valueOf(factor)); return this; } /** - * By default PIT will create a date and time stamped folder for its output each time it is run. This can can make + * By default, PIT will create a date and time stamped folder for its output each time it is run. This can can make * automation difficult, so the behaviour can be suppressed by passing {@code false}. *

    * Defaults to {@code false} + * + * @param isTimestamped {@code true} or {@code false} + * @return this operation instance */ public PitestOperation timestampedReports(boolean isTimestamped) { if (isTimestamped) { - options.put("--timestampedReports", TRUE); + options_.put("--timestampedReports", TRUE); } else { - options.put("--timestampedReports", FALSE); + options_.put("--timestampedReports", FALSE); } return this; } @@ -712,12 +1376,15 @@ public class PitestOperation extends AbstractProcessOperation { * Support large classpaths by creating a classpath jar. *

    * Defaults to {@code false} + * + * @param isUseClasspathJar {@code true} or {@code false} + * @return this operation instance */ public PitestOperation useClasspathJar(boolean isUseClasspathJar) { if (isUseClasspathJar) { - options.put("--useClasspathJar", TRUE); + options_.put("--useClasspathJar", TRUE); } else { - options.put("--useClasspathJar", FALSE); + options_.put("--useClasspathJar", FALSE); } return this; } @@ -726,13 +1393,29 @@ public class PitestOperation extends AbstractProcessOperation { * Output verbose logging. *

    * Defaults to {@code false} + * + * @param isVerbose {@code true} or {@code false} + * @return this operation instance */ public PitestOperation verbose(boolean isVerbose) { if (isVerbose) { - options.put("--verbose", TRUE); + options_.put("--verbose", TRUE); } else { - options.put("--verbose", FALSE); + options_.put("--verbose", FALSE); } return this; } -} \ No newline at end of file + + /** + * The verbosity of output. + *

    + * Defaults to {@code DEFAULT} + * + * @param verbosity the verbosity + * @return this operation instance + */ + public PitestOperation verbosity(String verbosity) { + options_.put("--verbosity", verbosity); + return this; + } +} diff --git a/src/test/java/rife/bld/extension/PitestOperationTest.java b/src/test/java/rife/bld/extension/PitestOperationTest.java index 70d5ef8..bb5ed8c 100644 --- a/src/test/java/rife/bld/extension/PitestOperationTest.java +++ b/src/test/java/rife/bld/extension/PitestOperationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 the original author or authors. + * Copyright 2023-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,32 +16,40 @@ package rife.bld.extension; +import org.assertj.core.api.AutoCloseableSoftAssertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; import rife.bld.BaseProject; 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; import java.util.List; import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; -import static rife.bld.extension.PitestOperation.*; +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"; - private final static String FOO = "foo"; - private final static String FOOBAR = FOO + ',' + BAR; + private static final String BAR = "bar"; + private static final String FOO = "foo"; + private static final String FOOBAR = FOO + ',' + BAR; @Test void argLine() { var op = new PitestOperation() .fromProject(new BaseProject()) .argLine(FOO); - assertThat(op.options.get("--argLine")).isEqualTo(FOO); + assertThat(op.options().get("--argLine")).isEqualTo(FOO); } @Test @@ -49,12 +57,87 @@ class PitestOperationTest { var op = new PitestOperation() .fromProject(new BaseProject()) .avoidCallsTo(FOO, BAR); - assertThat(op.options.get("--avoidCallsTo")).isEqualTo(FOOBAR); + assertThat(op.options().get("--avoidCallsTo")).isEqualTo(FOOBAR); op = new PitestOperation() .fromProject(new Project()) .avoidCallsTo(List.of(FOO, BAR)); - assertThat(op.options.get("--avoidCallsTo")).as(AS_LIST).isEqualTo(FOOBAR); + assertThat(op.options().get("--avoidCallsTo")).as(AS_LIST).isEqualTo(FOOBAR); + } + + @Test + @EnabledOnOs(OS.LINUX) + void checkAllParameters() throws IOException { + var args = Files.readAllLines(Paths.get("src", "test", "resources", "pitest-args.txt")); + + assertThat(args).isNotEmpty(); + + var params = new PitestOperation() + .fromProject(new BaseProject()) + .argLine(FOO) + .avoidCallsTo(FOO, BAR) + .classPath(FOO, BAR) + .classPathFile(FOO) + .coverageThreshold(0) + .detectInlinedCode(false) + .dryRun(false) + .excludedClasses("class") + .excludedClasses(List.of(FOO, BAR)) + .excludedGroups("group") + .excludedGroups(List.of(FOO, BAR)) + .excludedMethods("method") + .excludedMethods(List.of(FOO, BAR)) + .excludedRunners("runners") + .excludedTestClasses("test") + .exportLineCoverage(true) + .failWhenNoMutations(true) + .features("feature") + .fullMutationMatrix(true) + .historyInputLocation("inputLocation") + .historyOutputLocation("outputLocation") + .includeLaunchClasspath(true) + .includedGroups("group") + .includedTestMethods("method") + .inputEncoding("encoding") + .jvmArgs("-XX:+UnlogregckDiagnosticVMOptions") + .jvmPath("path") + .maxMutationsPerClass(3) + .maxSurviving(1) + .mutableCodePaths("codePaths") + .mutationEngine("engine") + .mutationThreshold(0) + .mutationUnitSize(1) + .mutators(List.of(FOO, BAR)) + .outputEncoding("encoding") + .outputFormats("json") + .pluginConfiguration("key", "value") + .projectBase("base") + .reportDir("dir") + .skipFailingTests(true) + .targetClasses("class") + .targetTests("test") + .testStrengthThreshold(0) + .threads(0) + .timeoutConst(0) + .timeoutFactor(0) + .timestampedReports(true) + .useClasspathJar(true) + .verbose(true) + .verbosity("default") + .executeConstructProcessCommandList(); + + try (var softly = new AutoCloseableSoftAssertions()) { + for (var p : args) { + var found = false; + for (var a : params) { + if (a.startsWith(p)) { + found = true; + break; + } + } + softly.assertThat(found).as(p + " not found.").isTrue(); + } + } } @Test @@ -62,12 +145,12 @@ class PitestOperationTest { var op = new PitestOperation() .fromProject(new BaseProject()) .classPath(FOO, BAR); - assertThat(op.options.get("--classPath")).isEqualTo(FOOBAR); + assertThat(op.options().get("--classPath")).isEqualTo(FOOBAR); op = new PitestOperation() .fromProject(new Project()) .classPath(List.of(FOO, BAR)); - assertThat(op.options.get("--classPath")).as(AS_LIST).isEqualTo(FOOBAR); + assertThat(op.options().get("--classPath")).as(AS_LIST).isEqualTo(FOOBAR); } @Test @@ -75,7 +158,7 @@ class PitestOperationTest { var op = new PitestOperation() .fromProject(new BaseProject()) .classPathFile(FOO); - assertThat(op.options.get("--classPathFile")).isEqualTo(FOO); + assertThat(op.options().get("--classPathFile")).isEqualTo(FOO); } @Test @@ -83,12 +166,12 @@ class PitestOperationTest { var op = new PitestOperation() .fromProject(new BaseProject()) .coverageThreshold(3); - assertThat(op.options.get("--coverageThreshold")).isEqualTo("3"); + assertThat(op.options().get("--coverageThreshold")).isEqualTo("3"); op = new PitestOperation() .fromProject(new BaseProject()) .coverageThreshold(101); - assertThat(op.options.get("--coverageThreshold")).isNull(); + assertThat(op.options().get("--coverageThreshold")).isNull(); } @Test @@ -96,12 +179,12 @@ class PitestOperationTest { var op = new PitestOperation() .fromProject(new BaseProject()) .detectInlinedCode(true); - assertThat(op.options.get("--detectInlinedCode")).isEqualTo(TRUE); + assertThat(op.options().get("--detectInlinedCode")).isEqualTo(TRUE); op = new PitestOperation() .fromProject(new Project()) .detectInlinedCode(false); - assertThat(op.options.get("--detectInlinedCode")).isEqualTo(FALSE); + assertThat(op.options().get("--detectInlinedCode")).isEqualTo(FALSE); } @Test @@ -109,12 +192,12 @@ class PitestOperationTest { var op = new PitestOperation() .fromProject(new BaseProject()) .excludedClasses(FOO, BAR); - assertThat(op.options.get("--excludedClasses")).isEqualTo(FOOBAR); + assertThat(op.options().get("--excludedClasses")).isEqualTo(FOOBAR); op = new PitestOperation() .fromProject(new Project()) .excludedClasses(Set.of(FOO, BAR)); - assertThat(op.options.get("--excludedClasses")).as("as set").contains(FOO).contains(BAR).contains(","); + assertThat(op.options().get("--excludedClasses")).as("as set").contains(FOO).contains(BAR).contains(","); } @Test @@ -122,12 +205,12 @@ class PitestOperationTest { var op = new PitestOperation() .fromProject(new BaseProject()) .excludedGroups(FOO, BAR); - assertThat(op.options.get("--excludedGroups")).isEqualTo(FOOBAR); + assertThat(op.options().get("--excludedGroups")).isEqualTo(FOOBAR); op = new PitestOperation() .fromProject(new Project()) .excludedGroups(List.of(FOO, BAR)); - assertThat(op.options.get("--excludedGroups")).as(AS_LIST).isEqualTo(FOOBAR); + assertThat(op.options().get("--excludedGroups")).as(AS_LIST).isEqualTo(FOOBAR); } @Test @@ -135,30 +218,38 @@ class PitestOperationTest { var op = new PitestOperation() .fromProject(new BaseProject()) .excludedMethods(FOO, BAR); - assertThat(op.options.get("--excludedMethods")).isEqualTo(FOOBAR); + assertThat(op.options().get("--excludedMethods")).isEqualTo(FOOBAR); op = new PitestOperation() .fromProject(new Project()) .excludedMethods(List.of(FOO, BAR)); - assertThat(op.options.get("--excludedMethods")).as(AS_LIST).isEqualTo(FOOBAR); + assertThat(op.options().get("--excludedMethods")).as(AS_LIST).isEqualTo(FOOBAR); + } + + @Test + void excludedRunners() { + var op = new PitestOperation() + .fromProject(new BaseProject()) + .excludedRunners(FOO); + assertThat(op.options().get("--excludedRunners")).isEqualTo(FOO); } @Test void excludedTests() { var op = new PitestOperation() .fromProject(new BaseProject()) - .excludedTests(FOO, BAR); - assertThat(op.options.get("--excludedTests")).isEqualTo(FOOBAR); + .excludedTestClasses(FOO, BAR); + assertThat(op.options().get("--excludedTestClasses")).isEqualTo(FOOBAR); op = new PitestOperation() .fromProject(new Project()) - .excludedTests(List.of(FOO, BAR)); - assertThat(op.options.get("--excludedTests")).as("as list").isEqualTo(FOOBAR); + .excludedTestClasses(List.of(FOO, BAR)); + assertThat(op.options().get("--excludedTestClasses")).as("as list").isEqualTo(FOOBAR); } @Test void execute() throws IOException { - var tmpDir = Files.createTempDirectory("bld-pitest"); + var tmpDir = Files.createTempDirectory("bld-pitest-"); tmpDir.toFile().deleteOnExit(); var op = new PitestOperation(). fromProject(new WebProject()) @@ -207,17 +298,23 @@ class PitestOperationTest { "--sourceDirs c:\\myProject\\src"); } + @Test + void executeNoProject() { + var op = new PitestOperation(); + assertThatCode(op::execute).isInstanceOf(ExitStatusException.class); + } + @Test void exportLineCoverage() { var op = new PitestOperation() .fromProject(new BaseProject()) .exportLineCoverage(true); - assertThat(op.options.get("--exportLineCoverage")).isEqualTo(TRUE); + assertThat(op.options().get("--exportLineCoverage")).isEqualTo(TRUE); op = new PitestOperation() .fromProject(new Project()) .exportLineCoverage(false); - assertThat(op.options.get("--exportLineCoverage")).isEqualTo(FALSE); + assertThat(op.options().get("--exportLineCoverage")).isEqualTo(FALSE); } @Test @@ -225,12 +322,12 @@ class PitestOperationTest { var op = new PitestOperation() .fromProject(new BaseProject()) .failWhenNoMutations(true); - assertThat(op.options.get("--failWhenNoMutations")).isEqualTo(TRUE); + assertThat(op.options().get("--failWhenNoMutations")).isEqualTo(TRUE); op = new PitestOperation() .fromProject(new Project()) .failWhenNoMutations(false); - assertThat(op.options.get("--failWhenNoMutations")).isEqualTo(FALSE); + assertThat(op.options().get("--failWhenNoMutations")).isEqualTo(FALSE); } @Test @@ -238,12 +335,20 @@ class PitestOperationTest { var op = new PitestOperation() .fromProject(new BaseProject()) .features(FOO, BAR); - assertThat(op.options.get("--features")).isEqualTo(FOOBAR); + assertThat(op.options().get("--features")).isEqualTo(FOOBAR); op = new PitestOperation() .fromProject(new Project()) .features(List.of(FOO, BAR)); - assertThat(op.options.get("--features")).as(AS_LIST).isEqualTo(FOOBAR); + assertThat(op.options().get("--features")).as(AS_LIST).isEqualTo(FOOBAR); + } + + @Test + void fullMutationMatrix() { + var op = new PitestOperation() + .fromProject(new BaseProject()) + .fullMutationMatrix(true); + assertThat(op.options().get("--fullMutationMatrix")).isEqualTo(TRUE); } @Test @@ -251,7 +356,7 @@ class PitestOperationTest { var op = new PitestOperation() .fromProject(new BaseProject()) .historyInputLocation(FOO); - assertThat(op.options.get("--historyInputLocation")).isEqualTo(FOO); + assertThat(op.options().get("--historyInputLocation")).isEqualTo(FOO); } @Test @@ -259,7 +364,7 @@ class PitestOperationTest { var op = new PitestOperation() .fromProject(new BaseProject()) .historyOutputLocation(FOO); - assertThat(op.options.get("--historyOutputLocation")).isEqualTo(FOO); + assertThat(op.options().get("--historyOutputLocation")).isEqualTo(FOO); } @Test @@ -267,12 +372,12 @@ class PitestOperationTest { var op = new PitestOperation() .fromProject(new BaseProject()) .includeLaunchClasspath(true); - assertThat(op.options.get("--includeLaunchClasspath")).isEqualTo(TRUE); + assertThat(op.options().get("--includeLaunchClasspath")).isEqualTo(TRUE); op = new PitestOperation() .fromProject(new Project()) .includeLaunchClasspath(false); - assertThat(op.options.get("--includeLaunchClasspath")).isEqualTo(FALSE); + assertThat(op.options().get("--includeLaunchClasspath")).isEqualTo(FALSE); } @Test @@ -280,12 +385,28 @@ class PitestOperationTest { var op = new PitestOperation() .fromProject(new BaseProject()) .includedGroups(FOO, BAR); - assertThat(op.options.get("--includedGroups")).isEqualTo(FOOBAR); + assertThat(op.options().get("--includedGroups")).isEqualTo(FOOBAR); op = new PitestOperation() .fromProject(new Project()) .includedGroups(List.of(FOO, BAR)); - assertThat(op.options.get("--includedGroups")).as(AS_LIST).isEqualTo(FOOBAR); + assertThat(op.options().get("--includedGroups")).as(AS_LIST).isEqualTo(FOOBAR); + } + + @Test + void includedTestMethods() { + var op = new PitestOperation() + .fromProject(new Project()) + .includedTestMethods(FOO); + assertThat(op.options().get("--includedTestMethods")).isEqualTo(FOO); + } + + @Test + void inputEncoding() { + var op = new PitestOperation() + .fromProject(new BaseProject()) + .inputEncoding(FOO); + assertThat(op.options().get("--inputEncoding")).isEqualTo(FOO); } @Test @@ -293,12 +414,12 @@ class PitestOperationTest { var op = new PitestOperation() .fromProject(new BaseProject()) .jvmArgs(FOO, BAR); - assertThat(op.options.get("--jvmArgs")).isEqualTo(FOOBAR); + assertThat(op.options().get("--jvmArgs")).isEqualTo(FOOBAR); op = new PitestOperation() .fromProject(new Project()) .jvmArgs(List.of(FOO, BAR)); - assertThat(op.options.get("--jvmArgs")).as(AS_LIST).isEqualTo(FOOBAR); + assertThat(op.options().get("--jvmArgs")).as(AS_LIST).isEqualTo(FOOBAR); } @Test @@ -306,7 +427,23 @@ class PitestOperationTest { var op = new PitestOperation() .fromProject(new BaseProject()) .jvmPath(FOO); - assertThat(op.options.get("--jvmPath")).isEqualTo(FOO); + assertThat(op.options().get("--jvmPath")).isEqualTo(FOO); + } + + @Test + void maxMutationsPerClass() { + var op = new PitestOperation() + .fromProject(new BaseProject()) + .maxMutationsPerClass(12); + assertThat(op.options().get("--maxMutationsPerClass")).isEqualTo("12"); + } + + @Test + void maxSurviving() { + var op = new PitestOperation() + .fromProject(new Project()) + .maxSurviving(1); + assertThat(op.options().get("--maxSurviving")).isEqualTo("1"); } @Test @@ -314,12 +451,20 @@ class PitestOperationTest { var op = new PitestOperation() .fromProject(new BaseProject()) .mutableCodePaths(FOO, BAR); - assertThat(op.options.get("--mutableCodePaths")).isEqualTo(FOOBAR); + assertThat(op.options().get("--mutableCodePaths")).isEqualTo(FOOBAR); op = new PitestOperation() .fromProject(new Project()) .mutableCodePaths(List.of(FOO, BAR)); - assertThat(op.options.get("--mutableCodePaths")).as(AS_LIST).isEqualTo(FOOBAR); + assertThat(op.options().get("--mutableCodePaths")).as(AS_LIST).isEqualTo(FOOBAR); + } + + @Test + void mutationEngine() { + var op = new PitestOperation() + .fromProject(new Project()) + .mutationEngine(FOO); + assertThat(op.options().get("--mutationEngine")).isEqualTo(FOO); } @Test @@ -327,12 +472,20 @@ class PitestOperationTest { var op = new PitestOperation() .fromProject(new BaseProject()) .mutationThreshold(3); - assertThat(op.options.get("--mutationThreshold")).isEqualTo("3"); + assertThat(op.options().get("--mutationThreshold")).isEqualTo("3"); op = new PitestOperation() .fromProject(new BaseProject()) .mutationThreshold(101); - assertThat(op.options.get("--mutationThreshold")).isNull(); + assertThat(op.options().get("--mutationThreshold")).isNull(); + } + + @Test + void mutationUnitSize() { + var op = new PitestOperation() + .fromProject(new Project()) + .mutationUnitSize(2); + assertThat(op.options().get("--mutationUnitSize")).isEqualTo("2"); } @Test @@ -340,12 +493,12 @@ class PitestOperationTest { var op = new PitestOperation() .fromProject(new BaseProject()) .mutators(FOO, BAR); - assertThat(op.options.get("--mutators")).isEqualTo(FOOBAR); + assertThat(op.options().get("--mutators")).isEqualTo(FOOBAR); op = new PitestOperation() .fromProject(new Project()) .mutators(List.of(FOO, BAR)); - assertThat(op.options.get("--mutators")).as(AS_LIST).isEqualTo(FOOBAR); + assertThat(op.options().get("--mutators")).as(AS_LIST).isEqualTo(FOOBAR); } @Test @@ -353,7 +506,7 @@ class PitestOperationTest { var op = new PitestOperation() .fromProject(new BaseProject()) .outputEncoding(FOO); - assertThat(op.options.get("--outputEncoding")).isEqualTo(FOO); + assertThat(op.options().get("--outputEncoding")).isEqualTo(FOO); } @Test @@ -361,12 +514,28 @@ class PitestOperationTest { var op = new PitestOperation() .fromProject(new BaseProject()) .outputFormats(FOO, BAR); - assertThat(op.options.get("--outputFormats")).isEqualTo(FOOBAR); + assertThat(op.options().get("--outputFormats")).isEqualTo(FOOBAR); op = new PitestOperation() .fromProject(new Project()) .outputFormats(List.of(FOO, BAR)); - assertThat(op.options.get("--outputFormats")).as(AS_LIST).isEqualTo(FOOBAR); + assertThat(op.options().get("--outputFormats")).as(AS_LIST).isEqualTo(FOOBAR); + } + + @Test + void pluginConfiguration() { + var op = new PitestOperation() + .fromProject(new Project()) + .pluginConfiguration(FOO, BAR); + assertThat(op.options().get("--pluginConfiguration")).isEqualTo(FOO + "=" + BAR); + } + + @Test + void projectBase() { + var op = new PitestOperation() + .fromProject(new Project()) + .projectBase(FOO); + assertThat(op.options().get("--projectBase")).isEqualTo(FOO); } @Test @@ -374,7 +543,7 @@ class PitestOperationTest { var op = new PitestOperation() .fromProject(new BaseProject()) .reportDir(FOO); - assertThat(op.options.get("--reportDir")).isEqualTo(FOO); + assertThat(op.options().get("--reportDir")).isEqualTo(FOO); } @Test @@ -382,12 +551,12 @@ class PitestOperationTest { var op = new PitestOperation() .fromProject(new BaseProject()) .skipFailingTests(true); - assertThat(op.options.get("--skipFailingTests")).isEqualTo(TRUE); + assertThat(op.options().get("--skipFailingTests")).isEqualTo(TRUE); op = new PitestOperation() .fromProject(new Project()) .skipFailingTests(false); - assertThat(op.options.get("--skipFailingTests")).isEqualTo(FALSE); + assertThat(op.options().get("--skipFailingTests")).isEqualTo(FALSE); } @Test @@ -395,12 +564,12 @@ class PitestOperationTest { var op = new PitestOperation() .fromProject(new BaseProject()) .sourceDirs(FOO, BAR); - assertThat(op.options.get(SOURCE_DIRS)).isEqualTo(FOOBAR); + assertThat(op.options().get("--sourceDirs")).isEqualTo(FOOBAR); op = new PitestOperation() .fromProject(new Project()) .sourceDirs(List.of(FOO, BAR)); - assertThat(op.options.get(SOURCE_DIRS)).as(AS_LIST).isEqualTo(FOOBAR); + assertThat(op.options().get("--sourceDirs")).as(AS_LIST).isEqualTo(FOOBAR); } @Test @@ -408,12 +577,12 @@ class PitestOperationTest { var op = new PitestOperation() .fromProject(new BaseProject()) .targetClasses(FOO, BAR); - assertThat(op.options.get("--targetClasses")).isEqualTo(FOOBAR); + assertThat(op.options().get("--targetClasses")).isEqualTo(FOOBAR); op = new PitestOperation() .fromProject(new Project()) .targetClasses(List.of(FOO, BAR)); - assertThat(op.options.get("--targetClasses")).as(AS_LIST).isEqualTo(FOOBAR); + assertThat(op.options().get("--targetClasses")).as(AS_LIST).isEqualTo(FOOBAR); } @Test @@ -421,12 +590,189 @@ class PitestOperationTest { var op = new PitestOperation() .fromProject(new BaseProject()) .targetTests(FOO, BAR); - assertThat(op.options.get("--targetTests")).isEqualTo(FOOBAR); + assertThat(op.options().get("--targetTests")).isEqualTo(FOOBAR); op = new PitestOperation() .fromProject(new Project()) .targetTests(List.of(FOO, BAR)); - assertThat(op.options.get("--targetTests")).as(AS_LIST).isEqualTo(FOOBAR); + 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() + .fromProject(new Project()) + .testStrengthThreshold(6); + assertThat(op.options().get("--testStrengthThreshold")).isEqualTo("6"); } @Test @@ -434,7 +780,7 @@ class PitestOperationTest { var op = new PitestOperation() .fromProject(new BaseProject()) .threads(3); - assertThat(op.options.get("--threads")).isEqualTo("3"); + assertThat(op.options().get("--threads")).isEqualTo("3"); } @Test @@ -442,7 +788,7 @@ class PitestOperationTest { var op = new PitestOperation() .fromProject(new BaseProject()) .timeoutConst(300); - assertThat(op.options.get("--timeoutConst")).isEqualTo("300"); + assertThat(op.options().get("--timeoutConst")).isEqualTo("300"); } @Test @@ -450,7 +796,7 @@ class PitestOperationTest { var op = new PitestOperation() .fromProject(new BaseProject()) .timeoutFactor(5.25); - assertThat(op.options.get("--timeoutFactor")).isEqualTo("5.25"); + assertThat(op.options().get("--timeoutFactor")).isEqualTo("5.25"); } @Test @@ -458,12 +804,12 @@ class PitestOperationTest { var op = new PitestOperation() .fromProject(new BaseProject()) .timestampedReports(true); - assertThat(op.options.get("--timestampedReports")).isEqualTo(TRUE); + assertThat(op.options().get("--timestampedReports")).isEqualTo(TRUE); op = new PitestOperation() .fromProject(new Project()) .timestampedReports(false); - assertThat(op.options.get("--timestampedReports")).isEqualTo(FALSE); + assertThat(op.options().get("--timestampedReports")).isEqualTo(FALSE); } @Test @@ -471,12 +817,12 @@ class PitestOperationTest { var op = new PitestOperation() .fromProject(new BaseProject()) .useClasspathJar(true); - assertThat(op.options.get("--useClasspathJar")).isEqualTo(TRUE); + assertThat(op.options().get("--useClasspathJar")).isEqualTo(TRUE); op = new PitestOperation() .fromProject(new Project()) .useClasspathJar(false); - assertThat(op.options.get("--useClasspathJar")).isEqualTo(FALSE); + assertThat(op.options().get("--useClasspathJar")).isEqualTo(FALSE); } @Test @@ -484,11 +830,19 @@ class PitestOperationTest { var op = new PitestOperation() .fromProject(new BaseProject()) .verbose(true); - assertThat(op.options.get("--verbose")).isEqualTo(TRUE); + assertThat(op.options().get("--verbose")).isEqualTo(TRUE); op = new PitestOperation() .fromProject(new Project()) .verbose(false); - assertThat(op.options.get("--verbose")).isEqualTo(FALSE); + assertThat(op.options().get("--verbose")).isEqualTo(FALSE); } -} \ No newline at end of file + + @Test + void verbosity() { + var op = new PitestOperation() + .fromProject(new Project()) + .verbosity(FOO); + assertThat(op.options().get("--verbosity")).isEqualTo(FOO); + } +} diff --git a/src/test/resources/pitest-args.txt b/src/test/resources/pitest-args.txt new file mode 100644 index 0000000..df24bfb --- /dev/null +++ b/src/test/resources/pitest-args.txt @@ -0,0 +1,48 @@ +--argLine +--avoidCallsTo +--classPath +--classPathFile +--coverageThreshold +--detectInlinedCode +--dryRun +--excludedClasses +--excludedGroups +--excludedMethods +--excludedRunners +--excludedTestClasses +--exportLineCoverage +--failWhenNoMutations +--features +--fullMutationMatrix +--historyInputLocation +--historyOutputLocation +--includedGroups +--includedTestMethods +--includeLaunchClasspath +--inputEncoding +--jvmArgs +--jvmPath +--maxMutationsPerClass +--maxSurviving +--mutableCodePaths +--mutationEngine +--mutationThreshold +--mutationUnitSize +--mutators +--outputEncoding +--outputFormats +--pluginConfiguration +--projectBase +--reportDir +--skipFailingTests +--sourceDirs +--targetClasses +--targetTests +--testStrengthThreshold +--threads +--timeoutConst +--timeoutFactor +--timestampedReports +--useClasspathJar +--verbose +--verbosity