From 3a1b407446e69934c5b104884a0782eb9b6634a4 Mon Sep 17 00:00:00 2001 From: Adam Armistead Date: Thu, 13 Apr 2023 21:04:00 -0700 Subject: [PATCH] Upgraded to java 11 and nio file management Upgrade to junit 5 tests Bumped version of all dependencies Normalized formatting as recommended by SonarLint Fixed Typos, and re-worded a lot of text to normalize verbiage Fixed BOOLEAN_TRUE_RETURN.html having the wrong ID Cleaned up formatting inconsistencies Built/Tested/Verified against SonarQube 8.9.10 (build 61524) Ran SonarLint and resolved all issues not marked TODO --- .gitignore | 3 + README.md | 2 +- pom.xml | 688 ++++++------ .../MutationAnalysisPlugin.java | 123 ++- .../plugins/mutationanalysis/Streams.java | 20 +- .../metrics/MetricDefinitionException.java | 7 +- .../metrics/MutationAnalysisMetrics.java | 9 +- .../metrics/MutationDensityComputer.java | 87 +- .../metrics/MutationMetrics.java | 470 +++++---- .../metrics/MutationScoreComputer.java | 118 +-- .../metrics/QuantitativeMeasureComputer.java | 64 +- .../metrics/ResourceMutationMetrics.java | 21 +- .../metrics/TestKillRatioComputer.java | 28 +- .../metrics/TotalMutationsComputer.java | 34 +- .../mutationanalysis/model/Mutant.java | 67 +- .../model/MutationOperator.java | 220 ++-- .../model/MutationOperators.java | 206 ++-- .../model/TestDescriptor.java | 116 +-- .../report/PitestReportParser.java | 21 +- .../mutationanalysis/report/ReportFinder.java | 19 +- .../mutationanalysis/report/Reports.java | 34 +- .../rules/JavaProfileDefinition.java | 9 +- .../rules/JavaRulesDefinition.java | 5 +- .../rules/KotlinProfileDefinition.kt | 1 + .../rules/KotlinRulesDefinition.kt | 3 +- .../MutationAnalysisProfileDefinition.java | 19 +- .../MutationAnalysisRulesDefinition.java | 50 +- .../sensors/PitestSensor.java | 71 +- .../sensors/ReportCollector.java | 127 +-- .../sensors/ResourceResolver.java | 50 +- .../sensors/RulesProcessor.java | 453 ++++---- .../sensors/SourceMetricsWriter.java | 67 +- .../sensors/TestMetricsWriter.java | 19 +- .../model/ARGUMENT_PROPAGATION.html | 46 +- .../model/BOOLEAN_FALSE_RETURN.html | 4 +- .../model/BOOLEAN_TRUE_RETURN.html | 6 +- .../model/CONDITIONALS_BOUNDARY.html | 82 +- .../model/CONSTRUCTOR_CALLS.html | 48 +- .../model/EMPTY_RETURN_VALUES.html | 22 +- .../model/EXPERIMENTAL_INLINE_CONSTS.html | 132 +-- .../model/EXPERIMENTAL_MEMBER_VARIABLE.html | 143 +-- .../model/EXPERIMENTAL_NAKED_RECEIVER.html | 36 +- .../model/EXPERIMENTAL_REMOVE_INCREMENTS.html | 9 +- .../model/EXPERIMENTAL_REMOVE_SWITCH.html | 8 +- .../model/EXPERIMENTAL_RETURN_VALUES.html | 17 +- .../model/EXPERIMENTAL_SWITCH.html | 12 +- .../mutationanalysis/model/INCREMENTS.html | 33 +- .../mutationanalysis/model/INLINE_CONSTS.html | 173 ++-- .../mutationanalysis/model/INVERT_NEGS.html | 19 +- .../plugins/mutationanalysis/model/MATH.html | 196 ++-- .../model/NEGATE_CONDITIONALS.html | 117 ++- .../model/NON_VOID_METHOD_CALLS.html | 134 +-- .../model/NULL_RETURN_VALUES.html | 16 +- .../model/PRIMITIVE_RETURN_VALS.html | 4 +- .../model/REMOVE_CONDITIONALS.html | 32 +- .../mutationanalysis/model/RETURN_VALS.html | 151 ++- .../model/VOID_METHOD_CALLS.html | 37 +- .../mutationanalysis/model/mutagen-def.xml | 14 +- .../sonar/plugins/mutationanalysis/rules.xml | 126 ++- .../MutationAnalysisPluginTest.java | 98 +- .../plugins/mutationanalysis/StreamsTest.java | 34 +- .../metrics/MutationAnalysisMetricsTest.java | 24 +- .../metrics/MutationDensityComputerTest.java | 38 +- .../metrics/MutationMetricsTest.java | 71 +- .../metrics/MutationScoreComputerTest.java | 65 +- .../QuantitativeMeasureComputerTest.java | 114 +- .../metrics/ResourceMutationMetricsTest.java | 400 ++++--- .../metrics/TestKillRatioComputerTest.java | 71 +- .../metrics/TotalMutationsComputerTest.java | 85 +- .../mutationanalysis/model/BuilderTest.java | 979 +++++++++--------- .../mutationanalysis/model/MutantTest.java | 558 +++++----- .../model/MutationOperatorTest.java | 375 ++++--- .../model/MutationOperatorsTest.java | 148 +-- .../mutationanalysis/model/StateTest.java | 78 +- .../model/TestDescriptorTest.java | 88 +- .../report/PitestReportParserTest.java | 368 +++---- .../report/ReportFinderTest.java | 416 ++++---- .../mutationanalysis/report/ReportsTest.java | 120 +-- .../rules/JavaProfileDefinitionTest.java | 23 +- .../rules/JavaRulesDefinitionTest.java | 76 +- .../rules/KotlinProfileDefinitionTest.kt | 9 +- .../rules/KotlinRulesDefinitionTest.kt | 12 +- .../sensors/PitestSensorTest.java | 941 +++++++++-------- .../sensors/ReportCollectorTest.java | 184 ++-- .../sensors/ResourceResolverTest.java | 128 +-- .../sensors/RulesProcessorTest.java | 809 ++++++++------- .../sensors/SourceMetricsWriterTest.java | 164 ++- .../sensors/TestMetricsWriterTest.java | 105 +- .../testharness/LogRecordingAppender.java | 56 +- .../MeasureComputerTestHarness.java | 23 +- .../testharness/SensorTestHarness.java | 436 ++++---- .../testharness/SystemLocale.java | 38 - .../testharness/SystemLocaleExtension.java | 32 + .../testharness/TestConfiguration.java | 39 +- .../testharness/TestSensorContext.java | 152 +-- .../testharness/TestUtils.java | 272 +++-- 96 files changed, 5940 insertions(+), 6537 deletions(-) delete mode 100644 src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/SystemLocale.java create mode 100644 src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/SystemLocaleExtension.java diff --git a/.gitignore b/.gitignore index ebb5150..28aabb7 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,6 @@ target/ .classpath .project .settings + +# ---- MacOS +.DS_Store diff --git a/README.md b/README.md index d378dee..4f88d6d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Mutation Analysis Plugin for Sonarqube ====================================== -This plugin enables Mutation Analyis of reports generated by [Pitest](http://pitest.org/) in SonarQube +This plugin enables Mutation Analysis of reports generated by [Pitest](http://pitest.org/) in SonarQube Badges ------ diff --git a/pom.xml b/pom.xml index ebaade0..76bb7e6 100644 --- a/pom.xml +++ b/pom.xml @@ -18,357 +18,363 @@ ~ You should have received a copy of the GNU Lesser General Public License ~ along with this program; if not, write to the Free Software Foundation, ~ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - --> - 4.0.0 + --> + + 4.0.0 - mutation-analysis-plugin - ch.devcon5.sonar - sonar-plugin - 1.7 + mutation-analysis-plugin + ch.devcon5.sonar + sonar-plugin + 1.8 - ${project.artifactId} - Sonar Plugin for integrating and visualizing mutation analysis results - 2015 - https://www.devcon5.ch/en/products_and_services/products/sonarqube-mutation-plugin/ + ${project.artifactId} + Sonar Plugin for integrating and visualizing mutation analysis results + 2015 + https://www.devcon5.ch/en/products_and_services/products/sonarqube-mutation-plugin/ - - - Gerald Muecke - gerald.muecke@devcon5.ch - - + + + Gerald Muecke + gerald.muecke@devcon5.ch + + - - DevCon5 GmbH - http://www.devcon5.ch - + + DevCon5 GmbH + http://www.devcon5.ch + - - GitHub - https://github.com/devcon5io/${project.name}/issues - + + GitHub + https://github.com/devcon5io/${project.name}/issues + - - scm:git:git@github.com:devcon5io/${project.name}.git - scm:git:git@github.com:devcon5io/${project.name}.git - https://github.com/devcon5io/${project.name} - + + scm:git:git@github.com:devcon5io/${project.name}.git + scm:git:git@github.com:devcon5io/${project.name}.git + https://github.com/devcon5io/${project.name} + - - - GNU LGPL 3 - http://www.gnu.org/licenses/lgpl.txt - repo - - + + + GNU LGPL 3 + http://www.gnu.org/licenses/lgpl.txt + repo + + - - 8.9.2.46101 - UTF-8 - 1.8 - 1.5.31 - 4.13.2 - 2.0.1 - 1.7.32 - 2.18.0 - 4.7.0 - 0.8.8 - 1.9.5 - - DevCon5 GmbH - Mutation Analysis Plugin - 2015-2022 - info@devcon5.ch - 7.9.5 - + + 8.9.10.61524 + UTF-8 + 11 + 1.8.20 + 5.9.1 + 2.0.1 + 1.7.36 + 2.20.0 + 5.2.0 + 0.8.9 + 1.12.0 + + DevCon5 GmbH + Mutation Analysis Plugin + 2015-2022 + info@devcon5.ch + 7.9.5 + - - - org.sonarsource.sonarqube - sonar-plugin-api - ${sonar.build.version} - provided - - - org.slf4j - slf4j-api - ${slf4j.version} - - - org.jetbrains.kotlin - kotlin-stdlib-jdk8 - ${kotlin.version} - - - org.codehaus.staxmate - staxmate - ${staxmate.version} - - - - org.apache.logging.log4j - log4j-slf4j-impl - ${log4j.version} - test - - - org.apache.logging.log4j - log4j-api - ${log4j.version} - test - - - org.apache.logging.log4j - log4j-core - ${log4j.version} - test - - - - org.sonarsource.sonarqube - sonar-plugin-api-impl - ${sonar.build.version} - test - - - org.sonarsource.sonarqube - sonar-testing-harness - ${sonar.build.version} - test - - - junit - junit - ${junit.version} - test - - - org.jetbrains.kotlin - kotlin-test - ${kotlin.version} - test - - - org.mockito - mockito-core - ${mockito.version} - test - - + + + org.sonarsource.sonarqube + sonar-plugin-api + ${sonar.build.version} + provided + + + org.slf4j + slf4j-api + ${slf4j.version} + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + ${kotlin.version} + + + org.codehaus.staxmate + staxmate + ${staxmate.version} + + + + org.apache.logging.log4j + log4j-slf4j-impl + ${log4j.version} + test + + + org.apache.logging.log4j + log4j-api + ${log4j.version} + test + + + org.apache.logging.log4j + log4j-core + ${log4j.version} + test + + + + org.sonarsource.sonarqube + sonar-plugin-api-impl + ${sonar.build.version} + test + + + org.sonarsource.sonarqube + sonar-testing-harness + ${sonar.build.version} + test + + + org.junit.jupiter + junit-jupiter-engine + ${junit.version} + test + + + org.junit.jupiter + junit-jupiter-params + ${junit.version} + test + + + org.mockito + mockito-core + ${mockito.version} + test + + - - - - org.sonarsource.sonar-packaging-maven-plugin - sonar-packaging-maven-plugin - 1.18.0.372 - true - - mutationanalysis - ch.devcon5.sonar.plugins.mutationanalysis.MutationAnalysisPlugin - Mutation Analysis - ${sonarQubeMinVersion} - - + + + + org.sonarsource.sonar-packaging-maven-plugin + sonar-packaging-maven-plugin + 1.21.0.505 + true + + mutationanalysis + ch.devcon5.sonar.plugins.mutationanalysis.MutationAnalysisPlugin + Mutation Analysis + ${sonarQubeMinVersion} + + - - org.jacoco - jacoco-maven-plugin - ${jacoco.version} - - - prepare-agent - - prepare-agent - - - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.19.1 - - - - - org.jetbrains.kotlin - kotlin-maven-plugin - ${kotlin.version} - - - compile - compile - - compile - - - - src/main/java - src/main/resources - - - - - test-compile - test-compile - - test-compile - - - - src/test/java - src/test/resources - - - - - - 1.8 - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.5.1 - - - - default-compile - none - - - default-testCompile - none - - - java-compile - compile - compile - - - java-test-compile - test-compile - testCompile - - - - ${jdk.min.version} - ${jdk.min.version} - - - - + + org.jacoco + jacoco-maven-plugin + ${jacoco.version} + + + prepare-agent + + prepare-agent + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0 + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + compile + + compile + + + + src/main/java + src/main/resources + + + + + test-compile + test-compile + + test-compile + + + + src/test/java + src/test/resources + + + + + + 1.8 + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + + + default-compile + none + + + default-testCompile + none + + + java-compile + compile + + compile + + + + java-test-compile + test-compile + + testCompile + + + + + ${jdk.min.version} + ${jdk.min.version} + + + + - - - - pit - - - - org.pitest - pitest-maven - ${pitest.version} - - - ch.devcon5.sonar.plugins.mutationanalysis.* - - - ch.devcon5.sonar.plugins.mutationanalysis.* - - - DEFAULTS - CONSTRUCTOR_CALLS - NON_VOID_METHOD_CALLS - INLINE_CONSTS - REMOVE_CONDITIONALS - EXPERIMENTAL_MEMBER_VARIABLE - EXPERIMENTAL_SWITCH - - - false - - - XML - HTML - - 15000 - - - - - mutationCoverage - - verify - - - - - - - - release - - - - org.apache.maven.plugins - maven-source-plugin - - - attach-sources - - jar-no-fork - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - - - attach-javadocs - - jar - - - - - false - - - - - - - - - central - Central Repository - https://repo.maven.apache.org/maven2 - default - - false - - - never - - - - - - central - Central Repository - https://repo.maven.apache.org/maven2 - default - - false - - - + + + + pit + + + + org.pitest + pitest-maven + ${pitest.version} + + + ch.devcon5.sonar.plugins.mutationanalysis.* + + + ch.devcon5.sonar.plugins.mutationanalysis.* + + + DEFAULTS + CONSTRUCTOR_CALLS + NON_VOID_METHOD_CALLS + INLINE_CONSTS + REMOVE_CONDITIONALS + EXPERIMENTAL_MEMBER_VARIABLE + EXPERIMENTAL_SWITCH + + + false + + + XML + HTML + + 15000 + + + + + mutationCoverage + + verify + + + + + + + + release + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + attach-javadocs + + jar + + + + + false + + + + + + + + + central + Central Repository + https://repo.maven.apache.org/maven2 + default + + false + + + never + + + + + + central + Central Repository + https://repo.maven.apache.org/maven2 + default + + false + + + diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/MutationAnalysisPlugin.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/MutationAnalysisPlugin.java index cd6f64a..5b57eaf 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/MutationAnalysisPlugin.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/MutationAnalysisPlugin.java @@ -40,69 +40,68 @@ import org.sonar.api.config.Configuration; /** - * This class is the entry point for the Mutation Analysis Plugin. - * The properties define, which {@link org.sonar.api.config.Configuration} are configurable for the plugin. + * This class is the entry point for the Mutation Analysis Plugin. The properties define, which + * {@link org.sonar.api.config.Configuration} are configurable for the plugin. */ @Properties({ - @Property(key = MutationAnalysisPlugin.PITEST_JAVA_SENSOR_ENABLED, - name = "Active Pitest Java Sensor", - description = "Enables the Kotlin Sensor for PIT. Default is 'true'", - type = PropertyType.BOOLEAN, - defaultValue = "true", - project = true), - @Property(key = MutationAnalysisPlugin.PITEST_KOTLIN_SENSOR_ENABLED, - name = "Active Pitest Kotlin Sensor", - description = "Enables the Kotlin Sensor for PIT. Default is 'true'", - type = PropertyType.BOOLEAN, - defaultValue = "true", - project = true), - @Property(key = MutationAnalysisPlugin.EXPERIMENTAL_FEATURE_ENABLED, - name = "Enable experimental features", - description = "Enables features that are still under development and may cause existing behavior to break", - type = PropertyType.BOOLEAN, - defaultValue = "false", - project = true), - @Property(key = MutationAnalysisPlugin.REPORT_DIRECTORY_KEY, - defaultValue = MutationAnalysisPlugin.REPORT_DIRECTORY_DEF, - name = "Output directory for the PIT reports", - description = "This property is needed when the reports are not located in the default directory (i.e. target/pit-reports)", - project = true), - @Property(key = MutationAnalysisPlugin.PROJECT_ROOT_FOLDER, - name = "Root folder of multi-module projects", - description = "This optional property may be used to explicitly define the root project of a multi-module project in case the " - + "project root can not be determined by the plugin.", - project = true), - @Property(key = MutationAnalysisPlugin.EFFORT_MUTANT_KILL, - defaultValue = MutationAnalysisPlugin.DEFAULT_EFFORT_TO_KILL_MUTANT, - name = "Effort: Kill a mutant", - description = "Effort to kill a single mutant. Values may be hours, i.e. '1h' or minutes '30min'.", - type = PropertyType.STRING, - project = true), - @Property(key = MutationAnalysisPlugin.EFFORT_FACTOR_MISSING_COVERAGE, - defaultValue = "1.0", - name = "Effort Factor: Missing Coverage", - description = "Factor that is multiplied by the minimally additional required mutants to be killed to get required mutation coverage.", - type = PropertyType.FLOAT, - project = true), - @Property(key = MutationAnalysisPlugin.EFFORT_FACTOR_SURVIVED_MUTANT, - defaultValue = "1.0", - name = "Effort Factor: Survived Mutant ", - description = "Factor that is applied to any of the survived mutant rule to calculate effort to fix", - type = PropertyType.FLOAT, - project = true), - @Property(key = MutationAnalysisPlugin.FORCE_MISSING_COVERAGE_TO_ZERO, - name = "Force missing coverage to zero", - description = "If a project has no mutation report, it's coverage is forced to zero. If disabled, no coverage metric is calculated", - type = PropertyType.BOOLEAN, - defaultValue = "false" - //the project=true setting doesn't seem to have an effect on compute engine side - // see https://community.sonarsource.com/t/plugin-development-project-level-settings-have-no-effect-in-ce/1528 - //, project = true - ), - }) + @Property(key = MutationAnalysisPlugin.PITEST_JAVA_SENSOR_ENABLED, + name = "Active Pitest Java Sensor", + description = "Enables the Java Sensor for PIT. Default is 'true'", + type = PropertyType.BOOLEAN, + defaultValue = "true", + project = true), + @Property(key = MutationAnalysisPlugin.PITEST_KOTLIN_SENSOR_ENABLED, + name = "Active Pitest Kotlin Sensor", + description = "Enables the Kotlin Sensor for PIT. Default is 'true'", + type = PropertyType.BOOLEAN, + defaultValue = "true", + project = true), + @Property(key = MutationAnalysisPlugin.EXPERIMENTAL_FEATURE_ENABLED, + name = "Enable experimental features", + description = "Enables features that are still under development and may cause existing behavior to break", + type = PropertyType.BOOLEAN, + defaultValue = "false", + project = true), + @Property(key = MutationAnalysisPlugin.REPORT_DIRECTORY_KEY, + defaultValue = MutationAnalysisPlugin.REPORT_DIRECTORY_DEF, + name = "Output directory for the PIT reports", + description = "This property is needed when the reports are not located in the default directory (i.e. target/pit-reports)", + project = true), + @Property(key = MutationAnalysisPlugin.PROJECT_ROOT_FOLDER, + name = "Root folder of multi-module projects", + description = "This optional property may be used to explicitly define the root project of a multi-module project in case the " + + "project root can not be determined by the plugin.", + project = true), + @Property(key = MutationAnalysisPlugin.EFFORT_MUTANT_KILL, + defaultValue = MutationAnalysisPlugin.DEFAULT_EFFORT_TO_KILL_MUTANT, + name = "Effort: Kill a mutant", + description = "Effort to kill a single mutant. Values may be hours, i.e. '1h' or minutes '30min'.", + type = PropertyType.STRING, + project = true), + @Property(key = MutationAnalysisPlugin.EFFORT_FACTOR_MISSING_COVERAGE, + defaultValue = "1.0", + name = "Effort Factor: Missing Coverage", + description = "Factor that is multiplied by the minimally additional required mutants to be killed to get required mutation coverage.", + type = PropertyType.FLOAT, + project = true), + @Property(key = MutationAnalysisPlugin.EFFORT_FACTOR_SURVIVED_MUTANT, + defaultValue = "1.0", + name = "Effort Factor: Survived Mutant ", + description = "Factor that is applied to any of the survived mutant rule to calculate effort to fix", + type = PropertyType.FLOAT, + project = true), + @Property(key = MutationAnalysisPlugin.FORCE_MISSING_COVERAGE_TO_ZERO, + name = "Force missing coverage to zero", + description = "If a project has no mutation report, it's coverage is forced to zero. If disabled, no coverage metric is calculated", + type = PropertyType.BOOLEAN, + defaultValue = "false" + //the project=true setting doesn't seem to have an effect on compute engine side + // see https://community.sonarsource.com/t/plugin-development-project-level-settings-have-no-effect-in-ce/1528 + //, project = true + ), +}) public final class MutationAnalysisPlugin implements Plugin { - public static final String PITEST_JAVA_SENSOR_ENABLED = "dc5.mutationAnalysis.pitest.java.sensor.enabled"; public static final String PITEST_KOTLIN_SENSOR_ENABLED = "dc5.mutationAnalysis.pitest.kotlin.sensor.enabled"; public static final String EXPERIMENTAL_FEATURE_ENABLED = "dc5.mutationAnalysis.experimentalFeatures.enabled"; @@ -117,13 +116,12 @@ public final class MutationAnalysisPlugin implements Plugin { @Override public void define(final Context context) { - //we add each extension separately although there is a method (addExtensions) accepting varargs that would handle this in one call. //Background: varargs produce bytecode artifacts that cause mutations that can not be killed (i.e. reordering of varargs) //Because of this plugin deals with mutations, it's also kind of an example how to run mutation testing and what impact on code bases it might have. //This option was chosen because it allows to kill all mutants in this class + reduces the mutation density. //The equivalent option would be to use the varargs, which would have the same LoC but just a single statement and lots of re-ordering mutations, which - //increased the mutation density + reduces the mutation coverage through unkillable mutations. + //increased the mutation density + reduces the mutation coverage through un-killable mutations. context.addExtension(PitestReportParser.class); context.addExtension(ReportFinder.class); context.addExtension(JavaRulesDefinition.class); @@ -139,7 +137,8 @@ public void define(final Context context) { context.addExtension(QuantitativeMeasureComputer.class); } - public static boolean isExperimentalFeaturesEnabled(Configuration config){ + public static boolean isExperimentalFeaturesEnabled(Configuration config) { return config.getBoolean(EXPERIMENTAL_FEATURE_ENABLED).orElse(false); } + } diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/Streams.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/Streams.java index 19f9f16..90d0b6b 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/Streams.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/Streams.java @@ -25,14 +25,16 @@ public final class Streams { - private Streams() { - //to prevent instantiation of this class - } + private Streams() { + //to prevent instantiation of this class + } + + public static Stream sequentialStream(Iterable elements) { + return StreamSupport.stream(elements.spliterator(), false); + } + + public static Stream parallelStream(Iterable elements) { + return StreamSupport.stream(elements.spliterator(), true); + } - public static Stream sequentialStream(Iterable elements){ - return StreamSupport.stream(elements.spliterator(), false); - } - public static Stream parallelStream(Iterable elements){ - return StreamSupport.stream(elements.spliterator(), true); - } } diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MetricDefinitionException.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MetricDefinitionException.java index afcd381..e6d09be 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MetricDefinitionException.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MetricDefinitionException.java @@ -25,7 +25,8 @@ */ public class MetricDefinitionException extends RuntimeException { - public MetricDefinitionException(final String s, final Exception e) { - super(s, e); - } + public MetricDefinitionException(final String s, final Exception e) { + super(s, e); + } + } diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationAnalysisMetrics.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationAnalysisMetrics.java index af2493b..337a6f0 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationAnalysisMetrics.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationAnalysisMetrics.java @@ -22,7 +22,6 @@ import java.util.Collections; import java.util.List; - import org.sonar.api.measures.Metric; import org.sonar.api.measures.Metrics; @@ -31,9 +30,9 @@ */ public class MutationAnalysisMetrics implements Metrics { - @Override - public List getMetrics() { + @Override + public List getMetrics() { + return Collections.unmodifiableList(MutationMetrics.getSensorMetrics()); + } - return Collections.unmodifiableList(MutationMetrics.getSensorMetrics()); - } } diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationDensityComputer.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationDensityComputer.java index 59f63c8..f1fecd0 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationDensityComputer.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationDensityComputer.java @@ -34,50 +34,47 @@ */ public class MutationDensityComputer implements MeasureComputer { - private static final Logger LOG = getLogger(MutationDensityComputer.class); + private static final Logger LOG = getLogger(MutationDensityComputer.class); + + private final Configuration config; + + public MutationDensityComputer(final Configuration config) { + this.config = config; + } + + @Override + public MeasureComputerDefinition define(final MeasureComputerDefinitionContext defContext) { + return defContext.newDefinitionBuilder() + .setInputMetrics(MutationMetrics.MUTATIONS_TOTAL.key(), CoreMetrics.LINES_TO_COVER_KEY) + .setOutputMetrics(MutationMetrics.MUTATIONS_DENSITY.key()) + .build(); + } + + @Override + public void compute(final MeasureComputerContext context) { + if (!MutationAnalysisPlugin.isExperimentalFeaturesEnabled(config)) { + LOG.info("Experimental Features disabled"); + return; + } + + LOG.info("Computing Mutation Density for {}", context.getComponent()); + final Measure lines = context.getMeasure(CoreMetrics.LINES_TO_COVER_KEY); + final Measure mutations = context.getMeasure(MutationMetrics.MUTATIONS_TOTAL.key()); + if (mutations == null) { + return; + } + + final double density; + if (lines == null) { + density = 0d; + } else { + density = 100.0d * ((double) mutations.getIntValue() / (double) lines.getIntValue()); + } + + if (!Double.isNaN(density)) { + LOG.info("Computed Mutation Density of {} for {}", density, context.getComponent()); + context.addMeasure(MutationMetrics.MUTATIONS_DENSITY.key(), density); + } + } - private Configuration config; - - public MutationDensityComputer(final Configuration config) { - - this.config = config; - } - - @Override - public MeasureComputerDefinition define(final MeasureComputerDefinitionContext defContext) { - - return defContext.newDefinitionBuilder() - .setInputMetrics(MutationMetrics.MUTATIONS_TOTAL.key(), CoreMetrics.LINES_TO_COVER_KEY) - .setOutputMetrics(MutationMetrics.MUTATIONS_DENSITY.key()) - .build(); - } - - @Override - public void compute(final MeasureComputerContext context) { - - if (!MutationAnalysisPlugin.isExperimentalFeaturesEnabled(config)) { - LOG.info("Experimental Features disabled"); - return; - } - - LOG.info("Computing Mutation Density for {}", context.getComponent()); - final Measure lines = context.getMeasure(CoreMetrics.LINES_TO_COVER_KEY); - final Measure mutations = context.getMeasure(MutationMetrics.MUTATIONS_TOTAL.key()); - - if (mutations == null) { - return; - } - final Double density; - - if (lines == null) { - density = 0d; - } else { - density = 100.0 * ((double) mutations.getIntValue() / (double) lines.getIntValue()); - } - - if(!Double.isNaN(density)){ - LOG.info("Computed Mutation Density of {} for {}", density, context.getComponent()); - context.addMeasure(MutationMetrics.MUTATIONS_DENSITY.key(), density); - } - } } diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationMetrics.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationMetrics.java index 4c8ad22..9a36f43 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationMetrics.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationMetrics.java @@ -25,7 +25,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; - import org.sonar.api.measures.Metric; /** @@ -33,240 +32,237 @@ */ public final class MutationMetrics { - private MutationMetrics(){} - - private static final int DIRECTION_BETTER = 1; - private static final int DIRECTION_WORST = -1; - private static final int DIRECTION_NONE = 0; - - public static final String MUTATION_ANALYSIS_DOMAIN = "Mutation Analysis"; - - public static final String MUTATIONS_DATA_KEY = "dc5_mutationAnalysis_mutations_data"; - public static final Metric MUTATIONS_DATA = new Metric.Builder(MUTATIONS_DATA_KEY, "Mutations Data", Metric.ValueType.DATA) - .setDirection(DIRECTION_NONE) - .setDomain(MUTATION_ANALYSIS_DOMAIN) - .setQualitative(true) - .setDescription("None") - .setHidden(true) - .create(); - - public static final String MUTATIONS_TOTAL_KEY = "dc5_mutationAnalysis_mutations_total"; - public static final Metric MUTATIONS_TOTAL = new Metric.Builder(MUTATIONS_TOTAL_KEY, "Mutations: Total", Metric.ValueType.INT) - .setDirection(DIRECTION_WORST) - .setDomain(MUTATION_ANALYSIS_DOMAIN) - .setDescription("Total number of mutations generated") - .create(); - - public static final String MUTATIONS_TOTAL_PERCENT_KEY = "dc5_mutationAnalysis_mutations_hotspots"; - public static final Metric MUTATIONS_TOTAL_PERCENT = new Metric.Builder(MUTATIONS_TOTAL_PERCENT_KEY, "Mutations: Total %", Metric.ValueType.PERCENT) - .setDirection(DIRECTION_WORST) - .setDomain(MUTATION_ANALYSIS_DOMAIN) - .setQualitative(true) - .setDescription("Percent of total mutations per class") - .setBestValue(0.) - .setWorstValue(100.) - .setDecimalScale(1) - .create(); - - public static final String MUTATIONS_NO_COVERAGE_KEY = "dc5_mutationAnalysis_mutations_noCoverage"; - public static final Metric MUTATIONS_NO_COVERAGE = new Metric.Builder(MUTATIONS_NO_COVERAGE_KEY, "Alive: Not Covered", Metric.ValueType.INT) - .setDirection(DIRECTION_WORST) - .setDomain(MUTATION_ANALYSIS_DOMAIN) - .setDescription("Number of mutations non covered by any test.") - .create(); - - public static final String MUTATIONS_DETECTED_KEY = "dc5_mutationAnalysis_mutations_detected"; - public static final Metric MUTATIONS_DETECTED=new Metric.Builder(MUTATIONS_DETECTED_KEY, "Killed: Total", Metric.ValueType.INT) - .setDirection(DIRECTION_WORST) - .setDomain(MUTATION_ANALYSIS_DOMAIN) - .setDescription("The number of all mutations that are either
    " - + "
  • killed by a test
  • " - + "
  • killed by a timeout
  • " - + "
  • killed by a memory error
  • " - + "
") - .create(); - - public static final String MUTATIONS_ALIVE_KEY = "dc5_mutationAnalysis_mutations_alive"; - public static final Metric MUTATIONS_ALIVE=new Metric.Builder(MUTATIONS_ALIVE_KEY, "Alive: Total", Metric.ValueType.INT) - .setDirection(DIRECTION_WORST) - .setDomain(MUTATION_ANALYSIS_DOMAIN) - .setDescription("Number of mutations still alive this includes covered and non-covered mutations.") - .create(); - - public static final String MUTATIONS_KILLED_KEY = "dc5_mutationAnalysis_mutations_killed"; - public static final Metric MUTATIONS_KILLED=new Metric.Builder(MUTATIONS_KILLED_KEY, "Killed: by Tests", Metric.ValueType.INT) - .setDirection(DIRECTION_WORST) - .setDomain(MUTATION_ANALYSIS_DOMAIN) - .setDescription("Number of mutations killed by tests") - .create(); - - public static final String MUTATIONS_UNKNOWN_KEY = "dc5_mutationAnalysis_mutations_unknown"; - public static final Metric MUTATIONS_UNKNOWN=new Metric.Builder(MUTATIONS_UNKNOWN_KEY, "Unknown Status", Metric.ValueType.INT) - .setDirection(DIRECTION_WORST) - .setDomain(MUTATION_ANALYSIS_DOMAIN) - .setDescription("Number of mutations with unknown status.") - .create(); - - public static final String MUTATIONS_COVERAGE_KEY = "dc5_mutationAnalysis_mutations_coverage"; - public static final Metric MUTATIONS_COVERAGE=new Metric.Builder(MUTATIONS_COVERAGE_KEY, "Mutation: Coverage", Metric.ValueType.PERCENT) - .setDirection(DIRECTION_BETTER) - .setDomain(MUTATION_ANALYSIS_DOMAIN) - .setQualitative(true) - .setDescription("Mutations coverage percentage") - .setBestValue(100.) - .setWorstValue(0.) - .setDecimalScale(1) - .create(); - - public static final String MUTATIONS_TEST_STRENGTH_KEY = "dc5_mutationAnalysis_mutations_test_strength"; - public static final Metric MUTATIONS_TEST_STRENGTH = new Metric.Builder(MUTATIONS_TEST_STRENGTH_KEY, "Test Strength", Metric.ValueType.PERCENT) - .setDirection(DIRECTION_BETTER) - .setDomain(MUTATION_ANALYSIS_DOMAIN) - .setQualitative(true) - .setDescription("Test strength percentage") - .setBestValue(100.) - .setWorstValue(0.) - .setDecimalScale(1) - .create(); - - public static final String MUTATIONS_DENSITY_KEY = "dc5_mutationAnalysis_mutations_density"; - public static final Metric MUTATIONS_DENSITY=new Metric.Builder(MUTATIONS_DENSITY_KEY, "Mutation: Density", Metric.ValueType.PERCENT) - .setDirection(DIRECTION_WORST) - .setDomain(MUTATION_ANALYSIS_DOMAIN) - .setQualitative(true) - .setDescription("Mutations per Statement") - .setBestValue(100.) - .setWorstValue(0.) - .setDecimalScale(1) - .create(); - - public static final String MUTATIONS_ALIVE_PERCENT_KEY = "dc5_mutationAnalysis_mutations_survivor_hotspots"; - public static final Metric MUTATIONS_ALIVE_PERCENT=new Metric.Builder(MUTATIONS_ALIVE_PERCENT_KEY, "Alive: Total %", Metric.ValueType.PERCENT) - .setDirection(DIRECTION_WORST) - .setDomain(MUTATION_ANALYSIS_DOMAIN) - .setQualitative(true) - .setDescription("Distribution Percentage of all survivor") - .setBestValue(0.) - .setWorstValue(100.) - .setDecimalScale(1) - .create(); - - public static final String MUTATIONS_TIMED_OUT_KEY = "dc5_mutationAnalysis_mutations_timedOut"; - public static final Metric MUTATIONS_TIMED_OUT=new Metric.Builder(MUTATIONS_TIMED_OUT_KEY, "Killed: by Timeout", Metric.ValueType.INT) - .setDirection(DIRECTION_WORST) - .setDomain(MUTATION_ANALYSIS_DOMAIN) - .setDescription("Number of mutations detected by time outs.") - .create(); - - public static final String MUTATIONS_MEMORY_ERROR_KEY = "dc5_mutationAnalysis_mutations_memoryError"; - public static final Metric MUTATIONS_MEMORY_ERROR=new Metric.Builder(MUTATIONS_MEMORY_ERROR_KEY, "Killed: by Memory Error", Metric.ValueType.INT) - .setDirection(DIRECTION_WORST) - .setDomain(MUTATION_ANALYSIS_DOMAIN) - .setDescription("Number of mutations detected by memory errors.") - .create(); - - public static final String MUTATIONS_SURVIVED_KEY = "dc5_mutationAnalysis_mutations_survived"; - public static final Metric MUTATIONS_SURVIVED=new Metric.Builder(MUTATIONS_SURVIVED_KEY, "Alive: Survivors", Metric.ValueType.INT) - .setDirection(DIRECTION_WORST) - .setDomain(MUTATION_ANALYSIS_DOMAIN) - .setDescription("Number of mutations survived.") - .create(); - - public static final String TEST_KILLS_KEY = "dc5_mutationAnalysis_mutations_testkills"; - public static final Metric TEST_KILLS=new Metric.Builder(TEST_KILLS_KEY, "Test: Kills", Metric.ValueType.INT) - .setDirection(DIRECTION_WORST) - .setDomain(MUTATION_ANALYSIS_DOMAIN) - .setDescription("Kills per Test") - .create(); - - public static final String TEST_TOTAL_EXECUTED_KEY = "dc5_mutationAnalysis_mutations_tests_executed"; - public static final Metric TEST_TOTAL_EXECUTED=new Metric.Builder(TEST_TOTAL_EXECUTED_KEY, "Test: Executions", Metric.ValueType.INT) - .setDirection(DIRECTION_BETTER) - .setDomain(MUTATION_ANALYSIS_DOMAIN) - .setDescription("Number of Tests executed") - .create(); - - public static final String TEST_KILL_RATIO_KEY = "dc5_mutationAnalysis_mutations_testkill_ratio"; - public static final Metric TEST_KILL_RATIO=new Metric.Builder(TEST_KILL_RATIO_KEY, "Test: Kill Ratio", Metric.ValueType.PERCENT) - .setDirection(DIRECTION_WORST) - .setDomain(MUTATION_ANALYSIS_DOMAIN) - .setQualitative(true) - .setDescription("Kills per Test") - .setBestValue(100.) - .setWorstValue(0.) - .setDecimalScale(1) - .create(); - - public static final String UTILITY_GLOBAL_MUTATIONS_KEY = "dc5_mutationAnalysis_mutations_global"; - public static final Metric UTILITY_GLOBAL_MUTATIONS=new Metric.Builder(UTILITY_GLOBAL_MUTATIONS_KEY, "Utility: Total Mutations Global", Metric - .ValueType - .INT) - .setDirection(DIRECTION_BETTER) - .setDomain(MUTATION_ANALYSIS_DOMAIN) - .setDescription("Utility measure for computation to keep track of total number of mutations on each resource") - .setHidden(true) - .create(); - - public static final String UTILITY_GLOBAL_ALIVE_KEY = "dc5_mutationAnalysis_survivors_global"; - public static final Metric UTILITY_GLOBAL_ALIVE=new Metric.Builder(UTILITY_GLOBAL_ALIVE_KEY, "Utility: Total Survivors Global", Metric.ValueType.INT) - .setDirection(DIRECTION_BETTER) - .setDomain(MUTATION_ANALYSIS_DOMAIN) - .setDescription("Utility measure for computation") - .setHidden(true) - .create(); - - private static final List> QUANTITATIVE_METRICS = Collections.unmodifiableList(Arrays.asList( - MUTATIONS_TOTAL, - MUTATIONS_NO_COVERAGE, - MUTATIONS_DETECTED, - MUTATIONS_ALIVE, - MUTATIONS_KILLED, - MUTATIONS_UNKNOWN, - MUTATIONS_TIMED_OUT, - MUTATIONS_MEMORY_ERROR, - MUTATIONS_SURVIVED, - TEST_KILLS, - TEST_TOTAL_EXECUTED, - UTILITY_GLOBAL_MUTATIONS, - UTILITY_GLOBAL_ALIVE - )); - - private static final List> QUALITATIVE_METRICS = Collections.unmodifiableList(Arrays.asList( - MUTATIONS_DATA, - MUTATIONS_TOTAL_PERCENT, - MUTATIONS_COVERAGE, - MUTATIONS_TEST_STRENGTH, - MUTATIONS_DENSITY, - MUTATIONS_ALIVE_PERCENT, - TEST_KILL_RATIO - )); - - private static final List> SENSOR_METRICS; - - static { - final List> metrics = new ArrayList<>(QUALITATIVE_METRICS.size() + QUANTITATIVE_METRICS.size()); - metrics.addAll(QUALITATIVE_METRICS); - metrics.addAll(QUANTITATIVE_METRICS); - SENSOR_METRICS = Collections.unmodifiableList(metrics); - } - - /** - * Returns the pitest quantitative metrics list. - * - * @return The pitest quantitative metrics list. - */ - public static List> getQuantitativeMetrics() { - - return QUANTITATIVE_METRICS; - } - - /** - * Returns the all metrics the pitest sensor provides. - * - * @return The pitest sensor metrics list. - */ - public static List> getSensorMetrics() { - - return SENSOR_METRICS; - } + private MutationMetrics() {} + + private static final int DIRECTION_BETTER = 1; + private static final int DIRECTION_WORST = -1; + private static final int DIRECTION_NONE = 0; + + public static final String MUTATION_ANALYSIS_DOMAIN = "Mutation Analysis"; + + public static final String MUTATIONS_DATA_KEY = "dc5_mutationAnalysis_mutations_data"; + public static final Metric MUTATIONS_DATA = new Metric.Builder(MUTATIONS_DATA_KEY, "Mutations Data", Metric.ValueType.DATA) + .setDirection(DIRECTION_NONE) + .setDomain(MUTATION_ANALYSIS_DOMAIN) + .setQualitative(true) + .setDescription("None") + .setHidden(true) + .create(); + + public static final String MUTATIONS_TOTAL_KEY = "dc5_mutationAnalysis_mutations_total"; + public static final Metric MUTATIONS_TOTAL = new Metric.Builder(MUTATIONS_TOTAL_KEY, "Mutations: Total", Metric.ValueType.INT) + .setDirection(DIRECTION_WORST) + .setDomain(MUTATION_ANALYSIS_DOMAIN) + .setDescription("Total number of mutations generated") + .create(); + + public static final String MUTATIONS_TOTAL_PERCENT_KEY = "dc5_mutationAnalysis_mutations_hotspots"; + public static final Metric MUTATIONS_TOTAL_PERCENT = new Metric.Builder(MUTATIONS_TOTAL_PERCENT_KEY, "Mutations: Total %", Metric.ValueType.PERCENT) + .setDirection(DIRECTION_WORST) + .setDomain(MUTATION_ANALYSIS_DOMAIN) + .setQualitative(true) + .setDescription("Percent of total mutations per class") + .setBestValue(0.) + .setWorstValue(100.) + .setDecimalScale(1) + .create(); + + public static final String MUTATIONS_NO_COVERAGE_KEY = "dc5_mutationAnalysis_mutations_noCoverage"; + public static final Metric MUTATIONS_NO_COVERAGE = new Metric.Builder(MUTATIONS_NO_COVERAGE_KEY, "Alive: Not Covered", Metric.ValueType.INT) + .setDirection(DIRECTION_WORST) + .setDomain(MUTATION_ANALYSIS_DOMAIN) + .setDescription("Number of mutations not covered by any test.") + .create(); + + public static final String MUTATIONS_DETECTED_KEY = "dc5_mutationAnalysis_mutations_detected"; + public static final Metric MUTATIONS_DETECTED = new Metric.Builder(MUTATIONS_DETECTED_KEY, "Killed: Total", Metric.ValueType.INT) + .setDirection(DIRECTION_WORST) + .setDomain(MUTATION_ANALYSIS_DOMAIN) + .setDescription("The number of all mutations that are either
    " + + "
  • killed by a test
  • " + + "
  • killed by a timeout
  • " + + "
  • killed by a memory error
  • " + + "
") + .create(); + + public static final String MUTATIONS_ALIVE_KEY = "dc5_mutationAnalysis_mutations_alive"; + public static final Metric MUTATIONS_ALIVE = new Metric.Builder(MUTATIONS_ALIVE_KEY, "Alive: Total", Metric.ValueType.INT) + .setDirection(DIRECTION_WORST) + .setDomain(MUTATION_ANALYSIS_DOMAIN) + .setDescription("Number of mutations still alive, this includes covered and non-covered mutations.") + .create(); + + public static final String MUTATIONS_KILLED_KEY = "dc5_mutationAnalysis_mutations_killed"; + public static final Metric MUTATIONS_KILLED = new Metric.Builder(MUTATIONS_KILLED_KEY, "Killed: by Tests", Metric.ValueType.INT) + .setDirection(DIRECTION_WORST) + .setDomain(MUTATION_ANALYSIS_DOMAIN) + .setDescription("Number of mutations killed by tests") + .create(); + + public static final String MUTATIONS_UNKNOWN_KEY = "dc5_mutationAnalysis_mutations_unknown"; + public static final Metric MUTATIONS_UNKNOWN = new Metric.Builder(MUTATIONS_UNKNOWN_KEY, "Unknown Status", Metric.ValueType.INT) + .setDirection(DIRECTION_WORST) + .setDomain(MUTATION_ANALYSIS_DOMAIN) + .setDescription("Number of mutations with unknown status.") + .create(); + + public static final String MUTATIONS_COVERAGE_KEY = "dc5_mutationAnalysis_mutations_coverage"; + public static final Metric MUTATIONS_COVERAGE = new Metric.Builder(MUTATIONS_COVERAGE_KEY, "Mutation: Coverage", Metric.ValueType.PERCENT) + .setDirection(DIRECTION_BETTER) + .setDomain(MUTATION_ANALYSIS_DOMAIN) + .setQualitative(true) + .setDescription("Mutations coverage percentage") + .setBestValue(100.) + .setWorstValue(0.) + .setDecimalScale(1) + .create(); + + public static final String MUTATIONS_TEST_STRENGTH_KEY = "dc5_mutationAnalysis_mutations_test_strength"; + public static final Metric MUTATIONS_TEST_STRENGTH = new Metric.Builder(MUTATIONS_TEST_STRENGTH_KEY, "Test Strength", Metric.ValueType.PERCENT) + .setDirection(DIRECTION_BETTER) + .setDomain(MUTATION_ANALYSIS_DOMAIN) + .setQualitative(true) + .setDescription("Test strength percentage") + .setBestValue(100.) + .setWorstValue(0.) + .setDecimalScale(1) + .create(); + + public static final String MUTATIONS_DENSITY_KEY = "dc5_mutationAnalysis_mutations_density"; + public static final Metric MUTATIONS_DENSITY = new Metric.Builder(MUTATIONS_DENSITY_KEY, "Mutation: Density", Metric.ValueType.PERCENT) + .setDirection(DIRECTION_WORST) + .setDomain(MUTATION_ANALYSIS_DOMAIN) + .setQualitative(true) + .setDescription("Mutations per Statement") + .setBestValue(100.) + .setWorstValue(0.) + .setDecimalScale(1) + .create(); + + public static final String MUTATIONS_ALIVE_PERCENT_KEY = "dc5_mutationAnalysis_mutations_survivor_hotspots"; + public static final Metric MUTATIONS_ALIVE_PERCENT = new Metric.Builder(MUTATIONS_ALIVE_PERCENT_KEY, "Alive: Total %", Metric.ValueType.PERCENT) + .setDirection(DIRECTION_WORST) + .setDomain(MUTATION_ANALYSIS_DOMAIN) + .setQualitative(true) + .setDescription("Distribution Percentage of all survivors") + .setBestValue(0.) + .setWorstValue(100.) + .setDecimalScale(1) + .create(); + + public static final String MUTATIONS_TIMED_OUT_KEY = "dc5_mutationAnalysis_mutations_timedOut"; + public static final Metric MUTATIONS_TIMED_OUT = new Metric.Builder(MUTATIONS_TIMED_OUT_KEY, "Killed: by Timeout", Metric.ValueType.INT) + .setDirection(DIRECTION_WORST) + .setDomain(MUTATION_ANALYSIS_DOMAIN) + .setDescription("Number of mutations detected by time outs.") + .create(); + + public static final String MUTATIONS_MEMORY_ERROR_KEY = "dc5_mutationAnalysis_mutations_memoryError"; + public static final Metric MUTATIONS_MEMORY_ERROR = new Metric.Builder(MUTATIONS_MEMORY_ERROR_KEY, "Killed: by Memory Error", Metric.ValueType.INT) + .setDirection(DIRECTION_WORST) + .setDomain(MUTATION_ANALYSIS_DOMAIN) + .setDescription("Number of mutations detected by memory errors.") + .create(); + + public static final String MUTATIONS_SURVIVED_KEY = "dc5_mutationAnalysis_mutations_survived"; + public static final Metric MUTATIONS_SURVIVED = new Metric.Builder(MUTATIONS_SURVIVED_KEY, "Alive: Survivors", Metric.ValueType.INT) + .setDirection(DIRECTION_WORST) + .setDomain(MUTATION_ANALYSIS_DOMAIN) + .setDescription("Number of mutations survived.") + .create(); + + public static final String TEST_KILLS_KEY = "dc5_mutationAnalysis_mutations_testkills"; + public static final Metric TEST_KILLS = new Metric.Builder(TEST_KILLS_KEY, "Test: Kills", Metric.ValueType.INT) + .setDirection(DIRECTION_WORST) + .setDomain(MUTATION_ANALYSIS_DOMAIN) + .setDescription("Kills per Test") + .create(); + + public static final String TEST_TOTAL_EXECUTED_KEY = "dc5_mutationAnalysis_mutations_tests_executed"; + public static final Metric TEST_TOTAL_EXECUTED = new Metric.Builder(TEST_TOTAL_EXECUTED_KEY, "Test: Executions", Metric.ValueType.INT) + .setDirection(DIRECTION_BETTER) + .setDomain(MUTATION_ANALYSIS_DOMAIN) + .setDescription("Number of Tests executed") + .create(); + + public static final String TEST_KILL_RATIO_KEY = "dc5_mutationAnalysis_mutations_testkill_ratio"; + public static final Metric TEST_KILL_RATIO = new Metric.Builder(TEST_KILL_RATIO_KEY, "Test: Kill Ratio", Metric.ValueType.PERCENT) + .setDirection(DIRECTION_WORST) + .setDomain(MUTATION_ANALYSIS_DOMAIN) + .setQualitative(true) + .setDescription("Kills per Test") + .setBestValue(100.) + .setWorstValue(0.) + .setDecimalScale(1) + .create(); + + public static final String UTILITY_GLOBAL_MUTATIONS_KEY = "dc5_mutationAnalysis_mutations_global"; + public static final Metric UTILITY_GLOBAL_MUTATIONS = new Metric.Builder(UTILITY_GLOBAL_MUTATIONS_KEY, "Utility: Total Mutations Global", Metric.ValueType.INT) + .setDirection(DIRECTION_BETTER) + .setDomain(MUTATION_ANALYSIS_DOMAIN) + .setDescription("Utility measure for computation to keep track of total number of mutations on each resource") + .setHidden(true) + .create(); + + public static final String UTILITY_GLOBAL_ALIVE_KEY = "dc5_mutationAnalysis_survivors_global"; + public static final Metric UTILITY_GLOBAL_ALIVE = new Metric.Builder(UTILITY_GLOBAL_ALIVE_KEY, "Utility: Total Survivors Global", Metric.ValueType.INT) + .setDirection(DIRECTION_BETTER) + .setDomain(MUTATION_ANALYSIS_DOMAIN) + .setDescription("Utility measure for computation") + .setHidden(true) + .create(); + + private static final List> QUANTITATIVE_METRICS = Collections.unmodifiableList(Arrays.asList( + MUTATIONS_TOTAL, + MUTATIONS_NO_COVERAGE, + MUTATIONS_DETECTED, + MUTATIONS_ALIVE, + MUTATIONS_KILLED, + MUTATIONS_UNKNOWN, + MUTATIONS_TIMED_OUT, + MUTATIONS_MEMORY_ERROR, + MUTATIONS_SURVIVED, + TEST_KILLS, + TEST_TOTAL_EXECUTED, + UTILITY_GLOBAL_MUTATIONS, + UTILITY_GLOBAL_ALIVE + )); + + private static final List> QUALITATIVE_METRICS = Collections.unmodifiableList(Arrays.asList( + MUTATIONS_DATA, + MUTATIONS_TOTAL_PERCENT, + MUTATIONS_COVERAGE, + MUTATIONS_TEST_STRENGTH, + MUTATIONS_DENSITY, + MUTATIONS_ALIVE_PERCENT, + TEST_KILL_RATIO + )); + + private static final List> SENSOR_METRICS; + static { + final List> metrics = new ArrayList<>( + QUALITATIVE_METRICS.size() + QUANTITATIVE_METRICS.size()); + metrics.addAll(QUALITATIVE_METRICS); + metrics.addAll(QUANTITATIVE_METRICS); + SENSOR_METRICS = Collections.unmodifiableList(metrics); + } + + /** + * Returns the pitest quantitative metrics list. + * + * @return The pitest quantitative metrics list. + */ + public static List> getQuantitativeMetrics() { + return QUANTITATIVE_METRICS; + } + + /** + * Returns the all metrics the pitest sensor provides. + * + * @return The pitest sensor metrics list. + */ + public static List> getSensorMetrics() { + return SENSOR_METRICS; + } + } diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationScoreComputer.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationScoreComputer.java index 860a222..37afcb1 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationScoreComputer.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationScoreComputer.java @@ -24,7 +24,6 @@ import static org.slf4j.LoggerFactory.getLogger; import java.util.Optional; - import org.slf4j.Logger; import org.sonar.api.batch.measure.Metric; import org.sonar.api.ce.measure.Measure; @@ -36,69 +35,64 @@ */ public class MutationScoreComputer implements MeasureComputer { - private static final Logger LOG = getLogger(MutationScoreComputer.class); - - private Configuration config; - - public MutationScoreComputer(final Configuration config) { - - this.config = config; - } - - @Override - public MeasureComputerDefinition define(final MeasureComputerDefinitionContext defContext) { - - return defContext.newDefinitionBuilder() - .setInputMetrics(MutationMetrics.MUTATIONS_DETECTED.key(), MutationMetrics.MUTATIONS_TOTAL.key(), MutationMetrics.MUTATIONS_SURVIVED.key()) - .setOutputMetrics(MutationMetrics.MUTATIONS_COVERAGE.key(), MutationMetrics.MUTATIONS_TEST_STRENGTH.key()) - .build(); - } - - @Override - public void compute(final MeasureComputerContext context) { - - LOG.info("Computing Mutation Coverage for {}", context.getComponent()); - final Measure mutationsTotalMeasure = context.getMeasure(MutationMetrics.MUTATIONS_TOTAL.key()); - if(mutationsTotalMeasure != null) { - final int mutationsTotal = mutationsTotalMeasure.getIntValue(); - if (mutationsTotal > 0) { - final int detectedMutants = getMetric(context, MutationMetrics.MUTATIONS_DETECTED); - final int survivedMutants = getMetric(context, MutationMetrics.MUTATIONS_SURVIVED); - computeMutationsCoverage(context, detectedMutants, mutationsTotal); - computeTestStrength(context, survivedMutants, detectedMutants); - } else { - //modules with no mutants (0 total) are always 100% covered (0 of 0 is 100%, right?) - context.addMeasure(MutationMetrics.MUTATIONS_COVERAGE.key(), 100.0); - context.addMeasure(MutationMetrics.MUTATIONS_TEST_STRENGTH.key(), 100.0); - } - } else if(config.getBoolean(FORCE_MISSING_COVERAGE_TO_ZERO).orElse(Boolean.FALSE)){ - context.addMeasure(MutationMetrics.MUTATIONS_COVERAGE.key(), 0.0); - context.addMeasure(MutationMetrics.MUTATIONS_TEST_STRENGTH.key(), 0.0); - } + private static final Logger LOG = getLogger(MutationScoreComputer.class); + + private final Configuration config; + + public MutationScoreComputer(final Configuration config) { + this.config = config; + } + + @Override + public MeasureComputerDefinition define(final MeasureComputerDefinitionContext defContext) { + return defContext.newDefinitionBuilder() + .setInputMetrics(MutationMetrics.MUTATIONS_DETECTED.key(), MutationMetrics.MUTATIONS_TOTAL.key(), MutationMetrics.MUTATIONS_SURVIVED.key()) + .setOutputMetrics(MutationMetrics.MUTATIONS_COVERAGE.key(), MutationMetrics.MUTATIONS_TEST_STRENGTH.key()) + .build(); + } + + @Override + public void compute(final MeasureComputerContext context) { + LOG.info("Computing Mutation Coverage for {}", context.getComponent()); + final Measure mutationsTotalMeasure = context.getMeasure(MutationMetrics.MUTATIONS_TOTAL.key()); + if (mutationsTotalMeasure != null) { + final int mutationsTotal = mutationsTotalMeasure.getIntValue(); + if (mutationsTotal > 0) { + final int detectedMutants = getMetric(context, MutationMetrics.MUTATIONS_DETECTED); + final int survivedMutants = getMetric(context, MutationMetrics.MUTATIONS_SURVIVED); + computeMutationsCoverage(context, detectedMutants, mutationsTotal); + computeTestStrength(context, survivedMutants, detectedMutants); + } else { + // modules with no mutants (0 total) are always 100% covered (0 of 0 is 100%, right?) + context.addMeasure(MutationMetrics.MUTATIONS_COVERAGE.key(), 100.0); + context.addMeasure(MutationMetrics.MUTATIONS_TEST_STRENGTH.key(), 100.0); + } + } else if (config.getBoolean(FORCE_MISSING_COVERAGE_TO_ZERO).orElse(Boolean.FALSE)) { + context.addMeasure(MutationMetrics.MUTATIONS_COVERAGE.key(), 0.0); + context.addMeasure(MutationMetrics.MUTATIONS_TEST_STRENGTH.key(), 0.0); } - - private void computeMutationsCoverage(final MeasureComputerContext context, final int coveredMutants, final int totalMutants) { - - final double coverage = 100.0 * coveredMutants / totalMutants; - LOG.info("Computed Mutation Coverage of {}% for {}", coverage, context.getComponent()); - context.addMeasure(MutationMetrics.MUTATIONS_COVERAGE.key(), coverage); + } + + private void computeMutationsCoverage(final MeasureComputerContext context, final int coveredMutants, final int totalMutants) { + final double coverage = 100.0 * coveredMutants / totalMutants; + LOG.info("Computed Mutation Coverage of {}% for {}", coverage, context.getComponent()); + context.addMeasure(MutationMetrics.MUTATIONS_COVERAGE.key(), coverage); + } + + private void computeTestStrength(final MeasureComputerContext context, final int survivedMutants, final int detectedMutants) { + final double testStrength; + int allMutantsQuantity = survivedMutants + detectedMutants; + if (allMutantsQuantity != 0) { + testStrength = 100.0 * detectedMutants / allMutantsQuantity; + } else { + testStrength = 0; } + LOG.info("Computed Test Strength of {}% for {}", testStrength, context.getComponent()); + context.addMeasure(MutationMetrics.MUTATIONS_TEST_STRENGTH.key(), testStrength); + } - private void computeTestStrength(final MeasureComputerContext context, final int survivedMutants, final int detectedMutants) { + private int getMetric(final MeasureComputerContext context, Metric metric) { + return Optional.ofNullable(context.getMeasure(metric.key())).map(Measure::getIntValue).orElse(0); + } - final double testStrength; - - int allMutantsQuantity = survivedMutants + detectedMutants; - if (allMutantsQuantity != 0) { - testStrength = 100.0 * detectedMutants / allMutantsQuantity; - } else { - testStrength = 0; - } - LOG.info("Computed Test Strength of {}% for {}", testStrength, context.getComponent()); - context.addMeasure(MutationMetrics.MUTATIONS_TEST_STRENGTH.key(), testStrength); - } - - private int getMetric(final MeasureComputerContext context, Metric metric){ - return Optional.ofNullable(context.getMeasure(metric.key())).map(Measure::getIntValue).orElse(0); - } } diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/QuantitativeMeasureComputer.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/QuantitativeMeasureComputer.java index 557a69e..36eca05 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/QuantitativeMeasureComputer.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/QuantitativeMeasureComputer.java @@ -38,41 +38,47 @@ public class QuantitativeMeasureComputer implements MeasureComputer { @Override public MeasureComputerDefinition define(final MeasureComputerDefinitionContext defContext) { - - return defContext.newDefinitionBuilder().setOutputMetrics(MutationMetrics.getQuantitativeMetrics() - .stream() - .map(Metric::getKey) - .toArray(String[]::new)).build(); + return defContext.newDefinitionBuilder() + .setOutputMetrics(MutationMetrics.getQuantitativeMetrics() + .stream() + .map(Metric::getKey) + .toArray(String[]::new)) + .build(); } @Override public void compute(final MeasureComputerContext context) { - LOG.info("Computing quantitative mutation metrics for {}", context.getComponent()); MutationMetrics.getQuantitativeMetrics() - .stream() - .filter(m -> !m.isHidden()) //exclude hidden metrics, see below - .map(Metric::getKey) - .filter(metricKey -> context.getMeasure(metricKey) == null) - .forEach(metricKey -> { - int sum = Streams.parallelStream(context.getChildrenMeasures(metricKey)).map(Measure::getIntValue).reduce(0, (s, i) -> s + i); - if (sum > 0) { - LOG.info("Computed {} {} for {}", sum, metricKey, context.getComponent()); - context.addMeasure(metricKey, sum); - } - }); - //hidden utility metric are globally constant for all components + .stream() + .filter(m -> !m.isHidden()) //exclude hidden metrics, see below + .map(Metric::getKey) + .filter(metricKey -> context.getMeasure(metricKey) == null) + .forEach(metricKey -> { + int sum = Streams.parallelStream(context.getChildrenMeasures(metricKey)) + .map(Measure::getIntValue) + .reduce(0, Integer::sum); + if (sum > 0) { + LOG.info("Computed {} {} for {}", sum, metricKey, context.getComponent()); + context.addMeasure(metricKey, sum); + } + }); + + // hidden utility metric are globally constant for all components MutationMetrics.getQuantitativeMetrics() - .stream() - .filter(Metric::isHidden) - .map(Metric::getKey) - .filter(metricKey -> context.getMeasure(metricKey) == null) - .forEach(metricKey -> { - int first = Streams.parallelStream(context.getChildrenMeasures(metricKey)).map(Measure::getIntValue).findFirst().orElse(0); - if (first > 0) { - LOG.info("Computed {} {} for {}", first, metricKey, context.getComponent()); - context.addMeasure(metricKey, first); - } - }); + .stream() + .filter(Metric::isHidden) + .map(Metric::getKey) + .filter(metricKey -> context.getMeasure(metricKey) == null) + .forEach(metricKey -> { + int first = Streams.parallelStream(context.getChildrenMeasures(metricKey)) + .map(Measure::getIntValue) + .findFirst().orElse(0); + if (first > 0) { + LOG.info("Computed {} {} for {}", first, metricKey, context.getComponent()); + context.addMeasure(metricKey, first); + } + }); } + } diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/ResourceMutationMetrics.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/ResourceMutationMetrics.java index b227a26..0c067a9 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/ResourceMutationMetrics.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/ResourceMutationMetrics.java @@ -29,7 +29,6 @@ /** * Metrics for Mutants found in a single resource. It is used to collect mutant information for a specific resource. - * */ public class ResourceMutationMetrics { @@ -53,7 +52,6 @@ public class ResourceMutationMetrics { * the Sonar resource for which mutation information should be collected */ public ResourceMutationMetrics(final InputFile resource) { - this.resource = resource; } @@ -64,7 +62,6 @@ public ResourceMutationMetrics(final InputFile resource) { * the mutant to be added */ public void addMutant(final Mutant mutant) { - mutants.add(mutant); if (mutant.isDetected()) { mutationsDetected++; @@ -94,24 +91,22 @@ public void addMutant(final Mutant mutant) { break; } // update mutation coverage - mutationCoverage = 100.0 * mutationsKilled / mutationsTotal; + mutationCoverage = 100.0d * ((double) mutationsKilled / (double) mutationsTotal); } /** * @return all mutants collected so for the resource */ public Collection getMutants() { - return mutants; } /** - * The total amount of {@link Mutant} added to the metric. + * The total number of {@link Mutant}s added to the metric. * * @return number of all mutations found in the resource */ public int getMutationsTotal() { - return mutationsTotal; } @@ -121,17 +116,15 @@ public int getMutationsTotal() { * @return number of mutations that are not covered */ public int getMutationsNoCoverage() { - return mutationsNoCoverage; } /** * The number of {@link Mutant}s added whose {@link ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant.State} was {@code KILLED} * - * @return number of mutation killed by a test + * @return number of mutations killed by a test */ public int getMutationsKilled() { - return mutationsKilled; } @@ -141,7 +134,6 @@ public int getMutationsKilled() { * @return number of mutations that survived a test */ public int getMutationsSurvived() { - return mutationsSurvived; } @@ -151,7 +143,6 @@ public int getMutationsSurvived() { * @return number of mutations killed by a memory error */ public int getMutationsMemoryError() { - return mutationsMemoryError; } @@ -161,7 +152,6 @@ public int getMutationsMemoryError() { * @return number of mutations killed by a timeout */ public int getMutationsTimedOut() { - return mutationsTimedOut; } @@ -169,7 +159,6 @@ public int getMutationsTimedOut() { * @return number of mutations with unknown status */ public int getMutationsUnknown() { - return mutationsUnknown; } @@ -177,7 +166,6 @@ public int getMutationsUnknown() { * @return the number of mutations detected at all */ public int getMutationsDetected() { - return mutationsDetected; } @@ -185,7 +173,6 @@ public int getMutationsDetected() { * @return the mutation coverage in percent, that is a value between 0.0 and 100.0 */ public double getMutationCoverage() { - return mutationCoverage; } @@ -195,7 +182,6 @@ public double getMutationCoverage() { * a number >= 0 */ public int getNumTestsRun() { - return numTestsRun; } @@ -205,7 +191,6 @@ public int getNumTestsRun() { * @return An {@link InputFile} representing the resource. */ public InputFile getResource() { - return resource; } diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/TestKillRatioComputer.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/TestKillRatioComputer.java index d411afd..ea77e28 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/TestKillRatioComputer.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/TestKillRatioComputer.java @@ -38,22 +38,22 @@ public class TestKillRatioComputer implements MeasureComputer { private static final Logger LOG = getLogger(TestKillRatioComputer.class); - private Configuration config; + private final Configuration config; public TestKillRatioComputer(final Configuration config) { - this.config = config; } @Override public MeasureComputerDefinition define(final MeasureComputerDefinitionContext defContext) { - - return defContext.newDefinitionBuilder().setInputMetrics(UTILITY_GLOBAL_MUTATIONS_KEY, TEST_KILLS_KEY).setOutputMetrics(TEST_KILL_RATIO_KEY).build(); + return defContext.newDefinitionBuilder() + .setInputMetrics(UTILITY_GLOBAL_MUTATIONS_KEY, TEST_KILLS_KEY) + .setOutputMetrics(TEST_KILL_RATIO_KEY) + .build(); } @Override public void compute(final MeasureComputerContext context) { - if (!MutationAnalysisPlugin.isExperimentalFeaturesEnabled(config)) { LOG.info("ExperimentalFeature disabled"); return; @@ -61,17 +61,16 @@ public void compute(final MeasureComputerContext context) { final Component comp = context.getComponent(); if (isNotUnitTestOrDirectory(comp)) { - LOG.info("Skipping {} because it's not test resource", comp); + LOG.info("Skipping {} because it's not a test resource", comp); return; } final double mutationsGlobal = getMutationsGlobal(context, UTILITY_GLOBAL_MUTATIONS_KEY); final double testKillsLocal = getTestKillsLocal(context); - if (mutationsGlobal == 0.0) { return; } - final double percentage = 100.0 * testKillsLocal / mutationsGlobal; + final double percentage = 100.0d * testKillsLocal / mutationsGlobal; LOG.info("Computed {} of {}% from ({} / {}) for {}", TEST_KILL_RATIO.getName(), percentage, testKillsLocal, mutationsGlobal, comp); context.addMeasure(TEST_KILL_RATIO_KEY, percentage); } @@ -81,12 +80,13 @@ boolean isNotUnitTestOrDirectory(final Component comp) { } private double getMutationsGlobal(final MeasureComputerContext context, String key) { - final double mutationsGlobal; final Measure globalMutationsMeasure = context.getMeasure(key); - if (globalMutationsMeasure == null) { - mutationsGlobal = Streams.parallelStream(context.getChildrenMeasures(key)).mapToInt(Measure::getIntValue).findFirst().orElse(0); + mutationsGlobal = Streams.parallelStream(context.getChildrenMeasures(key)) + .mapToInt(Measure::getIntValue) + .findFirst() + .orElse(0); LOG.info("Component {} has no global mutation information, using first child's: {}", context.getComponent(), mutationsGlobal); } else { mutationsGlobal = globalMutationsMeasure.getIntValue(); @@ -96,11 +96,11 @@ private double getMutationsGlobal(final MeasureComputerContext context, String k } private double getTestKillsLocal(final MeasureComputerContext context) { - final Measure localTestKills = context.getMeasure(TEST_KILLS_KEY); - if (localTestKills == null) { - return Streams.parallelStream(context.getChildrenMeasures(TEST_KILLS_KEY)).mapToInt(Measure::getIntValue).sum(); + return Streams.parallelStream(context.getChildrenMeasures(TEST_KILLS_KEY)) + .mapToInt(Measure::getIntValue) + .sum(); } else { return localTestKills.getIntValue(); } diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/TotalMutationsComputer.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/TotalMutationsComputer.java index 6be553b..d2d3218 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/TotalMutationsComputer.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/TotalMutationsComputer.java @@ -34,10 +34,9 @@ import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.UTILITY_GLOBAL_MUTATIONS_KEY; import static org.slf4j.LoggerFactory.getLogger; -import java.io.Serializable; - import ch.devcon5.sonar.plugins.mutationanalysis.MutationAnalysisPlugin; import ch.devcon5.sonar.plugins.mutationanalysis.Streams; +import java.io.Serializable; import org.slf4j.Logger; import org.sonar.api.ce.measure.Component; import org.sonar.api.ce.measure.Measure; @@ -52,22 +51,19 @@ public class TotalMutationsComputer implements MeasureComputer { private final Configuration config; public TotalMutationsComputer(final Configuration config) { - this.config = config; } @Override public MeasureComputerDefinition define(final MeasureComputerDefinitionContext defContext) { - return defContext.newDefinitionBuilder() - .setInputMetrics(UTILITY_GLOBAL_MUTATIONS_KEY, UTILITY_GLOBAL_ALIVE_KEY, MUTATIONS_TOTAL_KEY, MUTATIONS_ALIVE_KEY) - .setOutputMetrics(MUTATIONS_TOTAL_PERCENT_KEY, MUTATIONS_ALIVE_PERCENT_KEY) - .build(); + .setInputMetrics(UTILITY_GLOBAL_MUTATIONS_KEY, UTILITY_GLOBAL_ALIVE_KEY, MUTATIONS_TOTAL_KEY, MUTATIONS_ALIVE_KEY) + .setOutputMetrics(MUTATIONS_TOTAL_PERCENT_KEY, MUTATIONS_ALIVE_PERCENT_KEY) + .build(); } @Override public void compute(final MeasureComputerContext context) { - if (!MutationAnalysisPlugin.isExperimentalFeaturesEnabled(config)) { LOG.info("Experimental features disabled"); return; @@ -107,29 +103,25 @@ private boolean isUnitTestFile(final Component comp) { } private void computePercentage(final MeasureComputerContext context, final Metric globalMetric, final Metric localMetric, final Metric resultMetric) { - final double mutationsGlobal = getMutationsGlobal(context, globalMetric); final double mutationsLocal = getMutationsLocal(context, localMetric); - if (mutationsGlobal == 0.0) { LOG.info("No mutations found in project"); - } else { - final double percentage = 100.0 * mutationsLocal / mutationsGlobal; + } else { + final double percentage = 100.0d * mutationsLocal / mutationsGlobal; LOG.info("Computed {} of {}% from ({} / {}) for {}", resultMetric.getName(), percentage, mutationsLocal, mutationsGlobal, context.getComponent()); context.addMeasure(resultMetric.key(), percentage); } } private double getMutationsGlobal(final MeasureComputerContext context, Metric metric) { - final double mutationsGlobal; final Measure globalMutationsMeasure = context.getMeasure(metric.key()); - if (globalMutationsMeasure == null) { mutationsGlobal = Streams.parallelStream(context.getChildrenMeasures(metric.key())) - .mapToInt(Measure::getIntValue) - .findFirst() - .orElse(0); + .mapToInt(Measure::getIntValue) + .findFirst() + .orElse(0); LOG.info("Component {} has no global mutation information, using first child's: {}", context.getComponent(), mutationsGlobal); } else { mutationsGlobal = globalMutationsMeasure.getIntValue(); @@ -139,18 +131,16 @@ private double getMutationsGlobal(final MeasureComputerContext context, Metric metric) { - final double mutationsLocal; final Measure localMutationsMeasure = context.getMeasure(metric.key()); - if (localMutationsMeasure == null) { - mutationsLocal = Streams.parallelStream(context.getChildrenMeasures(metric.key())).mapToInt(Measure::getIntValue).sum(); + mutationsLocal = Streams.parallelStream(context.getChildrenMeasures(metric.key())) + .mapToInt(Measure::getIntValue) + .sum(); LOG.info("Component {} children have {} mutations ", context.getComponent(), mutationsLocal); - } else { mutationsLocal = localMutationsMeasure.getIntValue(); LOG.info("Component {} has no children, using local mutation count of {}", context.getComponent(), mutationsLocal); - } return mutationsLocal; } diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java index b0be8b5..22cd807 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java @@ -59,7 +59,7 @@ public class Mutant { private final int hashCode; private final String toString; private final TestDescriptor testDescriptor; - private String description; + private final String description; /** * Creates a new Mutant using the specified builder. This constructor is invoked by the builder. @@ -70,7 +70,6 @@ public class Mutant { * to be non-null for the construction to succeed. */ private Mutant(final Builder builder) { - this.state = builder.state; this.sourceFile = builder.sourceFile; this.mutatedClass = builder.mutatedClass; @@ -118,18 +117,15 @@ private Mutant(final Builder builder) { this.description == null ? 0 : this.description.hashCode()); this.testDescriptor = new TestDescriptor(this.killingTest); - } /** - * Creates a new build to define a mutant. As the {@link Mutant} class is designed as being immutable, the builder - * allows sequential definition of the {@link Mutant}'s properties instead of passing all at once to the - * constructor. + * Creates a new builder to define a mutant. As the {@link Mutant} class is immutable, the builder allows + * sequential definition of the {@link Mutant}'s properties instead of passing all at once to the constructor. * * @return a {@link ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant.Builder} for creating a {@link Mutant} */ public static Builder builder() { - return new Builder(); } @@ -137,16 +133,13 @@ public static Builder builder() { * @return flag to indicate if the mutant was detected by a test or not */ public boolean isDetected() { - return state.isDetected(); } /** - * @return the {@link Mutant.State} of the mutant. Only killed mutants are - * good mutants. + * @return the {@link Mutant.State} of the mutant. Only killed mutants are good mutants. */ public State getState() { - return state; } @@ -154,7 +147,6 @@ public State getState() { * @return the path to the sourceFile that contains the mutant. The sourceFile is relative to the project path. */ public String getSourceFile() { - return sourceFile; } @@ -162,7 +154,6 @@ public String getSourceFile() { * @return the fully qualified class name containing the mutant */ public String getMutatedClass() { - return mutatedClass; } @@ -170,7 +161,6 @@ public String getMutatedClass() { * @return the name of the method containing the mutant */ public String getMutatedMethod() { - return mutatedMethod; } @@ -178,7 +168,6 @@ public String getMutatedMethod() { * @return the description of the method that specifies its signature. {@see http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3.3} */ public String getMethodDescription() { - return methodDescription; } @@ -186,7 +175,6 @@ public String getMethodDescription() { * @return the line number where the mutant was found */ public int getLineNumber() { - return lineNumber; } @@ -194,7 +182,6 @@ public int getLineNumber() { * @return the mutationOperator that was used to create the mutant */ public MutationOperator getMutationOperator() { - return mutationOperator; } @@ -204,7 +191,6 @@ public MutationOperator getMutationOperator() { * null is not allowed */ public String getMutatorSuffix() { - return mutatorSuffix; } @@ -212,7 +198,6 @@ public String getMutatorSuffix() { * @return the index of the mutationOperator. It has no relevance to the sonar results */ public int getIndex() { - return index; } @@ -221,44 +206,36 @@ public int getIndex() { * not killed, this is an empty string and is never null. */ public String getKillingTest() { - return killingTest; } /** - * - * @return the number of tests that had to be executed to kill the mutant. The number is - * usually >= 1. + * @return the number of tests that had to be executed to kill the mutant. The number is usually >= 1. */ public int getNumberOfTestsRun() { - return numberOfTestsRun; } /** * Newer versions of Pit produce a description containing more details about what has been mutated. * - * @return the description if the mutant contained any or an empty optional + * @return the description if the mutant contained any, or an empty optional */ public Optional getDescription() { - return Optional.ofNullable(this.description); } public TestDescriptor getTestDescriptor() { - return this.testDescriptor; } @Override public int hashCode() { - return this.hashCode; } @Override public boolean equals(final Object obj) { - if (this == obj) { return true; } @@ -273,12 +250,10 @@ public boolean equals(final Object obj) { @Override public String toString() { - return toString; } private int calculateHashCode(final int... values) { - int result = 1; for (final int value : values) { result = PRIME * result + value; @@ -287,7 +262,6 @@ private int calculateHashCode(final int... values) { } private boolean equalsMutant(final Mutant other) { // NOSONAR - if (index != other.index) { return false; } @@ -334,10 +308,10 @@ public enum State { // a sane implementation would use an enum constructor with the alive flag and a getter // for example: State(boolean alive) // - // unfortunately, this creates an unkillable mutant by removing the variable assignment. Although + // unfortunately, this creates an un-killable mutant by removing the variable assignment. Although // some test would fail when run separately, when running with pit this does not fail. // I assume this is because the literals of the enum are created once and remain immutable - // in the JVM, even if it's constructor change. + // in the JVM, even if its constructor changes. // to circumvent this, I chose to use the abstract method / override method approach that // leaves nothing to mutate. // It's working, but it's certainly less readable. @@ -409,7 +383,6 @@ public boolean isAlive() { * otherwise the matching state. */ public static State parse(final String stateName) { - for (final State state : State.values()) { if (state.name().equals(stateName)) { return state; @@ -432,7 +405,6 @@ public static State parse(final String stateName) { * @return true if the {@link Mutant} was detected. */ public boolean isDetected() { - return !isAlive(); } } @@ -459,7 +431,6 @@ public static class Builder { private String description; Builder() { - } /** @@ -469,7 +440,6 @@ public static class Builder { * @return this builder */ public Builder mutantStatus(final State state) { - this.state = state; return this; } @@ -481,7 +451,6 @@ public Builder mutantStatus(final State state) { * @return this builder */ public Builder mutantStatus(final String statusName) { - return mutantStatus(State.parse(statusName)); } @@ -492,7 +461,6 @@ public Builder mutantStatus(final String statusName) { * @return this builder */ public Builder inSourceFile(final String sourceFile) { - this.sourceFile = sourceFile; return this; } @@ -504,7 +472,6 @@ public Builder inSourceFile(final String sourceFile) { * @return this builder */ public Builder inClass(final String mutatedClass) { - this.mutatedClass = mutatedClass; return this; } @@ -516,7 +483,6 @@ public Builder inClass(final String mutatedClass) { * @return this builder */ public Builder inMethod(final String mutatedMethod) { - this.mutatedMethod = mutatedMethod; return this; } @@ -529,7 +495,6 @@ public Builder inMethod(final String mutatedMethod) { * @return this builder */ public Builder withMethodParameters(final String methodDescription) { - this.methodDescription = methodDescription; return this; } @@ -541,7 +506,6 @@ public Builder withMethodParameters(final String methodDescription) { * @return this builder */ public Builder inLine(final int lineNumber) { - this.lineNumber = lineNumber; return this; } @@ -553,7 +517,6 @@ public Builder inLine(final int lineNumber) { * @return this builder */ public Builder usingMutator(final MutationOperator mutationOperator) { - this.mutationOperator = mutationOperator; mutatorSuffix = ""; return this; @@ -561,16 +524,14 @@ public Builder usingMutator(final MutationOperator mutationOperator) { /** * @param mutagenName - * the mutationOperator that was used to create the mutant specified as String. The string may be either the the ID, + * the mutationOperator that was used to create the mutant specified as String. The string may be either the ID, * the fully qualified class name or the fully qualified class name and a suffix. If the mutagenName is * specified with suffix, the mutationOperator suffix is set accordingly, otherwise the empty string is used. * * @return this builder */ public Builder usingMutator(final String mutagenName) { - mutationOperator = MutationOperators.find(mutagenName); - if (mutationOperator == MutationOperators.UNKNOWN) { LOGGER.warn("Found unknown mutation operator: {}", mutagenName); mutatorSuffix = ""; @@ -587,7 +548,6 @@ public Builder usingMutator(final String mutagenName) { mutatorSuffix = mutatorSuffix.substring(1); } return this; - } /** @@ -597,7 +557,6 @@ public Builder usingMutator(final String mutagenName) { * @return this builder */ public Builder atIndex(final int index) { - this.index = index; return this; } @@ -605,13 +564,12 @@ public Builder atIndex(final int index) { /** * @param killingTest * the fully qualified name of the test including the test method that killed the test. This method is - * optional and only has to be invoked, if the mutant was actually killed. If not invoked, the the + * optional and only has to be invoked, if the mutant was actually killed. If not invoked, the * killingTest property is passed as empty string * * @return this builder */ public Builder killedBy(final String killingTest) { - this.killingTest = killingTest; return this; } @@ -637,18 +595,15 @@ public Builder numberOfTestsRun(final int numberOfTestsRun){ * @return a new instance of a {@link ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant} */ public Mutant build() { - requireNonNull(state, "state must be set"); requireNonNull(sourceFile, "sourceFile must be set"); requireNonNull(mutatedClass, "mutatedClass must be set"); requireNonNull(mutatedMethod, "mutatedMethod must be set"); requireNonNull(methodDescription, "methodDescription must be set"); requireNonNull(mutationOperator, "mutationOperator must be set"); - if (!state.isAlive()) { requireNonNull(killingTest, "killingTest must be set"); } - return new Mutant(this); } @@ -662,5 +617,7 @@ private static void requireNonNull(T obj, String message) { throw new IllegalArgumentException(message); } } + } + } diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/MutationOperator.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/MutationOperator.java index 9e88536..59ae73c 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/MutationOperator.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/MutationOperator.java @@ -26,11 +26,9 @@ import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.Collection; -import java.util.Collections; import java.util.HashSet; import java.util.Optional; import java.util.Set; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.internal.apachecommons.io.IOUtils; @@ -45,124 +43,110 @@ */ public final class MutationOperator { - private static final Logger LOG = LoggerFactory.getLogger(MutationOperator.class); - - private final String id; - private final URL mutagenDescLoc; - private final String violationDesc; - private final String name; - private final Set classNames; - - MutationOperator(final String id, - final String name, - final Collection classNames, - final String violationDesc, - final URL mutagenDescriptionLocation) { - - requireNonNull(id, "Id must not be null"); - requireNonNull(name, "name must not be null"); - requireNonNull(violationDesc, "violation description must not be null"); - this.id = id; - this.name = name; - this.classNames = new HashSet<>(requireNonNull(classNames, "classNames must not be null")); - this.violationDesc = violationDesc; - this.mutagenDescLoc = mutagenDescriptionLocation; - } - - - - /** - * The ID of the mutagen. The ID is a String that uniquely defines the MutationOperator, written uppercase, like {@code - * ARGUMENT_PROPAGATION}. - * - * @return the {@link MutationOperator} id as a String - */ - public String getId() { - - return id; - } - - /** - * An URL pointing to the description of the {@link MutationOperator}. The {@link MutationOperator} descriptions are stored in the - * classpath as resource, for each {@link MutationOperator} a separate description. The URLs are specified in the {@code - * mutagen-def.xml} in the classpath. The resources itself are html fragments that can be embedded in a website. - * - * @return URL pointing to the resource containing the description. - */ - public Optional getMutagenDescriptionLocation() { - - return Optional.ofNullable(mutagenDescLoc); - } - - /** - * The violation description used for the {@link MutationOperator} specific {@link Rule} that are violated if the mutation - * caused by the {@link MutationOperator} is not killed. - * - * @return the string description the violation. - */ - public String getViolationDescription() { - - return violationDesc; - } - - /** - * The name of the MutationOperator. Unlike the Id, it is more a display name. - * - * @return the name as a String - */ - public String getName() { - - return name; + private static final Logger LOG = LoggerFactory.getLogger(MutationOperator.class); + + private final String id; + private final URL mutagenDescLoc; + private final String violationDesc; + private final String name; + private final Set classNames; + + MutationOperator(final String id, final String name, final Collection classNames, final String violationDesc, final URL mutagenDescriptionLocation) { + requireNonNull(id, "Id must not be null"); + requireNonNull(name, "name must not be null"); + requireNonNull(violationDesc, "violation description must not be null"); + this.id = id; + this.name = name; + this.classNames = new HashSet<>(requireNonNull(classNames, "classNames must not be null")); + this.violationDesc = violationDesc; + this.mutagenDescLoc = mutagenDescriptionLocation; + } + + /** + * The ID of the mutagen. The ID is a String that uniquely defines the MutationOperator, written uppercase, like + * {@code ARGUMENT_PROPAGATION}. + * + * @return the {@link MutationOperator} id as a String + */ + public String getId() { + return id; + } + + /** + * A URL pointing to the description of the {@link MutationOperator}. The {@link MutationOperator} descriptions are + * stored in the classpath as resource, for each {@link MutationOperator} a separate description. The URLs are + * specified in the {@code mutagen-def.xml} in the classpath. The resources itself are html fragments that can be + * embedded in a website. + * + * @return URL pointing to the resource containing the description. + */ + public Optional getMutagenDescriptionLocation() { + return Optional.ofNullable(mutagenDescLoc); + } + + /** + * The violation description used for the {@link MutationOperator} specific {@link Rule} that are violated if the + * mutation caused by the {@link MutationOperator} is not killed. + * + * @return the string description the violation. + */ + public String getViolationDescription() { + return violationDesc; + } + + /** + * The name of the MutationOperator. Unlike the Id, it is more a display name. + * + * @return the name as a String + */ + public String getName() { + return name; + } + + /** + * The fully qualified classnames of the {@link MutationOperator} class. + * + * @return the classnames + */ + public Set getClassNames() { + return classNames; + } + + /** + * The description of the {@link MutationOperator}. The method loads the content defined in the resource that is + * referred to by the description URL. + * + * @return the description as a string + */ + public String getMutagenDescription() { + return getMutagenDescriptionLocation().map(u -> { + try { + return IOUtils.toString(u, StandardCharsets.UTF_8); + } catch (IOException e) { + LOG.warn("Cannot read mutagen description for mutagen {}", id, e); + return "No description"; + } + }).orElse(""); + } + + @Override + public int hashCode() { + return 31 + id.hashCode(); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; } - - /** - * The fully qualified classnames of the {@link MutationOperator} class. - * - * @return the classnames - */ - public Set getClassNames() { - return Collections.unmodifiableSet(classNames); + if (obj == null) { + return false; } - - /** - * The description of the {@link MutationOperator}. The method loads the content defined in the resource that is referred to - * by the description URL. - * - * @return the description as a string - */ - public String getMutagenDescription() { - - return getMutagenDescriptionLocation().map(u -> { - try { - return IOUtils.toString(u, StandardCharsets.UTF_8); - } catch (IOException e) { - LOG.warn("Cannot read mutagen description for mutagen {}", id, e); - return "No description"; - } - }).orElse(""); - } - - @Override - public int hashCode() { - - return 31 + id.hashCode(); - } - - @Override - public boolean equals(final Object obj) { - - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final MutationOperator other = (MutationOperator) obj; - - return id.equals(other.id); + if (getClass() != obj.getClass()) { + return false; } + final MutationOperator other = (MutationOperator) obj; + return id.equals(other.id); + } } diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/MutationOperators.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/MutationOperators.java index 0becd7b..bc7773c 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/MutationOperators.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/MutationOperators.java @@ -22,9 +22,6 @@ import static javax.xml.xpath.XPathConstants.NODESET; -import javax.xml.xpath.XPath; -import javax.xml.xpath.XPathExpressionException; -import javax.xml.xpath.XPathFactory; import java.io.IOException; import java.io.InputStream; import java.net.URL; @@ -34,7 +31,9 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; - +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; @@ -44,118 +43,103 @@ */ public final class MutationOperators { - - private MutationOperators(){} - - - - /** - * Default MutationOperator definition for an unknown {@link MutationOperator} - */ - public static final MutationOperator UNKNOWN = new MutationOperator("UNKNOWN", - "Unknown mutagen", - Collections.singleton("unknown.mutation.operator"), - "An unknown mutagen has been applied", - null); - /** - * URL of the mutagen definitions. - */ - private static final URL MUTAGEN_DEF = MutationOperator.class.getResource("mutagen-def.xml"); - /** - * Contains all instances of {@link MutationOperator}s defined in the mutagen-def.xml - */ - private static final Map INSTANCES; - - static { - try (InputStream stream = MUTAGEN_DEF.openStream()) { - final Map mutagens = new HashMap<>(); - final XPathFactory xPathFactory = XPathFactory.newInstance(); - final XPath xp = xPathFactory.newXPath(); - final NodeList mutagenNodes = (NodeList) xp.evaluate("//operator", new InputSource(stream), NODESET); - for (int i = 0, len = mutagenNodes.getLength(); i < len; i++) { - final Node mutagenNode = mutagenNodes.item(i); - final MutationOperator mutationOperator = toMutagen(xPathFactory, mutagenNode); - mutagens.put(mutationOperator.getId(), mutationOperator); - } - INSTANCES = Collections.unmodifiableMap(mutagens); - } catch (IOException | XPathExpressionException e) { - throw new MutationOperatorsInitializationException("Could not load mutagen definitions", e); - } + /** + * Default MutationOperator definition for an unknown {@link MutationOperator} + */ + public static final MutationOperator UNKNOWN = new MutationOperator("UNKNOWN", "Unknown mutagen", + Collections.singleton("unknown.mutation.operator"), "An unknown mutagen has been applied", null); + + /** + * URL of the mutagen definitions. + */ + private static final URL MUTAGEN_DEF = MutationOperator.class.getResource("mutagen-def.xml"); + + /** + * Contains all instances of {@link MutationOperator}s defined in the mutagen-def.xml + */ + private static final Map INSTANCES; + static { + try (InputStream stream = MUTAGEN_DEF.openStream()) { + final Map mutagens = new HashMap<>(); + final XPathFactory xPathFactory = XPathFactory.newInstance(); + final XPath xp = xPathFactory.newXPath(); + final NodeList mutagenNodes = (NodeList) xp.evaluate("//operator", new InputSource(stream), NODESET); + for (int i = 0, len = mutagenNodes.getLength(); i < len; i++) { + final Node mutagenNode = mutagenNodes.item(i); + final MutationOperator mutationOperator = toMutagen(xPathFactory, mutagenNode); + mutagens.put(mutationOperator.getId(), mutationOperator); + } + INSTANCES = Collections.unmodifiableMap(mutagens); + } catch (IOException | XPathExpressionException e) { + throw new MutationOperatorsInitializationException("Could not load mutagen definitions", e); } - - - - /** - * Converts a MutationOperator from the given {@link Node} - * - * - * @param xPathFactory - * @param mutagenNode - * the node to convert to {@link ch.devcon5.sonar.plugins.mutationanalysis.model.MutationOperator} - * - * @return a {@link MutationOperator} for the {@link Node} - * - * @throws XPathExpressionException - */ - private static MutationOperator toMutagen(final XPathFactory xPathFactory, final Node mutagenNode) throws XPathExpressionException { - - final XPath xp = xPathFactory.newXPath(); - final String id = xp.evaluate("@id", mutagenNode); - - final Set classNames = new HashSet<>(); - final NodeList classNodes = (NodeList) xp.evaluate("classes/class", mutagenNode, NODESET); - for (int i = 0, len = classNodes.getLength(); i < len; i++) { - final Node classNode = classNodes.item(i); - classNames.add(classNode.getTextContent()); - } - - final String name = xp.evaluate("name", mutagenNode); - final String violationDescription = xp.evaluate("violationDescription", mutagenNode).trim(); - final URL mutagenDescLoc = MutationOperator.class.getResource(xp.evaluate("operatorDescription/@classpath", - mutagenNode)); - - return new MutationOperator(id, name, classNames, violationDescription, mutagenDescLoc); + } + + private MutationOperators() {} + + /** + * Converts a MutationOperator from the given {@link Node} + * + * @param xPathFactory the factory with which to create a new xpath + * @param mutagenNode the node to convert to {@link ch.devcon5.sonar.plugins.mutationanalysis.model.MutationOperator} + * @return a {@link MutationOperator} for the {@link Node} + * @throws XPathExpressionException if there was an exception evaluating an xpath expression + */ + private static MutationOperator toMutagen(final XPathFactory xPathFactory, final Node mutagenNode) throws XPathExpressionException { + final XPath xp = xPathFactory.newXPath(); + final String id = xp.evaluate("@id", mutagenNode); + + final Set classNames = new HashSet<>(); + final NodeList classNodes = (NodeList) xp.evaluate("classes/class", mutagenNode, NODESET); + for (int i = 0, len = classNodes.getLength(); i < len; i++) { + final Node classNode = classNodes.item(i); + classNames.add(classNode.getTextContent()); } - /** - * Finds the {@link MutationOperator} using the specified key. The key could be the ID of the MutationOperator, it's classname or an - * extended classname, which is the classname with a suffix. - * - * @param mutagenKey - * the key to use for searching for the mutagen - * - * @return a matchin {@link MutationOperator} or an UNKNOWN mutagen - */ - public static MutationOperator find(final String mutagenKey) { - - MutationOperator result = UNKNOWN; - for (final MutationOperator mutationOperator : INSTANCES.values()) { - if (mutagenKey.equals(mutationOperator.getId()) - || mutationOperator.getClassNames().stream().anyMatch(mutagenKey::startsWith)) { - result = mutationOperator; - break; - } - } - return result; + final String name = xp.evaluate("name", mutagenNode); + final String violationDescription = xp.evaluate("violationDescription", mutagenNode).trim(); + final URL mutagenDescLoc = MutationOperator.class.getResource(xp.evaluate("operatorDescription/@classpath", mutagenNode)); + + return new MutationOperator(id, name, classNames, violationDescription, mutagenDescLoc); + } + + /** + * Finds the {@link MutationOperator} using the specified key. The key could be the ID of the MutationOperator, + * its classname or an extended classname, which is the classname with a suffix. + * + * @param mutagenKey the key to use when searching for the mutagen + * @return a matching {@link MutationOperator} or an UNKNOWN mutagen + */ + public static MutationOperator find(final String mutagenKey) { + MutationOperator result = UNKNOWN; + for (final MutationOperator mutationOperator : INSTANCES.values()) { + if (mutagenKey.equals(mutationOperator.getId()) + || mutationOperator.getClassNames().stream().anyMatch(mutagenKey::startsWith)) { + result = mutationOperator; + break; + } } - - /** - * Retrieves all defined {@link MutationOperator}s from the mutagen-def.xml. - * - * @return a collection of {@link MutationOperator}s - */ - public static Collection allMutationOperators() { - - return Collections.unmodifiableCollection(INSTANCES.values()); + return result; + } + + /** + * Retrieves all defined {@link MutationOperator}s from the mutagen-def.xml. + * + * @return a collection of {@link MutationOperator}s + */ + public static Collection allMutationOperators() { + return Collections.unmodifiableCollection(INSTANCES.values()); + } + + /** + * Exception that is thrown if the mutation operators can not be loaded from configuration. + */ + public static class MutationOperatorsInitializationException extends RuntimeException { + + public MutationOperatorsInitializationException(final String message, final Throwable cause) { + super(message, cause); } - /** - * Exception that is thrown if the mutation operators can not be loaded from configuration. - */ - public static class MutationOperatorsInitializationException extends RuntimeException { + } - public MutationOperatorsInitializationException(final String message, final Throwable cause) { - super(message, cause); - } - } } diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/TestDescriptor.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/TestDescriptor.java index fd5fb6b..2c8fdba 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/TestDescriptor.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/TestDescriptor.java @@ -24,71 +24,65 @@ public class TestDescriptor { - private final String className; - private final String methodName; - private final String spec; + private final String className; + private final String methodName; + private final String spec; - public TestDescriptor(final String spec) { - this.spec = spec; + public TestDescriptor(final String spec) { + this.spec = spec; - final int parenthesis = spec.indexOf('('); - final int methodSeparator = spec.lastIndexOf('.', parenthesis); - final int nestedSeparator = spec.indexOf('$'); - - if(nestedSeparator != -1){ - this.className = spec.substring(0, nestedSeparator); - if(methodSeparator != -1){ - this.methodName = spec.substring(methodSeparator + 1, parenthesis); - } else { - this.methodName = "unknown"; - } - } else - if(methodSeparator != -1){ - this.className = spec.substring(0, methodSeparator); - this.methodName = spec.substring(methodSeparator + 1, parenthesis); + final int parenthesis = spec.indexOf('('); + final int methodSeparator = spec.lastIndexOf('.', parenthesis); + final int nestedSeparator = spec.indexOf('$'); + if (nestedSeparator != -1) { + this.className = spec.substring(0, nestedSeparator); + if (methodSeparator != -1) { + this.methodName = spec.substring(methodSeparator + 1, parenthesis); } else { - className = spec; - methodName = "unknown"; - } - - } - - public String getClassName() { - return className; - } - - public String getMethodName() { - return methodName; - } - - public String getSpec() { - return spec; - } - - @Override - public String toString() { - final StringBuilder sb = new StringBuilder("TestDescriptor{"); - sb.append("class='").append(className).append("', method='").append(methodName); - sb.append("'}"); - return sb.toString(); - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; + this.methodName = "unknown"; } - if (o == null || getClass() != o.getClass()) { - return false; - } - final TestDescriptor that = (TestDescriptor) o; - return Objects.equals(className, that.className); - } - - @Override - public int hashCode() { + } else if (methodSeparator != -1) { + this.className = spec.substring(0, methodSeparator); + this.methodName = spec.substring(methodSeparator + 1, parenthesis); + } else { + className = spec; + methodName = "unknown"; + } + } + + public String getClassName() { + return className; + } + + public String getMethodName() { + return methodName; + } + + public String getSpec() { + return spec; + } + + @Override + public String toString() { + return "TestDescriptor{class='" + className + "', method='" + methodName + "'}"; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final TestDescriptor that = (TestDescriptor) o; + return Objects.equals(className, that.className); + } + + @Override + public int hashCode() { + return Objects.hash(className); + } - return Objects.hash(className); - } } diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/report/PitestReportParser.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/report/PitestReportParser.java index 4a17149..9b1e765 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/report/PitestReportParser.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/report/PitestReportParser.java @@ -55,7 +55,7 @@ * <killingTest>io.inkstand.scribble.inject.ResourceInjectionTest.testByMappedName_match(io.inkstand.scribble.inject.ResourceInjectionTest)</killingTest> * </mutation> * ... - * </mutantions> + * </mutations> * */ public class PitestReportParser { @@ -102,17 +102,13 @@ public class PitestReportParser { * if the report file could not be read */ public Collection parseMutants(final Path report) throws IOException { - Collection result; - - //TODO replace java.nio.Files API when migrating to JDK9+ - if (!report.toFile().exists()) { + if (report == null || !Files.exists(report)) { LOG.debug("No report {} found", report); return Collections.emptyList(); } try (InputStream stream = Files.newInputStream(report)) { result = readMutants(stream); - } catch (XMLStreamException e) { LOG.warn("Parsing report failed: {}", e.getMessage()); LOG.debug("Parsing error ", e); @@ -123,7 +119,7 @@ public Collection parseMutants(final Path report) throws IOException { /** * Reads mutants from the input stream which is assumed to be a stream of xml data. In case the stream contains invalid mutation description - i.e. mandatory information - * is mssing - an {@link XMLStreamException} containing the exact location of the fault is thrown. + * is missing - an {@link XMLStreamException} containing the exact location of the fault is thrown. * @param stream * the input stream containing the xml data * @return @@ -153,7 +149,6 @@ Collection readMutants(final InputStream stream) throws XMLStreamExcepti * @throws XMLStreamException */ private Collection readMutants(final XMLStreamReader reader) throws XMLStreamException { - final Collection result = new ArrayList<>(); int event; while (reader.hasNext()) { @@ -162,7 +157,6 @@ private Collection readMutants(final XMLStreamReader reader) throws XMLS startElement(reader, result); } } - return result; } @@ -178,7 +172,6 @@ private Collection readMutants(final XMLStreamReader reader) throws XMLS * @throws XMLStreamException */ private void startElement(final XMLStreamReader reader, final Collection result) throws XMLStreamException { - if (ELEMENT_MUTATION.equals(reader.getLocalName())) { final Mutant mutant = parseMutant(reader); LOG.debug("Found mutant {}", mutant); @@ -187,7 +180,7 @@ private void startElement(final XMLStreamReader reader, final Collection } /** - * The method assumes, the reader is at the start element position of a <mutation> element. + * The method assumes the reader is at the start element position of a <mutation> element. * * @param reader * @@ -196,11 +189,9 @@ private void startElement(final XMLStreamReader reader, final Collection * @throws XMLStreamException */ private Mutant parseMutant(final XMLStreamReader reader) throws XMLStreamException { - final Mutant.Builder builder = Mutant.builder() .mutantStatus(getMutantStatus(reader)) .numberOfTestsRun(getNumberOfTestsRun(reader)); - while (true) { int event = reader.next(); if (event == START_ELEMENT) { @@ -224,7 +215,6 @@ private Mutant parseMutant(final XMLStreamReader reader) throws XMLStreamExcepti * @throws XMLStreamException */ private void buildMutant(final XMLStreamReader reader, final Mutant.Builder builder) throws XMLStreamException { - switch (reader.getLocalName()) { case ELEMENT_SOURCE_FILE: builder.inSourceFile(reader.getElementText()); @@ -267,7 +257,6 @@ private void buildMutant(final XMLStreamReader reader, final Mutant.Builder buil * @return the mutant status as a string */ private String getMutantStatus(final XMLStreamReader reader) { - return reader.getAttributeValue(NAMESPACE_URI, ATTR_STATUS); } @@ -280,11 +269,11 @@ private String getMutantStatus(final XMLStreamReader reader) { * @return the mutant status as a string */ private int getNumberOfTestsRun(final XMLStreamReader reader) { - final String numberOfTestsRun = reader.getAttributeValue(NAMESPACE_URI, ATTR_NUMBER_OF_TESTS_RUN); if(numberOfTestsRun != null){ return Integer.parseInt(numberOfTestsRun); } return 0; } + } diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/report/ReportFinder.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/report/ReportFinder.java index 3d9e1d8..a2500bf 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/report/ReportFinder.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/report/ReportFinder.java @@ -37,7 +37,6 @@ /** * Searches the latest xml file in the reports directory. - * */ public class ReportFinder { @@ -59,13 +58,10 @@ public class ReportFinder { * if the most recent report could not be determined */ public Path findReport(final Path reportDirectory) throws IOException { - - //TODO replace java.nio.Files API when migrating to JDK9+ - if (reportDirectory == null || !reportDirectory.toFile().exists() ) { - LOG.warn("ReportDirectory {} is no valid directory", reportDirectory); + if (reportDirectory == null || !Files.exists(reportDirectory)) { + LOG.warn("ReportDirectory {} is not a valid directory", reportDirectory); return null; } - return findMostRecentReport(reportDirectory, "*.xml"); } @@ -83,17 +79,14 @@ public Path findReport(final Path reportDirectory) throws IOException { * @throws java.io.IOException if the report or the directory of the report can not be accessed */ protected Path findMostRecentReport(final Path reportDirectory, final String pattern) throws IOException { - Path mostRecent = null; ReportFinderVisitor reportFinderVisitor = new ReportFinderVisitor(pattern); Files.walkFileTree(reportDirectory, reportFinderVisitor); - for (final Path report : reportFinderVisitor.getReports()) { if (mostRecent == null || isNewer(mostRecent, report)) { mostRecent = report; } } - return mostRecent; } @@ -103,18 +96,16 @@ protected Path findMostRecentReport(final Path reportDirectory, final String pat * @param referencePath * the path to compare the other path against * @param otherPath - * the other path to be comapred against the reference path + * the other path to be compared against the reference path * * @return true if the otherPath is newer than the referencePath * * @throws IOException if the last modification time can not be determined */ protected boolean isNewer(final Path referencePath, final Path otherPath) throws IOException { - return Files.getLastModifiedTime(referencePath).compareTo(Files.getLastModifiedTime(otherPath)) < 0; } - /** * Recursive search report xml */ @@ -135,17 +126,19 @@ public List getReports() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { requireNonNull(file, "file must not be null"); - final Path filename = file.getFileName(); if (Objects.nonNull(filename) && matcher.matches(filename)) { reports.add(file); } return FileVisitResult.CONTINUE; } + private static void requireNonNull(T obj, String message) { if (obj == null) { throw new IllegalArgumentException(message); } } + } + } diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/report/Reports.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/report/Reports.java index b227951..59bc023 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/report/Reports.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/report/Reports.java @@ -20,18 +20,17 @@ package ch.devcon5.sonar.plugins.mutationanalysis.report; +import ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant; import java.io.IOException; import java.nio.file.Path; import java.util.Collection; import java.util.Collections; - -import ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * Utility class to read a {@link ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant}s from a report located in a directory. - * + * Utility class to read a {@link ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant}s from a report located in a + * directory. */ public final class Reports { @@ -40,25 +39,19 @@ public final class Reports { */ private static final Logger LOG = LoggerFactory.getLogger(Reports.class); - private Reports() { - - } + private Reports() {} /** - * Reads the {@link ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant}s from the report in the reports directory. The method searches for the most recent - * {@code mutations.xml} report and returns it's contents as a list. + * Reads the {@link ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant}s from the report in the reports directory. + * The method searches for the most recent {@code mutations.xml} report and returns its contents as a list. * - * @param reportsDirectory - * the {@link Path} to the directory containing the report. - * - * @return a collection of all {@link ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant}s declared in the report or an empty list if neither report was found - * nor it contained any {@link ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant}s. The method does not return null - * - * @throws IOException - * if the search for the report failed or the report could not be read. + * @param reportsDirectory the {@link Path} to the directory containing the report. + * @return a collection of all {@link ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant}s declared in the report + * or an empty list if neither report was found nor it contained any + * {@link ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant}s. The method does not return null + * @throws IOException if the search for the report failed or the report could not be read. */ public static Collection readMutants(final Path reportsDirectory) throws IOException { - LOG.debug("Searching pit reports in {}", reportsDirectory); final Path xmlReport; @@ -71,10 +64,11 @@ public static Collection readMutants(final Path reportsDirectory) throws if (xmlReport == null) { LOG.warn("No XML PIT report found in directory {} !", reportsDirectory); LOG.warn( - "Checkout plugin documentation for more detailed explanations: https://github.com/devcon5io/mutation-analysis-plugin"); + "Checkout plugin documentation for more detailed explanations: https://github.com/devcon5io/mutation-analysis-plugin"); return Collections.emptyList(); } return new PitestReportParser().parseMutants(xmlReport); -} + } + } diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/JavaProfileDefinition.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/JavaProfileDefinition.java index c1c25b8..9afed57 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/JavaProfileDefinition.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/JavaProfileDefinition.java @@ -22,8 +22,9 @@ public class JavaProfileDefinition extends MutationAnalysisProfileDefinition { - @Override - protected String getLanguageKey() { - return "java"; - } + @Override + protected String getLanguageKey() { + return "java"; + } + } diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/JavaRulesDefinition.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/JavaRulesDefinition.java index ea6f9c7..d9870cb 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/JavaRulesDefinition.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/JavaRulesDefinition.java @@ -33,8 +33,8 @@ public class JavaRulesDefinition extends MutationAnalysisRulesDefinition { * Constructor to create the pitest rules definitions and repository. The constructor is invoked by Sonar. * * @param settings - * the settings of the Pitest-Sensor pluin - * @param xmlLoader + * the settings of the Pitest-Sensor plugin + * @param xmlLoader The xml loader with which to load rules */ public JavaRulesDefinition(final Configuration settings, final RulesDefinitionXmlLoader xmlLoader) { super(settings, xmlLoader); @@ -45,5 +45,4 @@ protected String getLanguageKey() { return "java"; } - } diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/KotlinProfileDefinition.kt b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/KotlinProfileDefinition.kt index ac53b27..f31887f 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/KotlinProfileDefinition.kt +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/KotlinProfileDefinition.kt @@ -26,4 +26,5 @@ class KotlinProfileDefinition : MutationAnalysisProfileDefinition() { override fun getLanguageKey(): String { return "kotlin" } + } diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/KotlinRulesDefinition.kt b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/KotlinRulesDefinition.kt index adcf684..55fed63 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/KotlinRulesDefinition.kt +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/KotlinRulesDefinition.kt @@ -29,11 +29,12 @@ import org.sonar.api.server.rule.RulesDefinitionXmlLoader * defined in the rules.xml file in the classpath. The rule keys are accessible as constants. */ class KotlinRulesDefinition + /** * Constructor to create the pitest rules definitions and repository. The constructor is invoked by Sonar. * * @param settings - * the settings of the Pitest-Sensor pluin + * the settings of the Pitest-Sensor plugin * @param xmlLoader */ (settings: Configuration, xmlLoader: RulesDefinitionXmlLoader) : MutationAnalysisRulesDefinition(settings, xmlLoader) { diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/MutationAnalysisProfileDefinition.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/MutationAnalysisProfileDefinition.java index fd64ec5..f34a14d 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/MutationAnalysisProfileDefinition.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/MutationAnalysisProfileDefinition.java @@ -28,15 +28,14 @@ public abstract class MutationAnalysisProfileDefinition implements BuiltInQualityProfilesDefinition { - @Override - public void define(final Context context) { - final NewBuiltInQualityProfile mutationAnalysis = context.createBuiltInQualityProfile("Mutation Analysis", getLanguageKey()); - - MutationOperators.allMutationOperators().forEach(m -> mutationAnalysis.activateRule(REPOSITORY_KEY + "." + getLanguageKey(), MUTANT_RULES_PREFIX + m.getId())); - - mutationAnalysis.done(); - } - - protected abstract String getLanguageKey(); + @Override + public void define(final Context context) { + final NewBuiltInQualityProfile mutationAnalysis = context.createBuiltInQualityProfile("Mutation Analysis", getLanguageKey()); + MutationOperators.allMutationOperators().forEach( + m -> mutationAnalysis.activateRule(REPOSITORY_KEY + "." + getLanguageKey(), MUTANT_RULES_PREFIX + m.getId())); + mutationAnalysis.done(); + } + + protected abstract String getLanguageKey(); } diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/MutationAnalysisRulesDefinition.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/MutationAnalysisRulesDefinition.java index 31d9a15..6626cc3 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/MutationAnalysisRulesDefinition.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/MutationAnalysisRulesDefinition.java @@ -25,6 +25,7 @@ import ch.devcon5.sonar.plugins.mutationanalysis.model.MutationOperator; import ch.devcon5.sonar.plugins.mutationanalysis.model.MutationOperators; +import java.nio.charset.StandardCharsets; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.config.Configuration; @@ -75,25 +76,22 @@ public abstract class MutationAnalysisRulesDefinition implements org.sonar.api.s */ private static final Logger LOG = LoggerFactory.getLogger(MutationAnalysisRulesDefinition.class); /** - * Loader used to load the rules from an xml files + * Loader used to load the rules from an xml file */ private final RulesDefinitionXmlLoader xmlLoader; /** - * The plugin setting.s + * The plugin settings */ private final Configuration settings; /** * Constructor to create the pitest rules definitions and repository. The constructor is invoked by Sonar. * - * @param settings - * the settings of the Pitest-Sensor pluin - * @param xmlLoader - * an XML loader to load the rules definitions from the rules def. + * @param settings the settings of the Pitest-Sensor plugin + * @param xmlLoader an XML loader to load the rules definitions from the rules def. */ protected MutationAnalysisRulesDefinition(final Configuration settings, final RulesDefinitionXmlLoader xmlLoader) { - this.xmlLoader = xmlLoader; this.settings = settings; } @@ -104,17 +102,16 @@ protected MutationAnalysisRulesDefinition(final Configuration settings, final Ru */ @Override public void define(final Context context) { - - final NewRepository repository = context.createRepository(REPOSITORY_KEY + "." + getLanguageKey(), getLanguageKey()).setName(REPOSITORY_NAME); + final NewRepository repository = context.createRepository(REPOSITORY_KEY + "." + getLanguageKey(), getLanguageKey()) + .setName(REPOSITORY_NAME); this.xmlLoader.load(repository, - getClass().getResourceAsStream("/ch/devcon5/sonar/plugins/mutationanalysis/rules.xml"), - "UTF-8"); + getClass().getResourceAsStream("/ch/devcon5/sonar/plugins/mutationanalysis/rules.xml"), StandardCharsets.UTF_8); addMutatorRules(repository); for (final NewRule rule : repository.rules()) { rule.setDebtRemediationFunction(rule.debtRemediationFunctions() - .linearWithOffset(settings.get(EFFORT_MUTANT_KILL) - .orElse(DEFAULT_EFFORT_TO_KILL_MUTANT), "7min")); + .linearWithOffset(settings.get(EFFORT_MUTANT_KILL) + .orElse(DEFAULT_EFFORT_TO_KILL_MUTANT), "7min")); rule.setGapDescription("Effort to kill the mutant(s)"); } repository.done(); @@ -123,20 +120,17 @@ public void define(final Context context) { protected abstract String getLanguageKey(); - /** * Enriches the mutator rules with the descriptions from the mutators * - * @param repository - * the repository containing the mutator rules + * @param repository the repository containing the mutator rules */ private void addMutatorRules(final NewRepository repository) { - for (final MutationOperator mutationOperator : MutationOperators.allMutationOperators()) { - //create for each mutation operator two rules, one is a "bug" rule, the other is - //a "code smell" rule. As there are project which prefer to treat them as bugs (or potential bugs) - //and other projects might prefer to treat them as code smell. This way - //the projects can decide themselves how to set up their quality profile + // create for each mutation operator two rules, one is a "bug" rule, the other is + // a "code smell" rule. As there are project which prefer to treat them as bugs (or potential bugs) + // and other projects might prefer to treat them as code smell. This way + // the projects can decide themselves how to set up their quality profile createBugRule(repository, mutationOperator); createCodeSmellRule(repository, mutationOperator); } @@ -154,23 +148,17 @@ private void createCodeSmellRule(final NewRepository repository, final MutationO createRule(repository, mutationOperator, RuleType.CODE_SMELL, id, name); } - private void createRule(final NewRepository repository, - final MutationOperator mutationOperator, - final RuleType type, - final String id, - final String name) { + private void createRule(final NewRepository repository, final MutationOperator mutationOperator, final RuleType type, final String id, final String name) { final NewRule rule = repository.createRule(id) - .setType(type) - .setName(name) - .setTags("pitest", "test", "test-quality", "mutator", "mutation-operator"); + .setType(type) + .setName(name) + .setTags("pitest", "test", "test-quality", "mutator", "mutation-operator"); mutationOperator.getMutagenDescriptionLocation().ifPresent(rule::setHtmlDescription); - if (mutationOperator.getId().startsWith("EXPERIMENTAL")) { rule.setStatus(RuleStatus.BETA); } else { rule.setActivatedByDefault(true); } - } } diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/PitestSensor.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/PitestSensor.java index 56343e4..4bca775 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/PitestSensor.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/PitestSensor.java @@ -22,16 +22,15 @@ import static ch.devcon5.sonar.plugins.mutationanalysis.rules.MutationAnalysisRulesDefinition.REPOSITORY_KEY; +import ch.devcon5.sonar.plugins.mutationanalysis.MutationAnalysisPlugin; +import ch.devcon5.sonar.plugins.mutationanalysis.metrics.ResourceMutationMetrics; +import ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; - -import ch.devcon5.sonar.plugins.mutationanalysis.MutationAnalysisPlugin; -import ch.devcon5.sonar.plugins.mutationanalysis.metrics.ResourceMutationMetrics; -import ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.batch.fs.FileSystem; @@ -43,8 +42,8 @@ import org.sonar.api.config.Configuration; /** - * Sonar sensor for pitest mutation coverage analysis. - * The pitest sensor supports Java and Kotlin as languages, both can be enabled/disabled separately and are + * Sonar sensor for pitest mutation coverage analysis. The pitest sensor supports Java and Kotlin as languages, both can + * be enabled/disabled separately and are */ public class PitestSensor implements Sensor { @@ -66,16 +65,11 @@ public class PitestSensor implements Sensor { /** * Constructor that is invoked by Sonar to create the sensor instance. * - * @param configuration - * the Configuration for the Pitest Sonar plugin - * @param rulesProfile - * the active rules profile containing all the active rules. - * @param fileSystem - * the FileSystem reference to access the project resources + * @param configuration the Configuration for the Pitest Sonar plugin + * @param rulesProfile the active rules profile containing all the active rules. + * @param fileSystem the FileSystem reference to access the project resources */ - public PitestSensor(final Configuration configuration, final ActiveRules rulesProfile, final FileSystem - fileSystem) { - + public PitestSensor(final Configuration configuration, final ActiveRules rulesProfile, final FileSystem fileSystem) { this.resourceResolver = new ResourceResolver(fileSystem); this.settings = configuration; this.rulesProcessor = new RulesProcessor(configuration, rulesProfile); @@ -86,51 +80,44 @@ public PitestSensor(final Configuration configuration, final ActiveRules rulesPr @Override public void describe(final SensorDescriptor descriptor) { - descriptor.name("Mutation Analysis"); - descriptor.onlyOnLanguages(toArray("java", "kotlin")); descriptor.createIssuesForRuleRepositories(toArray(REPOSITORY_KEY + ".java", REPOSITORY_KEY + ".kotlin")); - } /** - * Helper method to produce an array out of two items without creating unkillable mutations. - * - * a sane implementation would simply call a varargs method such as descriptor.onlyOnLanguages("java", "kotlin") - * - * unfortunately the varargs produce un-killable mutations which was tried to avoid for the plugin - * the list in combination with the stream to produce an array has no unkillable mutations - * the list has no varargs (unlike Arrays.asList()) - * the stream to array requires no size argument that can be mutated + * Helper method to produce an array out of two items without creating un-killable mutations. + *

+ * a sane implementation would simply call a varargs method such as descriptor.onlyOnLanguages("java", "kotlin") + *

+ * unfortunately the varargs produce un-killable mutations which was tried to avoid for the plugin the list in + * combination with the stream to produce an array has no un-killable mutations the list has no varargs (unlike + * Arrays.asList()) the stream to array requires no size argument that can be mutated */ private String[] toArray(String element1, String element2) { final List list = new ArrayList<>(); list.add(element1); list.add(element2); - return list.stream().toArray(String[]::new); + return list.toArray(new String[0]); } - private List getLanguageKeys(){ - + private List getLanguageKeys() { final List keys = new ArrayList<>(); - if( settings.getBoolean(MutationAnalysisPlugin.PITEST_JAVA_SENSOR_ENABLED).orElse(true)){ + if (settings.getBoolean(MutationAnalysisPlugin.PITEST_JAVA_SENSOR_ENABLED).orElse(true)) { keys.add("java"); } - if( settings.getBoolean(MutationAnalysisPlugin.PITEST_KOTLIN_SENSOR_ENABLED).orElse(true)){ + if (settings.getBoolean(MutationAnalysisPlugin.PITEST_KOTLIN_SENSOR_ENABLED).orElse(true)) { keys.add("kotlin"); } LOG.debug("Enabled Languages for Pitest: {}", keys); - return keys; } - @Override public void execute(final SensorContext context) { - if (isEnabled()) { - LOG.info("Pitest Sensor {} running on {} in {}", getLanguageKeys(), context.project(), context.fileSystem().baseDir()); + LOG.info("Pitest Sensor {} running on {} in {}", getLanguageKeys(), context.project(), + context.fileSystem().baseDir()); } else { LOG.info("Pitest Sensor {} disabled", getLanguageKeys()); return; @@ -147,7 +134,6 @@ public void execute(final SensorContext context) { getLanguageKeys().forEach(language -> { LOG.debug("applying {} rules", language); this.rulesProcessor.processRules(metrics, context, language); - }); LOG.debug("saving metrics"); @@ -163,38 +149,29 @@ public void execute(final SensorContext context) { } catch (final IOException e) { LOG.error("Could not read mutants", e); } - } private boolean isEnabled() { - return !getLanguageKeys().isEmpty(); } /** * Collect the metrics per resource (from the context) for the given mutants found on the project. * - * @param mutants - * the mutants found in by PIT - * + * @param mutants the mutants found in the resource by PIT * @return */ private Collection collectMetrics(final Collection mutants) { - final Map metricsByResource = new HashMap<>(); - for (final Mutant mutant : mutants) { - this.resourceResolver.resolve(mutant.getMutatedClass()) - .ifPresent(file -> metricsByResource.computeIfAbsent(file, ResourceMutationMetrics::new).addMutant(mutant)); - + .ifPresent(file -> metricsByResource.computeIfAbsent(file, ResourceMutationMetrics::new).addMutant(mutant)); } return metricsByResource.values(); } @Override public String toString() { - return getClass().getSimpleName(); } diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/ReportCollector.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/ReportCollector.java index 3297c7a..c2f456a 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/ReportCollector.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/ReportCollector.java @@ -25,9 +25,9 @@ import static javax.xml.xpath.XPathConstants.STRING; import static org.slf4j.LoggerFactory.getLogger; -import javax.xml.xpath.XPath; -import javax.xml.xpath.XPathExpressionException; -import javax.xml.xpath.XPathFactory; +import ch.devcon5.sonar.plugins.mutationanalysis.MutationAnalysisPlugin; +import ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant; +import ch.devcon5.sonar.plugins.mutationanalysis.report.Reports; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; @@ -46,10 +46,9 @@ import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; - -import ch.devcon5.sonar.plugins.mutationanalysis.MutationAnalysisPlugin; -import ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant; -import ch.devcon5.sonar.plugins.mutationanalysis.report.Reports; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; import org.slf4j.Logger; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.sensor.SensorContext; @@ -78,14 +77,12 @@ are supported (i.e. from the JS world), it might be better to split support for private final XPath xpath; public ReportCollector(final Configuration configuration, FileSystem fileSystem) { - this.settings = configuration; this.fileSystem = fileSystem; this.xpath = XPathFactory.newInstance().newXPath(); } public Collection collectGlobalMutants(final SensorContext context) { - final Collection globalMutants; if (MutationAnalysisPlugin.isExperimentalFeaturesEnabled(this.settings)) { globalMutants = collectReports(context); @@ -99,45 +96,40 @@ public Collection collectGlobalMutants(final SensorContext context) { * Reads the Mutants from the PIT reports for the current maven project the sensor analyzes * * @return a collection of all mutants found in the reports. If the report could not be located, the list is empty. - * - * @throws IOException - * if the search for the report file failed + * @throws IOException if the search for the report file failed */ public Collection collectLocalMutants() throws IOException { - return Reports.readMutants(getReportDirectory()); } /** * Collects all mutation reports from all parent and sibling modules. This method assumes a standard maven layout and - * and a standard gradle layout + * a standard gradle layout * - * @param context + * @param context The context from which to gather the reports */ private Collection collectReports(final SensorContext context) { - - final Path root = getProjectRootFromSettings().orElseGet(() -> findProjectRoot(context.fileSystem().baseDir().toPath())); + final Path root = getProjectRootFromSettings().orElseGet( + () -> findProjectRoot(context.fileSystem().baseDir().toPath())); LOG.info("Using {} as project root", root); final String reportDirectoryPath = getReportDirectoryPath(); - return findModuleRoots(root).map(module -> module.resolve(reportDirectoryPath)).flatMap(this::readMutantsFromReport).collect(Collectors.toList()); + return findModuleRoots(root).map(module -> module.resolve(reportDirectoryPath)).flatMap(this::readMutantsFromReport) + .collect(Collectors.toList()); } private Optional getProjectRootFromSettings() { - return settings.get(PROJECT_ROOT_FOLDER).map(Paths::get); } Path findProjectRoot(Path child) { - LOG.debug("Searching project root for {}", child); - return getRelativeParentPathFromPom(child).orElseGet(() -> getParentPathFromFilesystem(child).orElse(child)); } private Function> findRootInParents(final Path child) { return parentPath -> { - if(isMultiModuleParent(parentPath, child)) { + if (isMultiModuleParent(parentPath, child)) { LOG.debug("Path {} is parent module of {}", parentPath, child); return Optional.of(findProjectRoot(parentPath)); } else { @@ -148,27 +140,22 @@ private Function> findRootInParents(final Path child) { /** * Checks if the specified parent is a multi-module reactor pom or settings.gradle that contains the child module in - * it's module definition - * @param parentPath - * the path to the presumed multi-module parent pom or settings.gradle - * @param child - * the child that should be contained in the multi-module reactor pom or settings.gradle - * @return - * true if the the parentPath refers to a multi-module parent and the child is referenced in its - * modules list + * its module definition + * + * @param parentPath the path to the presumed multi-module parent pom or settings.gradle + * @param child the child that should be contained in the multi-module reactor pom or settings.gradle + * @return true if the parentPath refers to a multi-module parent and the child is referenced in its modules list */ private boolean isMultiModuleParent(final Path parentPath, final Path child) { - return getModulePaths(parentPath).stream().anyMatch(module -> isSamePath(child, module)); } /** - * Gets the parent for the child module from the folder structure of the filesystem. The parent path is checked - * if it's a parent module of the module defined by child - * @param child - * the child module for which the parent should be found - * @return - * the parent folder that is a multi-module module that defines the child-module in its module list + * Gets the parent for the child module from the folder structure of the filesystem. The parent path is checked if + * it's a parent module of the module defined by child + * + * @param child the child module for which the parent should be found + * @return the parent folder that is a multi-module module that defines the child-module in its module list */ private Optional getParentPathFromFilesystem(final Path child) { LOG.info("Could not determine project root of {} from parent", child); @@ -176,23 +163,21 @@ private Optional getParentPathFromFilesystem(final Path child) { } /** - * Evaluates the relative path definition - if present - from the pom file of the child. If the element is present - * and the parent exists, it's checked, whether the parent is a reactor module that defines the child in its - * modules list. - * @param child - * the path of the child module that should contain a pom file - * @return - * the parent module that defines the child in its modules list. - * if neithe the pom.xml nor the relativePath element are defined, or the parent does not define the child in its - * modules list, an empty optional is returned + * Evaluates the relative path definition - if present - from the pom file of the child. If the element is present and + * the parent exists, it's checked, whether the parent is a reactor module that defines the child in its modules + * list. + * + * @param child the path of the child module that should contain a pom file + * @return the parent module that defines the child in its modules list. if neither the pom.xml nor the relativePath + * element are defined, or the parent does not define the child in its modules list, an empty optional is returned */ private Optional getRelativeParentPathFromPom(final Path child) { return resolveExisting(child, POM_XML).flatMap(pomXml -> { try (InputStream is = Files.newInputStream(pomXml)) { final InputSource in = new InputSource(is); return Optional.ofNullable((String) this.xpath.evaluate(XPATH_RELATIVE_PARENT_PATH, in, STRING)) - .filter(relPath -> !relPath.isEmpty()) - .map(child::resolve); + .filter(relPath -> !relPath.isEmpty()) + .map(child::resolve); } catch (IOException | XPathExpressionException e) { LOG.debug("Could not parse pom {}", pomXml, e); return Optional.empty(); @@ -201,15 +186,13 @@ private Optional getRelativeParentPathFromPom(final Path child) { } /** - * Extracts all module definition from the current module. The module definitions can either be defined in a - * reactor pom.xml or a settings.gradle file. If both exists, both are evaluated. - * @param parentPath - * the path of the multi-module root folder - * @return - * a list of all child module paths + * Extracts all module definition from the current module. The module definitions can either be defined in a reactor + * pom.xml or a settings.gradle file. If both exists, both are evaluated. + * + * @param parentPath the path of the multi-module root folder + * @return a list of all child module paths */ private List getModulePaths(final Path parentPath) { - final SortedSet pathSet = new TreeSet<>(); //checking both maven and gradle module and retaining unique modules @@ -224,27 +207,23 @@ private List getModulePaths(final Path parentPath) { /** * Resolves the relativePath relative to the root path and checks if the resolved path exists. - * @param root - * the root path from which the relative path should be resolved - * @param relativePath - * the path relative to the root - * @return - * the the resolved path if it exists or an empty optional if it doesn't exist + * + * @param root the root path from which the relative path should be resolved + * @param relativePath the path relative to the root + * @return the resolved path if it exists or an empty optional if it doesn't exist */ - private Optional resolveExisting(Path root, String relativePath){ + private Optional resolveExisting(Path root, String relativePath) { //we don't need to run an existing check on the file, as any access on it will also result in an optional //and could therefore handle any cases where the file doesn't exist in its exception handling return Optional.of(root.resolve(relativePath)); } private List getModulePathsForMaven(Path configurationFilePath) { - final Path parent = configurationFilePath.getParent(); final List modulePaths = new ArrayList<>(); - try (InputStream is = Files.newInputStream(configurationFilePath)) { final InputSource in = new InputSource(is); - //TODO add support for profile-activated modules + // TODO add support for profile-activated modules final NodeList modules = (NodeList) this.xpath.evaluate(XPATH_MODULE, in, NODESET); //creating a pre-sized list is - mutation wise - equivalent to creating the list without size hint //we choose the less efficient way of not pre-sizing the array because this kills another mutant @@ -254,7 +233,7 @@ private List getModulePathsForMaven(Path configurationFilePath) { } return modulePaths.stream().map(parent::resolve).collect(Collectors.toList()); } catch (IOException | XPathExpressionException e) { - LOG.debug("Could not resolve module paths for pom {}",configurationFilePath, e); + LOG.debug("Could not resolve module paths for pom {}", configurationFilePath, e); return Collections.emptyList(); } } @@ -276,9 +255,8 @@ private List getModulePathsForGradle(Path configurationFilePath) { } } - //package protected visibilty for testing exception handling + //package protected visibility for testing exception handling Stream readMutantsFromReport(final Path reportPath) { - Stream result; try { result = Reports.readMutants(reportPath).stream(); @@ -291,18 +269,16 @@ Stream readMutantsFromReport(final Path reportPath) { } private String getReportDirectoryPath() { - - return settings.get(MutationAnalysisPlugin.REPORT_DIRECTORY_KEY).orElse(MutationAnalysisPlugin.REPORT_DIRECTORY_DEF); + return settings.get(MutationAnalysisPlugin.REPORT_DIRECTORY_KEY) + .orElse(MutationAnalysisPlugin.REPORT_DIRECTORY_DEF); } private Stream findModuleRoots(final Path root) { - return Stream.concat(Stream.of(root), getModulePaths(root).stream().flatMap(this::findModuleRoots)); } - //package protected visibilty for testing exception handling + //package protected visibility for testing exception handling boolean isSamePath(final Path child, final Path module) { - boolean result; try { result = Files.isSameFile(module, child); @@ -316,12 +292,13 @@ boolean isSamePath(final Path child, final Path module) { /** * Determine the absolute path of the directory where the PIT reports are located. The path is assembled using the - * base directory of the fileSystem and the reports directory configured in the plugin's {@link org.sonar.api.config.Settings}. + * base directory of the fileSystem and the reports directory configured in the plugin's + * {@link org.sonar.api.config.Settings}. * * @return the path to PIT reports directory */ private Path getReportDirectory() { - return fileSystem.baseDir().toPath().resolve(getReportDirectoryPath()); } + } diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/ResourceResolver.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/ResourceResolver.java index 015034a..bf9236d 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/ResourceResolver.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/ResourceResolver.java @@ -21,38 +21,36 @@ package ch.devcon5.sonar.plugins.mutationanalysis.sensors; import java.util.Optional; - import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; /** - * Resolver that resolves any given Java or Kotlin class to its source class. - * If the class is a nested class, than it's parent is returned + * Resolver that resolves any given Java or Kotlin class to its source class. If the class is a nested class, + * then its parent is returned */ public class ResourceResolver { - private FileSystem fs; - - public ResourceResolver(final FileSystem fs) { - this.fs = fs; - } - - public Optional resolve(String classname) { - - return Optional.ofNullable(fs.inputFile(fs.predicates() - .or(fs.predicates().matchesPathPattern("**/" + getPathToSourceFile(classname, "java")), - fs.predicates().matchesPathPattern("**/" + getPathToSourceFile(classname, "kt"))))); - } - - private String getPathToSourceFile(String classname, String language) { - final int nestedClass = classname.indexOf('$'); - final String mainClass; - if (nestedClass != -1) { - mainClass = classname.substring(0, nestedClass); - } else { - mainClass = classname; - } - return mainClass.trim().replace(".","/") + "." + language; - } + private final FileSystem fs; + + public ResourceResolver(final FileSystem fs) { + this.fs = fs; + } + + public Optional resolve(String classname) { + return Optional.ofNullable(fs.inputFile(fs.predicates() + .or(fs.predicates().matchesPathPattern("**/" + getPathToSourceFile(classname, "java")), + fs.predicates().matchesPathPattern("**/" + getPathToSourceFile(classname, "kt"))))); + } + + private String getPathToSourceFile(String classname, String language) { + final int nestedClass = classname.indexOf('$'); + final String mainClass; + if (nestedClass != -1) { + mainClass = classname.substring(0, nestedClass); + } else { + mainClass = classname; + } + return mainClass.trim().replace(".", "/") + "." + language; + } } diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/RulesProcessor.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/RulesProcessor.java index 25eb463..d8a0676 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/RulesProcessor.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/RulesProcessor.java @@ -23,14 +23,13 @@ import static ch.devcon5.sonar.plugins.mutationanalysis.rules.MutationAnalysisRulesDefinition.PARAM_MUTANT_COVERAGE_THRESHOLD; import static org.slf4j.LoggerFactory.getLogger; -import java.text.DecimalFormat; -import java.util.Collection; -import java.util.Optional; - import ch.devcon5.sonar.plugins.mutationanalysis.MutationAnalysisPlugin; import ch.devcon5.sonar.plugins.mutationanalysis.metrics.ResourceMutationMetrics; import ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant; import ch.devcon5.sonar.plugins.mutationanalysis.rules.MutationAnalysisRulesDefinition; +import java.text.DecimalFormat; +import java.util.Collection; +import java.util.Optional; import org.slf4j.Logger; import org.sonar.api.batch.rule.ActiveRule; import org.sonar.api.batch.rule.ActiveRules; @@ -44,256 +43,202 @@ */ public class RulesProcessor { - private static final Logger LOG = getLogger(RulesProcessor.class); - - private final Configuration settings; - - /** - * the rules profile containing the currently active rule. - */ - private final ActiveRules rulesProfile; - - public RulesProcessor(final Configuration configuration, final ActiveRules rulesProfile) { - this.settings = configuration; - this.rulesProfile = rulesProfile; - - } - - /** - * Applies the active rules to the resources based on each resource's metrics. - * - * @param metrics - * the metrics for each individual resource - * @param context - * the current sensor context - */ - public void processRules(final Collection metrics, final SensorContext context, String language) { - final Collection activeRules = this.rulesProfile.findByRepository(MutationAnalysisRulesDefinition.REPOSITORY_KEY + "." + language); - - if (activeRules.isEmpty()) { - // ignore violations from report, if rule not activated in Sonar - LOG.warn( - "/!\\ At least one Mutation Analysis rule needs to be activated for the current profile and language: {}.", - language); - } - - metrics.stream() - .filter(resourceMetrics -> language.equals(resourceMetrics.getResource().language())) - .forEach(resourceMetrics -> applyRules(resourceMetrics, activeRules, context)); - } - - /** - * Applies the active rules on resource metrics. - * - * @param resourceMetrics - * the mutants for found for the issuable - * @param activeRules - * the active rules to apply - * @param context - * the current sensor context - */ - private void applyRules(final ResourceMutationMetrics resourceMetrics, final Collection activeRules, final SensorContext context) { - - for (final ActiveRule rule : activeRules) { - applyRule(resourceMetrics, rule, context); - } - } - - /** - * Applies the active rule on the issuable if any of the resource metrics for the issuable violates the rule - * - * @param resourceMetrics - * the metrics for the Resource - * @param rule - * the active rule to apply - * @param context - * the current sensor context - */ - private void applyRule(final ResourceMutationMetrics resourceMetrics, final ActiveRule rule, final SensorContext context) { - - applyThresholdRule(resourceMetrics, rule, context); - - applyMutantRule(resourceMetrics, rule, context); - - } - - /** - * Creates a the mutation coverage threshold issue if the active rule is the Mutation Coverage rule. - * - * @param resourceMetrics - * the issuable on which to apply the rule - * @param rule - * the metrics for the resource behind the issuable - * @param context - * the rule to apply. - * - * @return true if the rule was the mutation coverage rule and the rule have been applied or - * false if it was another rule - */ - private void applyThresholdRule(final ResourceMutationMetrics resourceMetrics, final ActiveRule rule, final SensorContext context) { - - //we can skip the check whether the current rule is the coverage_threshold rule - //because if it's not, then it won't have the coverage threshold parameter, defaulting to 0 - //an issue is only created if the actual coverage is less than the threshold, which is not possible with 0 - - final double actualCoverage = resourceMetrics.getMutationCoverage(); - final double threshold = Double.parseDouble(Optional.ofNullable(rule.param(PARAM_MUTANT_COVERAGE_THRESHOLD)) - .orElse("0.0")); - - if (resourceMetrics.getMutationCoverage() < threshold) { - - final double minimumKilledMutants = resourceMetrics.getMutationsTotal() * threshold / 100.0; - final double additionalRequiredMutants = Math.ceil(minimumKilledMutants - resourceMetrics.getMutationsKilled()); - - NewIssue newIssue = context.newIssue().forRule(rule.ruleKey()); - newIssue.gap(settings.getDouble(MutationAnalysisPlugin.EFFORT_FACTOR_MISSING_COVERAGE).orElse(1.0) * additionalRequiredMutants) - .at(newIssue.newLocation().on(resourceMetrics.getResource()).message(generateThresholdViolationMessage(actualCoverage, threshold, additionalRequiredMutants))) - .save(); - } - } - - private String generateThresholdViolationMessage(final double actualCoverage, final double threshold, final double additionalRequiredMutants) { - // - // this method's implementation is a demonstration of how a 'killing spree' might affect your code. - // a sane implementation would simply use String.format - // - // String.format("%.0f more mutants need to be killed to get the mutation coverage from %.1f%% to %.1f%%",new Object[]{additionalRequiredMutants,actualCoverage,threshold}) - // - // But this creates an un-killable mutant through the varargs parameter it takes (something like Substituted 3 -> 4) - // Secondly, despite best practices the StringBuilder is not initialized with an initial size hint as the value of - // size hint is also an un-killable mutant as it only affects the resizing of the backing array but has no impact to the outcome - // - final DecimalFormat noDecimalPlace = new DecimalFormat("#"); - final DecimalFormat oneDecimalPlace = new DecimalFormat("#.0"); - return new StringBuilder().append(noDecimalPlace.format(additionalRequiredMutants)) - .append(" more mutants need to be killed to get the mutation coverage from ") - .append(oneDecimalPlace.format(actualCoverage)) - .append('%') - .append(" to ") - .append(oneDecimalPlace.format(threshold)) - .append('%') - .toString(); - } - - /** - * Applies mutant specific rule on each mutant captured in the resource metric. For each mutant assigned to the - * resource, it is checked if it violates:

  • the survived mutant rule
  • the uncovered mutant rule
  • - *
  • the unknown mutator status rule
  • any of the mutator specific rules
- * - * @param resourceMetrics - * the resource metric containing the resource that might have an issue and all mutants found for that - * resource - * @param rule - * the rule that might be violated - * @param context - * the current sensor context - */ - private void applyMutantRule(final ResourceMutationMetrics resourceMetrics, final ActiveRule rule, final SensorContext context) { - - for (final Mutant mutant : resourceMetrics.getMutants()) { - if (violatesSurvivedMutantRule(rule, mutant) - || violatesUncoveredMutantRule(rule, mutant) - || violatesUnknownMutantStatusRule(rule, mutant) - || violatesMutatorRule(rule, mutant)) { - - NewIssue newIssue = context.newIssue().forRule(rule.ruleKey()); - - NewIssueLocation newLocation = newIssue.newLocation().on(resourceMetrics.getResource()) - .at(resourceMetrics.getResource().selectLine(mutant.getLineNumber())) - .message(getViolationDescription(mutant)); - - newIssue.gap(settings.getDouble(MutationAnalysisPlugin.EFFORT_FACTOR_SURVIVED_MUTANT).orElse(1.0)) - .at(newLocation) - .save(); - } - } - } - - /** - * Checks if the rule if the Survived Mutant rule and if the mutant violates it - * - * @param rule - * the rule to verify - * @param mutant - * the mutant that might violate the rule - * - * @return true if the rule is violated - */ - private boolean violatesSurvivedMutantRule(final ActiveRule rule, final Mutant mutant) { - - return MutationAnalysisRulesDefinition.RULE_SURVIVED_MUTANT.equals(rule.ruleKey().rule()) - && (mutant.getState() == Mutant.State.SURVIVED - || mutant.getState() == Mutant.State.NO_COVERAGE); - } - - /** - * Checks if the rule is the Uncovered Mutant rule and if the mutant violates it - * - * @param rule - * the rule to verify - * @param mutant - * the mutant that might violate the rule - * - * @return true if the rule is violated - */ - private boolean violatesUncoveredMutantRule(final ActiveRule rule, final Mutant mutant) { - - return MutationAnalysisRulesDefinition.RULE_UNCOVERED_MUTANT.equals(rule.ruleKey().rule()) - && mutant.getState() == Mutant.State.NO_COVERAGE; - } - - /** - * Checks if the rule is the Unknown MutationOperator Status rule and if the mutant violates it - * - * @param rule - * the rule to verify - * @param mutant - * the mutant that might violate the rule - * - * @return true if the rule is violated - */ - private boolean violatesUnknownMutantStatusRule(final ActiveRule rule, final Mutant mutant) { - - return MutationAnalysisRulesDefinition.RULE_UNKNOWN_MUTANT_STATUS.equals(rule.ruleKey().rule()) - && mutant.getState() == Mutant.State.UNKNOWN; - } - - /** - * Checks if the active rule is a mutator-specific rule and if the mutant violates it. - * - * @param rule - * the rule to verify - * @param mutant - * the mutant that might violate the rule - * - * @return true if the rule is violated - */ - private boolean violatesMutatorRule(final ActiveRule rule, final Mutant mutant) { - - return rule.ruleKey() - .rule() - .startsWith(MutationAnalysisRulesDefinition.MUTANT_RULES_PREFIX + mutant.getMutationOperator().getId()) - && mutant.getState().isAlive(); - } - - /** - * Gets the mutant specific violation description of the mutator of the mutant - * - * @param mutant - * the mutant to receive the violation description - * - * @return the description as string - */ - private String getViolationDescription(final Mutant mutant) { - - final StringBuilder message = new StringBuilder(mutant.getMutationOperator().getViolationDescription()); - - mutant.getDescription().ifPresent(desc -> message.append(" Mutation: ").append(desc)); - - if (!mutant.getMutatorSuffix().isEmpty()) { - message.append(" (").append(mutant.getMutatorSuffix()).append(')'); + private static final Logger LOG = getLogger(RulesProcessor.class); + + private final Configuration settings; + + /** + * the rules profile containing the currently active rule. + */ + private final ActiveRules rulesProfile; + + public RulesProcessor(final Configuration configuration, final ActiveRules rulesProfile) { + this.settings = configuration; + this.rulesProfile = rulesProfile; + } + + /** + * Applies the active rules to the resources based on each resource's metrics. + * + * @param metrics the metrics for each individual resource + * @param context the current sensor context + */ + public void processRules(final Collection metrics, final SensorContext context, String language) { + final Collection activeRules = this.rulesProfile.findByRepository( + MutationAnalysisRulesDefinition.REPOSITORY_KEY + "." + language); + if (activeRules.isEmpty()) { + // ignore violations from report, if rule not activated in Sonar + LOG.warn( + "/!\\ At least one Mutation Analysis rule needs to be activated for the current profile and language: {}.", + language); + } + + metrics.stream() + .filter(resourceMetrics -> language.equals(resourceMetrics.getResource().language())) + .forEach(resourceMetrics -> applyRules(resourceMetrics, activeRules, context)); + } + + /** + * Applies the active rules on resource metrics. + * + * @param resourceMetrics the mutants for found for the issuable + * @param activeRules the active rules to apply + * @param context the current sensor context + */ + private void applyRules(final ResourceMutationMetrics resourceMetrics, final Collection activeRules, final SensorContext context) { + for (final ActiveRule rule : activeRules) { + applyRule(resourceMetrics, rule, context); + } + } + + /** + * Applies the active rule on the issuable if any of the resource metrics for the issuable violates the rule + * + * @param resourceMetrics the metrics for the Resource + * @param rule the active rule to apply + * @param context the current sensor context + */ + private void applyRule(final ResourceMutationMetrics resourceMetrics, final ActiveRule rule, final SensorContext context) { + applyThresholdRule(resourceMetrics, rule, context); + applyMutantRule(resourceMetrics, rule, context); + } + + /** + * Creates the mutation coverage threshold issue if the active rule is the Mutation Coverage rule. + * + * @param resourceMetrics the issuable on which to apply the rule + * @param rule the metrics for the resource behind the issuable + * @param context the rule to apply. + */ + private void applyThresholdRule(final ResourceMutationMetrics resourceMetrics, final ActiveRule rule, final SensorContext context) { + //we can skip the check whether the current rule is the coverage_threshold rule + //because if it's not, then it won't have the coverage threshold parameter, defaulting to 0 + //an issue is only created if the actual coverage is less than the threshold, which is not possible with 0 + final double actualCoverage = resourceMetrics.getMutationCoverage(); + final double threshold = Double.parseDouble(Optional.ofNullable(rule.param(PARAM_MUTANT_COVERAGE_THRESHOLD)).orElse("0.0")); + if (resourceMetrics.getMutationCoverage() < threshold) { + final double minimumKilledMutants = resourceMetrics.getMutationsTotal() * threshold / 100.0d; + final double additionalRequiredMutants = Math.ceil(minimumKilledMutants - resourceMetrics.getMutationsKilled()); + NewIssue newIssue = context.newIssue().forRule(rule.ruleKey()); + newIssue.gap(settings.getDouble(MutationAnalysisPlugin.EFFORT_FACTOR_MISSING_COVERAGE) + .orElse(1.0) * additionalRequiredMutants) + .at(newIssue.newLocation().on(resourceMetrics.getResource()) + .message(generateThresholdViolationMessage(actualCoverage, threshold, additionalRequiredMutants))) + .save(); + } + } + + private String generateThresholdViolationMessage(final double actualCoverage, final double threshold, final double additionalRequiredMutants) { + // this method's implementation is a demonstration of how a 'killing spree' might affect your code. + // a sane implementation would simply use String.format + // + // String.format("%.0f more mutants need to be killed to get the mutation coverage from %.1f%% to %.1f%%",new Object[]{additionalRequiredMutants,actualCoverage,threshold}) + // + // But this creates an un-killable mutant through the varargs parameter it takes (something like Substituted 3 -> 4) + // Secondly, despite best practices the StringBuilder is not initialized with an initial size hint as the value of + // size hint is also an un-killable mutant as it only affects the resizing of the backing array but has no impact to the outcome + // + final DecimalFormat noDecimalPlace = new DecimalFormat("#"); + final DecimalFormat oneDecimalPlace = new DecimalFormat("#.0"); + return new StringBuilder().append(noDecimalPlace.format(additionalRequiredMutants)) + .append(" more mutants need to be killed to get the mutation coverage from ") + .append(oneDecimalPlace.format(actualCoverage)) + .append('%') + .append(" to ") + .append(oneDecimalPlace.format(threshold)) + .append('%') + .toString(); + } + + /** + * Applies mutant specific rule on each mutant captured in the resource metric. For each mutant assigned to the + * resource, it is checked if it violates:
  • the survived mutant rule
  • the uncovered mutant rule
  • + *
  • the unknown mutator status rule
  • any of the mutator specific rules
+ * + * @param resourceMetrics the resource metric containing the resource that might have an issue and all mutants found + * for that resource + * @param rule the rule that might be violated + * @param context the current sensor context + */ + private void applyMutantRule(final ResourceMutationMetrics resourceMetrics, final ActiveRule rule, final SensorContext context) { + for (final Mutant mutant : resourceMetrics.getMutants()) { + if (violatesSurvivedMutantRule(rule, mutant) || violatesUncoveredMutantRule(rule, mutant) + || violatesUnknownMutantStatusRule(rule, mutant) || violatesMutatorRule(rule, mutant)) { + NewIssue newIssue = context.newIssue().forRule(rule.ruleKey()); + NewIssueLocation newLocation = newIssue.newLocation().on(resourceMetrics.getResource()) + .at(resourceMetrics.getResource().selectLine(mutant.getLineNumber())) + .message(getViolationDescription(mutant)); + newIssue.gap(settings.getDouble(MutationAnalysisPlugin.EFFORT_FACTOR_SURVIVED_MUTANT).orElse(1.0)) + .at(newLocation) + .save(); } - return message.toString(); - } + } + } + + /** + * Checks if the rule is the Survived Mutant rule and if the mutant violates it + * + * @param rule the rule to verify + * @param mutant the mutant that might violate the rule + * @return true if the rule is violated + */ + private boolean violatesSurvivedMutantRule(final ActiveRule rule, final Mutant mutant) { + return MutationAnalysisRulesDefinition.RULE_SURVIVED_MUTANT.equals(rule.ruleKey().rule()) + && (mutant.getState() == Mutant.State.SURVIVED + || mutant.getState() == Mutant.State.NO_COVERAGE); + } + + /** + * Checks if the rule is the Uncovered Mutant rule and if the mutant violates it + * + * @param rule the rule to verify + * @param mutant the mutant that might violate the rule + * @return true if the rule is violated + */ + private boolean violatesUncoveredMutantRule(final ActiveRule rule, final Mutant mutant) { + return MutationAnalysisRulesDefinition.RULE_UNCOVERED_MUTANT.equals(rule.ruleKey().rule()) + && mutant.getState() == Mutant.State.NO_COVERAGE; + } + + /** + * Checks if the rule is the Unknown MutationOperator Status rule and if the mutant violates it + * + * @param rule the rule to verify + * @param mutant the mutant that might violate the rule + * @return true if the rule is violated + */ + private boolean violatesUnknownMutantStatusRule(final ActiveRule rule, final Mutant mutant) { + return MutationAnalysisRulesDefinition.RULE_UNKNOWN_MUTANT_STATUS.equals(rule.ruleKey().rule()) + && mutant.getState() == Mutant.State.UNKNOWN; + } + + /** + * Checks if the active rule is a mutator-specific rule and if the mutant violates it. + * + * @param rule the rule to verify + * @param mutant the mutant that might violate the rule + * @return true if the rule is violated + */ + private boolean violatesMutatorRule(final ActiveRule rule, final Mutant mutant) { + return rule.ruleKey() + .rule() + .startsWith(MutationAnalysisRulesDefinition.MUTANT_RULES_PREFIX + mutant.getMutationOperator().getId()) + && mutant.getState().isAlive(); + } + + /** + * Gets the mutant specific violation description of the mutator of the mutant + * + * @param mutant the mutant to receive the violation description + * @return the description as string + */ + private String getViolationDescription(final Mutant mutant) { + final StringBuilder message = new StringBuilder(mutant.getMutationOperator().getViolationDescription()); + mutant.getDescription().ifPresent(desc -> message.append(" Mutation: ").append(desc)); + if (!mutant.getMutatorSuffix().isEmpty()) { + message.append(" (").append(mutant.getMutatorSuffix()).append(')'); + } + return message.toString(); + } } diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/SourceMetricsWriter.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/SourceMetricsWriter.java index 5659e54..b5e3d5d 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/SourceMetricsWriter.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/SourceMetricsWriter.java @@ -22,11 +22,10 @@ import static org.slf4j.LoggerFactory.getLogger; -import java.util.Collection; - import ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics; import ch.devcon5.sonar.plugins.mutationanalysis.metrics.ResourceMutationMetrics; import ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant; +import java.util.Collection; import org.slf4j.Logger; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.sensor.SensorContext; @@ -42,51 +41,41 @@ public class SourceMetricsWriter { /** * Saves the information of the mutants the sensors context. * - * @param metrics - * the mutant information parsed from the PIT report - * @param context - * the current {@link org.sonar.api.batch.sensor.SensorContext} - * @param globalMutants + * @param metrics the mutant information parsed from the PIT report + * @param context the current {@link org.sonar.api.batch.sensor.SensorContext} + * @param globalMutants the global collection of mutants */ public void writeMetrics(final Collection metrics, final SensorContext context, final Collection globalMutants) { - final int total = globalMutants.isEmpty() ? sumTotal(metrics) : globalMutants.size(); final int alive = total - (globalMutants.isEmpty() - ? metrics.stream().mapToInt(rmm -> countDetected(rmm.getMutants())).sum() - : countDetected(globalMutants)); - + ? metrics.stream().mapToInt(rmm -> countDetected(rmm.getMutants())).sum() + : countDetected(globalMutants)); for (final ResourceMutationMetrics resourceMetrics : metrics) { saveResourceMetrics(resourceMetrics, context); - - context.newMeasure().forMetric(MutationMetrics.UTILITY_GLOBAL_MUTATIONS).on(resourceMetrics.getResource()).withValue(total).save(); - context.newMeasure().forMetric(MutationMetrics.UTILITY_GLOBAL_ALIVE).on(resourceMetrics.getResource()).withValue(alive).save(); + context.newMeasure().forMetric(MutationMetrics.UTILITY_GLOBAL_MUTATIONS).on(resourceMetrics.getResource()) + .withValue(total).save(); + context.newMeasure().forMetric(MutationMetrics.UTILITY_GLOBAL_ALIVE).on(resourceMetrics.getResource()) + .withValue(alive).save(); } } private int countDetected(final Collection c) { - return (int) c.stream().filter(Mutant::isDetected).count(); } private int sumTotal(final Collection metrics) { - return (int) metrics.stream().mapToLong(ResourceMutationMetrics::getMutationsTotal).sum(); } /** * Saves the {@link Mutant} metrics for the given resource in the SonarContext * - * @param resourceMetrics - * the actual metrics for the resource to persist - * @param context - * the context to register the metrics + * @param resourceMetrics the actual metrics for the resource to persist + * @param context the context to register the metrics */ private void saveResourceMetrics(final ResourceMutationMetrics resourceMetrics, final SensorContext context) { - final InputFile resource = resourceMetrics.getResource(); - LOG.debug("Saving resource metrics for {}", resource); - if (resourceMetrics.getMutationsKilled() > 0) { final NewCoverage newCov = context.newCoverage().onFile(resource); for (Mutant m : resourceMetrics.getMutants()) { @@ -97,17 +86,27 @@ private void saveResourceMetrics(final ResourceMutationMetrics resourceMetrics, newCov.save(); } if (resource.type() == InputFile.Type.MAIN) { - - context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_TOTAL).withValue(resourceMetrics.getMutationsTotal()).save(); - context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_NO_COVERAGE).withValue(resourceMetrics.getMutationsNoCoverage()).save(); - context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_KILLED).withValue(resourceMetrics.getMutationsKilled()).save(); - context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_SURVIVED).withValue(resourceMetrics.getMutationsSurvived()).save(); - context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_ALIVE).withValue(resourceMetrics.getMutationsTotal() - resourceMetrics.getMutationsDetected()).save(); - context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_MEMORY_ERROR).withValue(resourceMetrics.getMutationsMemoryError()).save(); - context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_TIMED_OUT).withValue(resourceMetrics.getMutationsTimedOut()).save(); - context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_UNKNOWN).withValue(resourceMetrics.getMutationsUnknown()).save(); - context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_DETECTED).withValue(resourceMetrics.getMutationsDetected()).save(); - context.newMeasure().on(resource).forMetric(MutationMetrics.TEST_TOTAL_EXECUTED).withValue(resourceMetrics.getNumTestsRun()).save(); + context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_TOTAL) + .withValue(resourceMetrics.getMutationsTotal()).save(); + context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_NO_COVERAGE) + .withValue(resourceMetrics.getMutationsNoCoverage()).save(); + context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_KILLED) + .withValue(resourceMetrics.getMutationsKilled()).save(); + context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_SURVIVED) + .withValue(resourceMetrics.getMutationsSurvived()).save(); + context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_ALIVE) + .withValue(resourceMetrics.getMutationsTotal() - resourceMetrics.getMutationsDetected()).save(); + context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_MEMORY_ERROR) + .withValue(resourceMetrics.getMutationsMemoryError()).save(); + context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_TIMED_OUT) + .withValue(resourceMetrics.getMutationsTimedOut()).save(); + context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_UNKNOWN) + .withValue(resourceMetrics.getMutationsUnknown()).save(); + context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_DETECTED) + .withValue(resourceMetrics.getMutationsDetected()).save(); + context.newMeasure().on(resource).forMetric(MutationMetrics.TEST_TOTAL_EXECUTED) + .withValue(resourceMetrics.getNumTestsRun()).save(); } } + } diff --git a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/TestMetricsWriter.java b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/TestMetricsWriter.java index bb32901..fe82ba2 100644 --- a/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/TestMetricsWriter.java +++ b/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/TestMetricsWriter.java @@ -22,15 +22,14 @@ import static org.slf4j.LoggerFactory.getLogger; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - import ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics; import ch.devcon5.sonar.plugins.mutationanalysis.metrics.ResourceMutationMetrics; import ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant; import ch.devcon5.sonar.plugins.mutationanalysis.model.TestDescriptor; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import org.slf4j.Logger; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.sensor.SensorContext; @@ -44,19 +43,15 @@ public class TestMetricsWriter { private final ResourceResolver resourceResolver; public TestMetricsWriter(final FileSystem fileSystem) { - this.resourceResolver = new ResourceResolver(fileSystem); - } public void writeMetrics(final Collection metrics, final SensorContext context, final Collection globalMutants) { - Map> testKills = metrics.stream() - .flatMap(rmm -> rmm.getMutants().stream()) - .collect(Collectors.groupingBy(m -> new TestDescriptor(m.getKillingTest()), Collectors.toList())); + .flatMap(rmm -> rmm.getMutants().stream()) + .collect(Collectors.groupingBy(m -> new TestDescriptor(m.getKillingTest()), Collectors.toList())); final int total = globalMutants.isEmpty() ? sumTotal(metrics) : globalMutants.size(); - testKills.forEach((t, m) -> { LOG.debug("Test {} kills {} mutants ", t.getClassName(), m.size()); this.resourceResolver.resolve(t.getClassName()).ifPresent(f -> { @@ -67,7 +62,7 @@ public void writeMetrics(final Collection metrics, fina } private int sumTotal(final Collection metrics) { - return (int) metrics.stream().mapToLong(ResourceMutationMetrics::getMutationsTotal).sum(); } + } diff --git a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/ARGUMENT_PROPAGATION.html b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/ARGUMENT_PROPAGATION.html index c223235..6739503 100644 --- a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/ARGUMENT_PROPAGATION.html +++ b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/ARGUMENT_PROPAGATION.html @@ -19,31 +19,33 @@ -->
-

Argument Propagation Mutator (ARGUMENT_PROPAGATION)

+

Argument Propagation Mutator (ARGUMENT_PROPAGATION)

-

If a method has a parameter whose type matches the return type, this - operator replaces the actual return value with the value of the parameter. - Hence it is only applicable to non-void methods. - For example:

+

+ This operator replaces the return value of a method parameter, whose type matches the return type, with the + value of the parameter. Hence, it is only applicable to non-void methods. +

+
+

For example:

-
-   public String aMethod() {
-     String aValue = "example";
-     return bMethod(aValue);
-   }
+  
+public String aMethod() {
+  String aValue = "example";
+  return bMethod(aValue);
+}
 
-   private String bMethod(String param) {
-     return param + " not mutated";
-   }
- 
+private String bMethod(String param) { + return param + " not mutated"; +} +
- is mutated to + is mutated to -
-   public String mutatedMethod() {
-     String aValue = "example";
-     return aValue;
-   }
- 
-

see Pitest Mutators

+
+public String mutatedMethod() {
+  String aValue = "example";
+  return aValue;
+}
+    
+

see Pitest Mutators

diff --git a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/BOOLEAN_FALSE_RETURN.html b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/BOOLEAN_FALSE_RETURN.html index 3742567..b772df1 100644 --- a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/BOOLEAN_FALSE_RETURN.html +++ b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/BOOLEAN_FALSE_RETURN.html @@ -19,5 +19,7 @@ -->
- This mutator replaced a primitive or boxed boolean with false. +

Boolean False Return Mutator (BOOLEAN_FALSE_RETURN)

+ +

This operator replaces a primitive or boxed boolean with false.

diff --git a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/BOOLEAN_TRUE_RETURN.html b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/BOOLEAN_TRUE_RETURN.html index da67d22..c31e957 100644 --- a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/BOOLEAN_TRUE_RETURN.html +++ b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/BOOLEAN_TRUE_RETURN.html @@ -18,6 +18,8 @@ ~ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. --> -
- This mutator replaced a primitive or boxed boolean with true. +
+

Boolean True Return Mutator (BOOLEAN_TRUE_RETURN)

+ +

This operator replaces a primitive or boxed boolean with true.

diff --git a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/CONDITIONALS_BOUNDARY.html b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/CONDITIONALS_BOUNDARY.html index 548e02b..441bee3 100644 --- a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/CONDITIONALS_BOUNDARY.html +++ b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/CONDITIONALS_BOUNDARY.html @@ -19,56 +19,58 @@ -->
-

Conditionals Boundary Mutator (CONDITIONALS_BOUNDARY)

+

Conditionals Boundary Mutator (CONDITIONALS_BOUNDARY)

-

This operator replaces relational operators <, <=, >, >= with their conditional boundary - counterparts according to the following table

+

+ This operator replaces relational operators <, <=, >, >= with their conditional boundary + counterparts according to the following table +

- - - - - - - - - - - - - - - - - - - - - - - - - -
- Original - - Mutation -
<<=
<=<
>>=
>=>
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ Original + + Mutation +
<<=
<=<
>>=
>=>
-

For example

+

For example

-
+  
 if (a <= b) {
   // some code
 }
-
+
-

will be mutated to

+

will be mutated to

-
+  
 if (a < b) {
   // someCode
 }
-
-

see Pitest Mutators

+
+

see Pitest Mutators

diff --git a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/CONSTRUCTOR_CALLS.html b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/CONSTRUCTOR_CALLS.html index ec83579..f53cffd 100644 --- a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/CONSTRUCTOR_CALLS.html +++ b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/CONSTRUCTOR_CALLS.html @@ -19,37 +19,43 @@ -->
-

Constructor Call Mutator (CONSTRUCTOR_CALLS)

+

Constructor Call Mutator (CONSTRUCTOR_CALLS)

-

This operator targets at constructors where it replaces the call to the constructor with a null value - assignment. This should induce NullPointerException. - Example:

+

+ This operator targets constructors, where it replaces the call to the constructor with a null value + assignment. This should induce NullPointerException. +

+
+

For Example:

-
+  
 public Object aMethod() {
   Example x = new Example();
   return x;
 }
-
+
-

will be mutated to

+

will be mutated to

-
+  
 public Example aMethod() {
   Example x = null;
   return x;
 }
-
- -

This mutation is very likely to cause NullPointerExceptions even in test suites - that do not explicitly test for null value. Nevertheless, even if the test contain no assertion at - all, if the test runs this code, it is likely to kill this mutation.

- -

Every other method invocation is not affected by this operator. See Void Method Call Mutator, which mutates - void methods, and - Non Void Method Call Mutator - mutating non-void methods.

- -

see Pitest Mutators

+
+ +

+ This mutation is very likely to cause NullPointerExceptions even in test suites + that do not explicitly test for null values. Nevertheless, even if the test contains no assertions + at all, if the test runs this code, it is likely to kill this mutation. +

+ +

+ Every other method invocation is not affected by this operator. See Void Method Call Mutator, which mutates + void methods, and + Non Void Method Call Mutator + mutating non-void methods. +

+

see Pitest Mutators

diff --git a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EMPTY_RETURN_VALUES.html b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EMPTY_RETURN_VALUES.html index 5eb0918..593afde 100644 --- a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EMPTY_RETURN_VALUES.html +++ b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EMPTY_RETURN_VALUES.html @@ -19,13 +19,19 @@ -->
- Mutates a return value which is no primitive to return an empty value that relates to the Object's type. - For example: +

Empty Return Values Mutator (EMPTY_RETURN_VALUES)

-
    -
  • 0 - Integer
  • -
  • Optional.empty()
  • -
  • "" - String
  • -
  • empty List
  • -
+

+ This operator mutates a return value, which is not a primitive, to return an empty value that relates to the + Object's type. +

+
+

For example:

+ +
    +
  • 0 - Integer
  • +
  • Optional.empty()
  • +
  • "" - String
  • +
  • empty List
  • +
diff --git a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_INLINE_CONSTS.html b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_INLINE_CONSTS.html index 5ab5a56..4e8f8bc 100644 --- a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_INLINE_CONSTS.html +++ b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_INLINE_CONSTS.html @@ -19,73 +19,73 @@ -->
-

Experimental Inline Constant Mutator (EXPERIMENTALINLINECONSTS)

+

Experimental Inline Constant Mutator (EXPERIMENTAL_INLINE_CONSTS)

-

This yet experimental operator aims to provide a more consistent behavior than the inline const operator. -

+

This experimental operator aims to provide a more consistent behavior than the inline const operator.

-

This operator increments the value by 1. In some cases this would lead to equivalent mutations due to JVM - internals, i.e. for floating point types adding 1 would lead to equivalent mutations.

+

+ This operator increments the value by 1. In some cases this would lead to equivalent mutations due to JVM + internals, i.e. for floating point types adding 1 would lead to equivalent mutations. +

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeMutation
boolean -
    -
  • true gets replaced with false and
  • -
  • false gets replaced with true
  • -
-
int byte short -
    -
  • 1 is replaces with 0,
  • -
  • -1 is replaced with 0,
  • -
  • all other values are incremented by 1.
  • -
  • Byte.MAX, Short.MAX and Integer.MAX are replaced with - Byte.MIN, Short.MIN and Integer.MIN
  • -
-
long -
    -
  • 1 gets replaced with 0
  • -
  • all other values are incremented by 1
  • -
-
float -
    -
  • replace 0.0 with 1.0
  • -
  • replace 1.0 with 2.0
  • -
  • replace any other value with 1.0
  • -
-
double -
    -
  • replace 0.0 with 1.0
  • -
  • replace 1.0 with 2.0
  • -
  • replace any other value with 1.0
  • -
-
-

see Pitest Mutators

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeMutation
boolean +
    +
  • replace true with false,
  • +
  • replace false with true
  • +
+
int byte short +
    +
  • replace 1 with 0,
  • +
  • replace -1 with 0,
  • +
  • all other values are incremented by 1.
  • +
  • Byte.MAX, Short.MAX and Integer.MAX are replaced with + Byte.MIN, Short.MIN and Integer.MIN
  • +
+
long +
    +
  • 1 is replaced with 0
  • +
  • all other values are incremented by 1
  • +
+
float +
    +
  • replace 0.0 with 1.0,
  • +
  • replace 1.0 with 2.0,
  • +
  • replace any other value with 1.0
  • +
+
double +
    +
  • replace 0.0 with 1.0,
  • +
  • replace 1.0 with 2.0,
  • +
  • replace any other value with 1.0
  • +
+
+

see Pitest Mutators

diff --git a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_MEMBER_VARIABLE.html b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_MEMBER_VARIABLE.html index 8aa02d0..cfea507 100644 --- a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_MEMBER_VARIABLE.html +++ b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_MEMBER_VARIABLE.html @@ -19,86 +19,89 @@ -->
-

Experimental Member Variable Mutator (EXPERIMENTALMEMBERVARIABLE)

+

Experimental Member Variable Mutator (EXPERIMENTAL_MEMBER_VARIABLE)

-

This operator mutates assignments to member variables, including final members, by removing the assignments. - Member variables will be initialized with their Java defaul values, depending on the type of the variable. -

+

+ This operator mutates assignments to member variables, including final members, by removing the assignments. + Member variables will be initialized with their Java default values, depending on the type of the variable. +

-

Java Default Values for Primitives and Reference Types

+

Java Default Values for Primitives and Reference Types

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeDefault Value
- boolean - false
- int - short - byte - long - 0
- float - double - 0.0
- char - '\u0000'
- Object - null
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeDefault Value
+ boolean + false
+ int + short + byte + long + 0
+ float + double + 0.0
+ char + '\u0000'
+ Object + null
+
+

For Example:

-

Example

- -
+  
 public class Example {
-    private final int a = 10;
-    private String text = "abc";
-    //...
+  private final int a = 10;
+  private String text = "abc";
+  //...
 }
-
+
-

will be mutated to

+

will be mutated to

-
-  public class Example {
-    private final int a = 0;
-    private String text = null;
-    ...
-  }
-
+
+public class Example {
+  private final int a = 0;
+  private String text = null;
+  ...
+}
+    
-

In cases where the member variable is explicitly initialized with a default value this operator will create an - equivalent mutation that can not be killed.

+

+ In cases where the member variable is explicitly initialized with a default value this operator will create an + equivalent mutation that can not be killed. +

-
+  
 public class EquivalentExample {
-    private boolean b = false;
+  private boolean b = false;
 }
-
-

see Pitest Mutators

+
+

see Pitest Mutators

diff --git a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_NAKED_RECEIVER.html b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_NAKED_RECEIVER.html index 0cbc5d7..cf769d0 100644 --- a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_NAKED_RECEIVER.html +++ b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_NAKED_RECEIVER.html @@ -19,23 +19,27 @@ -->
- Mutation operator that replaces a call to method with the receiver of the method (the instance on which the - method is invoked), if the return type of the method is the same as the receiver. -
- For example: +

Experimental Naked Receiver Mutator (EXPERIMENTAL_NAKED_RECEIVER)

-

-    public String originalMethod() {
-      String someString = "original";
-      return someString.toUpperCase();
-    }
+  

+ This operator replaces a call to a method with the receiver of the method (the instance on which the method + is invoked), if the return type of the method is the same as the receiver. +

+
+

For example:

+ +

+  public String originalMethod() {
+    String someString = "original";
+    return someString.toUpperCase();
+  }
     
- is mutated to -

-    public int mutatedMethod() {
-      String someString = "original";
-      return someString;
-    }
+  is mutated to
+  

+  public int mutatedMethod() {
+    String someString = "original";
+    return someString;
+  }
     
- as the return type of toUpperCase() is the same as the type of someString + as the return type of toUpperCase() is the same as the type of someString
diff --git a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_REMOVE_INCREMENTS.html b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_REMOVE_INCREMENTS.html index 330d37f..b6cce64 100644 --- a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_REMOVE_INCREMENTS.html +++ b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_REMOVE_INCREMENTS.html @@ -19,7 +19,10 @@ -->
-

Experimental Remove Increments Mutator (EXPERIMENTAL_REMOVE_INCREMENTS)

- This operator removed an increment operation. As the number of loop iterations are typically controlled by an - incremeted variable, this operator may lead to infinite loops causing timeouts. +

Experimental Remove Increments Mutator (EXPERIMENTAL_REMOVE_INCREMENTS)

+ +

+ This operator removes an increment operation. As the number of loop iterations are typically controlled by an + incremented variable, this operator may lead to infinite loops causing timeouts. +

diff --git a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_REMOVE_SWITCH.html b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_REMOVE_SWITCH.html index 8cfb00c..be9192f 100644 --- a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_REMOVE_SWITCH.html +++ b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_REMOVE_SWITCH.html @@ -19,10 +19,8 @@ -->
-

Experimental Remove Switch Mutator (EXPERIMENTAL_SWITCH)

+

Experimental Remove Switch Mutator (EXPERIMENTAL_SWITCH)

-

The entire switch statement gets removed and replaced with the default label. -

- -

see Pitest Mutators

+

This operator removes an entire switch statement and replaces it with the default label.

+

see Pitest Mutators

diff --git a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_RETURN_VALUES.html b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_RETURN_VALUES.html index 3cb217e..020d4df 100644 --- a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_RETURN_VALUES.html +++ b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_RETURN_VALUES.html @@ -19,14 +19,15 @@ -->
-

Experimental Return Values (EXPERIMENTAL_RETURN_VALUES)

+

Experimental Return Values (EXPERIMENTAL_RETURN_VALUES)

-

- The return values of method calls are mutated by this operator depending on the return type. -

+

+ This operator mutates the return values of method calls depending on the return type. +

-

Primitive types are replaces with rules similar to rules of the inline consts operator. Object reference - replacesments are more complex and handled by the ObjectReferenceReplacer. - -

see Pitest Mutators

+

+ Primitive types are replaces with rules similar to rules of the inline consts operator. Object reference + replacements are more complex and handled by the ObjectReferenceReplacer. +

+

see Pitest Mutators

diff --git a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_SWITCH.html b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_SWITCH.html index 7cea3f8..3937e6a 100644 --- a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_SWITCH.html +++ b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_SWITCH.html @@ -19,11 +19,11 @@ -->
-

Experimental Switch Mutator (EXPERIMENTAL_SWITCH)

+

Experimental Switch Mutator (EXPERIMENTAL_SWITCH)

-

This operator replaces the default label with the first label that is different to the default label and replaces - all other labels with the default label. -

- -

see Pitest Mutators

+

+ This operator replaces the default label with the first label that is different from the default label, and replaces + all other labels with the default label. +

+

see Pitest Mutators

diff --git a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/INCREMENTS.html b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/INCREMENTS.html index d1cf3bf..4ae033c 100644 --- a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/INCREMENTS.html +++ b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/INCREMENTS.html @@ -19,33 +19,34 @@ -->
-

Increments Mutator (INCREMENTS)

+

Increments Mutator (INCREMENTS)

-

- This operator replaces increments with decrements and vice versa. It mutates increments, decrements, assignment - increments and decrements of stack variables (local variables).

+

+ This operator replaces increments with decrements and vice versa. It mutates increments, decrements, assignment + increments and decrements of stack variables (local variables). +

+
+

For example

-

For example

- -
+  
 public int aMethod(int i) {
   i--;
   return i;
 }
-
+
-

will be mutated to

+

will be mutated to

-
+  
 public int aMethod(int i) {
   i++;
   return i;
 }
-
- -

Only increments and decrements of local variables are mutated. Member variables and the increments and - decrements thereof will be covered by the Math Mutator. -

+
-

see Pitest Mutators

+

+ Only increments and decrements of local variables are mutated. Member variables and the increments and + decrements thereof will be covered by the Math Mutator. +

+

see Pitest Mutators

diff --git a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/INLINE_CONSTS.html b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/INLINE_CONSTS.html index 7ad8fef..a5caa01 100644 --- a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/INLINE_CONSTS.html +++ b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/INLINE_CONSTS.html @@ -19,122 +19,127 @@ -->
-

Inline Constant Mutator (INLINE_CONSTS)

+

Inline Constant Mutator (INLINE_CONSTS)

-

An inline constant is a fixed value - a literal - that is assigned to a non-final variable. The inline - constant operator alters the value that is assigned to the variable. -

-

Example:

+

+ This operator mutates inline constants assigned to variables. An inline constant is a fixed value - a literal - + that is assigned to a non-final variable. +

+
+

For Example:

-
+  
 public void aMethod() {
-  long l = 16L;
-  // some coding that uses l
+    long l = 16L;
+    // some coding that uses l
 }
-
+
-

The actual mutation depends on the type of the inline constant. Because the ways Java statements get converted - to byte code may vary, the rule for mutating inline constant are complex. -

+

+ The actual mutation depends on the type of the inline constant. Because the way Java statements get converted + to byte code may vary, the rule for mutating inline constant are complex. +

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
TypeMutation
boolean - true gets replaced with false and - false gets replaced with true -
integer byte short -
    -
  • 1 gets replaced with 0
  • -
  • -1 gets replaced with 1
  • -
  • 5 gets replaced with -1
  • -
  • all other values are incremented by 1
  • -
-
long -
    -
  • 1 gets replaced with 0
  • -
  • all other values are incremented by 1
  • -
-
float -
    -
  • 1.0 gets replaced with 0.0
  • -
  • 2.0 gets replaced with 0.0
  • -
  • any other value gets replaced with 1.0
  • -
-
double -
  • 1.0 gets replaced with 0.0
  • -
  • any other value gets replaced with 1.0
  • -
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeMutation
    boolean + true is replaced with false and + false is replaced with true +
    integer byte short +
      +
    • 1 is replaced with 0
    • +
    • -1 is replaced with 1
    • +
    • 5 is replaced with -1
    • +
    • all other values are incremented by 1
    • +
    +
    long +
      +
    • 1 is replaced with 0
    • +
    • all other values are incremented by 1
    • +
    +
    float +
      +
    • 1.0 is replaced with 0.0
    • +
    • 2.0 is replaced with 0.0
    • +
    • any other value is replaced with 1.0
    • +
    +
    double +
      +
    • 1.0 is replaced with 0.0
    • +
    • any other value is replaced with 1.0
    • +
    +
    -

    For example

    +

    For example

    -
    +  
     public int aMethod() {
       int i = 16;
       return i;
     }
    -
    +
    -

    will be changed to

    +

    will be changed to

    -
    +  
     public int aMethod() {
       int i = 17;
       return i;
     }
    -
    +
    -

    The compiler may optimize expressions with final variables - regardless of being local or member variables. - In those cases the mutation is not able to mutate any variable. -

    -

    Example

    +

    + The compiler may optimize expressions with final variables - regardless of being local or member variables. + In those cases the mutation is not able to mutate any variable. +

    +

    Example

    -
    +  
     public class Example {
       private static final int CONSTANT = 4;
    -  
    +
       public String aMethod() {
         final int i = 16;
         return "" + CONSTANT + ":" + i;
       }
     }
    -
    +
    -

    might get optimized to

    +

    might get optimized to

    -
    +  
     public class Example {
       public String aMethod() {
         return "4:16";
       }
     }
    -
    - -

    see Pitest Mutators

    +
    +

    see Pitest Mutators

    diff --git a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/INVERT_NEGS.html b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/INVERT_NEGS.html index a7b3c03..64f7b82 100644 --- a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/INVERT_NEGS.html +++ b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/INVERT_NEGS.html @@ -19,23 +19,24 @@ -->
    -

    Invert Negatives Mutator (INVERT_NEGS)

    +

    Invert Negatives Mutator (INVERT_NEGS)

    +

    This operator mutates an integer or float number by negation.

    +
    +

    For example

    -

    By this operator the negation of an integer or float number is negated. For example

    - -
    +  
     public float doNegate(final int i) {
       return -i;
     }
    -
    +
    -

    will be mutated to

    +

    will be mutated to

    -
    +  
     public float doNegate(final int i) {
       return i;
     }
    -
    -

    see Pitest Mutators

    +
    +

    see Pitest Mutators

    diff --git a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/MATH.html b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/MATH.html index 803885e..8b58632 100644 --- a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/MATH.html +++ b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/MATH.html @@ -19,111 +19,113 @@ -->
    -

    Math Mutator (MATH)

    - - -

    Binary arithmetic operations for integer or floating point arithmetic are replaced by this operator with another - operation according the following ruleset. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - Original operation - - Mutated operation -
    +-
    -+
    */
    /*
    %*
    &|
    |&
    ^&
    <<>>
    >><<
    >>><<<
    - -

    Example

    - -
    +  

    Math Mutator (MATH)

    + +

    + This operator mutates binary arithmetic operations for integer or floating point arithmetic by replacing one + operator with another. Operation according the following ruleset. +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Original operation + + Mutated operation +
    +-
    -+
    */
    /*
    %*
    &|
    |&
    ^&
    <<>>
    >><<
    >>><<<
    + +

    Example

    + +
     int a = b + c;
    -
    +
    -

    will be mutated to

    +

    will be mutated to

    -
    +  
     int a = b - c;
    -
    +
    -

    The + operator on Strings is overloaded and is not mutated by this operator. -

    +

    + The + operator on Strings is overloaded and is not mutated by this operator. +

    -
    +  
     String a = "a" + "Text";
    -
    +
    -

    is a string concatenation and not a mathematical operator. The compiler replaces this concatenation with - something like

    +

    + is a string concatenation and not a mathematical operator. The compiler replaces this concatenation with something like +

    -
    +  
     String a = new StringBuilder("a").append("Text").toString();
    -
    +
    -

    For increments, decrements and assignment increments and decrements of member variables (non-local) the compiler - will also use binary arithmetic operations. Although a special iinc opcode exists for increments, - but it is restricted to local variables (stack variables) and is not applicable to member variables. Hence, - the math mutages will also mutate +

    + For increments, decrements, assignment increments, and assignemnt decrements of member variables (non-local) the + compiler will also use binary arithmetic operations. Although a special iinc opcode exists for + increments, but it is restricted to local variables (stack variables) and is not applicable to member variables. + Hence, the math mutators will also mutate. - Please note that the compiler will also use binary arithmetic operations for - increments, decrements and assignment increments and decrements of non-local - variables (member variables) although a special iinc opcode for increments - exists. This special opcode is restricted to local variables (also called stack - variables) and cannot be used for member variables. That means the math operator - will also mutate

    + Please note that the compiler will also use binary arithmetic operations for increments, decrements and + assignment increments and decrements of non-local variables (member variables) although a special iinc + opcode for increments exists. This special opcode is restricted to local variables (also called stack + variables) and cannot be used for member variables. That means the math operator will also mutate. +

    -
    +  
     public class Example {
       private int a;
     
    @@ -131,11 +133,11 @@ 

    Math Mutator (MATH)

    this.a++; } } -
    +
    -

    to

    +

    to

    -
    +  
     public class Example {
       private int a;
     
    @@ -143,6 +145,6 @@ 

    Math Mutator (MATH)

    this.a = this.a - 1; } } -
    -

    see Pitest Mutators

    +
    +

    see Pitest Mutators

    diff --git a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/NEGATE_CONDITIONALS.html b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/NEGATE_CONDITIONALS.html index 8008f60..c54af9d 100644 --- a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/NEGATE_CONDITIONALS.html +++ b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/NEGATE_CONDITIONALS.html @@ -19,86 +19,85 @@ -->
    -

    Negate Conditionals Mutator (NEGATE_CONDITIONALS)

    +

    Negate Conditionals Mutator (NEGATE_CONDITIONALS)

    -

    Conditional expressions are negated by this operator.

    +

    This operator mutates conditional expressions negation.

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
    - Original - - Mutated -
    ==!=
    !===
    <=>
    >=<
    <>=
    ><=
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - -
    + Original + + Mutated +
    ==!=
    !===
    <=>
    >=<
    <>=
    ><=
    + + -

    Example

    +

    Example

    -
    +  
     if (a != b) {
       // some code
     }
    -
    +
    -

    will be mutated to

    +

    will be mutated to

    -
    +  
     if (a == b) {
       // some code
     }
    -
    +
    -

    or

    +

    or

    -
    +  
     if (a <= b) {
    -    // some code
    +  // some code
     }
    -
    +
    -

    will be mutated to

    +

    will be mutated to

    -
    +  
     if (a > b) {
       // some code
     }
    -
    - - -

    The is an overlap with the conditional boundary operator. The conditionals negation however is more unstable and - is therefore easier to be killed by a test. -

    +
    -

    see Pitest Mutators

    +

    + There is an overlap with the conditional boundary operator. The conditionals negation however is more unstable and + is therefore easier to be killed by a test. +

    +

    see Pitest Mutators

    diff --git a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/NON_VOID_METHOD_CALLS.html b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/NON_VOID_METHOD_CALLS.html index ccbbb7a..b446636 100644 --- a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/NON_VOID_METHOD_CALLS.html +++ b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/NON_VOID_METHOD_CALLS.html @@ -19,47 +19,49 @@ -->
    -

    Non Void Method Call Mutator (NONVOIDMETHOD_CALLS)

    - -

    This mutages removes a call to a method with a non-void return value. Instead of the return value the default - value according the following table is used instead.

    - -

    Default Values for Primitives and Reference Types

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    TypeDefault Value
    booleanfalse
    int byte short long0
    float double0.0
    char'\u0000'
    Objectnull
    - -

    Example

    - -
    +  

    Non Void Method Call Mutator (NON_VOID_METHOD_CALLS)

    + +

    + This operator mutates calls to a method with a non-void return value. Instead of the return value the + default value according the following table is used instead. +

    + +

    Default Values for Primitives and Reference Types

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TypeDefault Value
    booleanfalse
    int byte short long0
    float double0.0
    char'\u0000'
    Objectnull
    + +

    Example

    + +
     public long aMethod() {
       return 5L;
     }
    @@ -68,11 +70,11 @@ 

    Non Void Method Call Mutator (NONVOIDMETHOD_CALLS)

    long l = aMethod(); // do something l } -
    +
    -

    will be mutated to

    +

    will be mutated to

    -
    +  
     public long aMethod() {
       return 5L;
     }
    @@ -81,11 +83,11 @@ 

    Non Void Method Call Mutator (NONVOIDMETHOD_CALLS)

    int l = 0L; // do something with l } -
    +
    -

    or

    +

    or

    -
    +  
     public Object aMethod() {
       return new Object();
     }
    @@ -94,11 +96,11 @@ 

    Non Void Method Call Mutator (NONVOIDMETHOD_CALLS)

    Object o = bMethod(); // do something with o } -
    +
    -

    will be mutated to

    +

    will be mutated to

    -
    +  
     public Object aMethod() {
       return new Object();
     }
    @@ -107,19 +109,21 @@ 

    Non Void Method Call Mutator (NONVOIDMETHOD_CALLS)

    Object o = null; // do something o } -
    - -

    This operator produces mutations that are fairly easy to detect by a test, especially when Reference types - are mutated causing NullPointerExceptions. Further, it may create mutations that are an equivalent - of the original code because the original code already returns a default value, that make it impossible to be - detected by a test. -

    - -

    This operator does not affect void methods or constructor calls. See - Void Method Call Mutator for mutations of - void methods and - Constructor Call Mutator for mutations of - constructors.

    - -

    see Pitest Mutators

    +
    + +

    + This operator produces mutations that are fairly easy to detect by a test, especially when Reference types + are mutated causing NullPointerExceptions. Further, it may create mutations that are an equivalent + of the original code because the original code already returns a default value, that make it impossible to be + detected by a test. +

    + +

    + This operator does not affect void methods or constructor calls. See + Void Method Call Mutator for mutations of + void methods and + Constructor Call Mutator for mutations of + constructors. +

    +

    see Pitest Mutators

    diff --git a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/NULL_RETURN_VALUES.html b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/NULL_RETURN_VALUES.html index 9df35c0..2e4ed2b 100644 --- a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/NULL_RETURN_VALUES.html +++ b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/NULL_RETURN_VALUES.html @@ -19,11 +19,13 @@ -->
    - This mutator replaces all non-primitive return values with null. -
    - As this mutator produces mutations that are very likely to be unstable - causing a NullPointerException that - is even found by trivial tests - this operator does not mutate the return type, if a more stable return - value mutation exists. -
    - It does further not mutate methods that are annotated with @NotNull +

    Null Return Values Mutator (NULL_RETURN_VALUES)

    + +

    This operator mutates all non-primitive return values by replacing them with null.

    +
    + As this mutator produces mutations that are very likely to be unstable - causing a NullPointerException that + is even found by trivial tests - this operator does not mutate the return type, if a more stable return + value mutation exists. +
    + It does further not mutate methods that are annotated with @NotNull
    diff --git a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/PRIMITIVE_RETURN_VALS.html b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/PRIMITIVE_RETURN_VALS.html index a0f16d6..92c1a08 100644 --- a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/PRIMITIVE_RETURN_VALS.html +++ b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/PRIMITIVE_RETURN_VALS.html @@ -19,5 +19,7 @@ -->
    - The mutation operator replaces all number primitive return values with 0 or 0.0. +

    Primitive Return Values Mutator (PRIMITIVE_RETURN_VALS)

    + +

    This operator mutates all number primitive return values with 0 or 0.0.

    diff --git a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/REMOVE_CONDITIONALS.html b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/REMOVE_CONDITIONALS.html index 9ae3274..da62d68 100644 --- a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/REMOVE_CONDITIONALS.html +++ b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/REMOVE_CONDITIONALS.html @@ -19,30 +19,32 @@ -->
    -

    Remove Conditionals Mutator (REMOVE_CONDITIONALS)

    +

    Remove Conditionals Mutator (REMOVE_CONDITIONALS)

    -

    This operator will replace all conditional expressions so that that they always evaluate to true, so that the - path guarded by the condition will always be executed. -

    +

    + This operator will replace all conditional expressions so that they always evaluate to true, so the path guarded + by the condition will always be executed. +

    +
    +

    For Example

    -

    Example

    - -
    +  
     if (a != b) {
       // some code
     }
    -
    +
    -

    will be mutated to

    +

    will be mutated to

    -
    +  
     if (true) {
       // some code
     }
    -
    - -

    This operator is not enabled by default. Nevertheless it is recommended to enable this operator to get full - coverage on conditional expressions

    +
    -

    see Pitest Mutators

    +

    + This operator is not enabled by default. Nevertheless, it is recommended to enable this operator to get full + coverage on conditional expressions +

    +

    see Pitest Mutators

    diff --git a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/RETURN_VALS.html b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/RETURN_VALS.html index 0b396fa..91a0696 100644 --- a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/RETURN_VALS.html +++ b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/RETURN_VALS.html @@ -19,99 +19,98 @@ -->
    -

    Return Values Mutator (RETURN_VALS)

    +

    Return Values Mutator (RETURN_VALS)

    +

    + This operator mutates method returns of non-void return values. The actual mutation applied depends on the + return type, according the following table: +

    -

    When a method returns a non-void return value, the value of that call is mutated by this mutator. The actual - mutation applied depends on the return type, according the following table: -

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Return TypeMutation FunctionDescription
    booleanf(x) -> !x + true is replaced with false and + false is replaced with true +
    + int byte short + f(x) -> x == 0 ? 1 : 0 + + 0 is mutated to 1 and + any other value is mutated to 0 +
    longf(x) -> x+1 + the original value is incremented by 1, for example 5 -> 6 +
    float doublef(x) -> x == NAN ? 0 : -(x+1.0) + if the value is NAN it is replaced with 0, otherwise - if it is a number - it is + replaced with the negated increment of the value +
    Objectf(x) -> x == null ? java.lang.RuntimeException : null + if the return value would be null a java.lang.RuntimeException is thrown, + otherwise null is returned instead of the original value +
    +
    +

    For Example

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Return TypeMutation FunctionDescription
    booleanf(x) -> !x - true gets replaced with false and - false gets replaced with true -
    - int byte short - f(x) -> x == 0 ? 1 : 0 - - 0 gets mutated to 1 and - any other value gets mutated to 0 -
    longf(x) -> x+1 - the original value is incremented by 1, for example 5 -> 6 -
    float doublef(x) -> x == NAN ? 0 : -(x+1.0) - if the value is NAN it gets replaced with 0, otherwise - if it is a number - it is - replaced with the negated increment of the value -
    Objectf(x) -> x == null ? java.lang.RuntimeException : null - if the return value would be null a java.lang.RuntimeException is thrown, - otherwise null is returned instead of the original value -
    - -

    Example

    - -
    +  
     public boolean aMethod() {
       return false;
     }
    -
    +
    -

    will be mutated to

    +

    will be mutated to

    -
    +  
     public boolean aMethod() {
       return true;
     }
    -
    +
    -

    or

    +

    or

    -
    +  
     public Object aMethod() {
    -    return new Object();
    +  return new Object();
     }
    -
    +
    -

    will be mutated to

    +

    will be mutated to

    -
    +  
     public Object aMethod() {
    -    new Object();
    -  return null;
    +   new Object();
    +   return null;
     }
    -
    - -

    see Pitest Mutators

    +
    +

    see Pitest Mutators

    diff --git a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/VOID_METHOD_CALLS.html b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/VOID_METHOD_CALLS.html index d2694f7..155b9ed 100644 --- a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/VOID_METHOD_CALLS.html +++ b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/VOID_METHOD_CALLS.html @@ -19,13 +19,15 @@ -->
    -

    Void Method Call Mutator (VOIDMETHODCALLS)

    +

    Void Method Call Mutator (VOIDMETHODCALLS)

    -

    Method without a return value - void methods - do not produce an explicit result that is assigned to a - variable. This could be a call of a setter or a message to an external system or underlying components for - which the effects require more effort to verify. This operator removes such method calls.

    +

    + This operator mutates methods without a return value - void methods - that do not produce an explicit result that + is assigned to a variable. This could be a call of a setter or a message to an external system or underlying + component for which the effects requires more effort to verify. This operator removes such method calls. +

    -
    +  
     public void aMethod(String s) {
       // some Code
     }
    @@ -35,11 +37,11 @@ 

    Void Method Call Mutator (VOIDMETHODCALLS)

    aMethod(s); return s; } -
    +
    -

    will be changed to

    +

    will be changed to

    -
    +  
     public void aMethod(String s) {
       // someCode
     }
    @@ -48,13 +50,14 @@ 

    Void Method Call Mutator (VOIDMETHODCALLS)

    String s = "example"; return s; } -
    - -

    Constructor calls are not considered as method calls and are therefore not affected by this operator. - To mutate constructor calls, refer to the - Constructor Call Mutator or the - Non Void Method Call - Mutator if you want to mutate method calls that produce a return value.

    - -

    see Pitest Mutators

    +
    + +

    + Constructor calls are not considered as method calls and are therefore not affected by this operator. + To mutate constructor calls, refer to the + Constructor Call Mutator or the + Non Void Method Call Mutator if you + want to mutate method calls that produce a return value. +

    +

    see Pitest Mutators

    diff --git a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/mutagen-def.xml b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/mutagen-def.xml index 7892c72..757543c 100644 --- a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/mutagen-def.xml +++ b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/mutagen-def.xml @@ -32,7 +32,7 @@ - Boolean False Return Vals Mutator + Boolean False Return Values Mutator @@ -43,7 +43,7 @@ - Boolean True Return Vals Mutator + Boolean True Return Values Mutator @@ -74,9 +74,9 @@ - Empty Object Return Vals Mutator + Empty Object Return Values Mutator - + @@ -145,7 +145,7 @@ - Null Return Vals Mutator + Null Return Values Mutator @@ -177,7 +177,7 @@ - Return Vals Mutator + Return Values Mutator @@ -209,7 +209,7 @@ Naked Receiver Mutator - + diff --git a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/rules.xml b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/rules.xml index f17aec3..0b285e5 100644 --- a/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/rules.xml +++ b/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/rules.xml @@ -20,11 +20,11 @@ --> - - mutant.survived - Survived Mutants - - Survived mutant + + mutant.survived + Survived Mutants + + Survived mutant

    A mutant of a class has not been killed although appearing to be covered by a test.

    If you don't want to activate any mutation operator specific rule, you may use this rule which is triggered by any Mutator. You should not activate this rule, when you use mutation operator specific rules, as it may result @@ -36,20 +36,20 @@ the mutation specific rules.

    ]]> -
    - DEPRECATED - BUG - aggregate - pitest - test - test-quality - mutation -
    - - mutant.lurking - Lurking Mutants - - + DEPRECATED + BUG + aggregate + pitest + test + test-quality + mutation + + + mutant.lurking + Lurking Mutants + + Uncovered mutant

    A mutant of a class has not been covered by a test at all.

    An uncovered mutant is also a survived mutant. If you activate this rule, it will produce more issues @@ -62,55 +62,53 @@ the mutation specific rules.

    ]]> -
    - DEPRECATED - BUG - aggregate - pitest - test - test-quality - mutation -
    - - mutant.unknownStatus - Mutant with unknown Status - - + DEPRECATED + BUG + aggregate + pitest + test + test-quality + mutation + + + mutant.unknownStatus + Mutant with unknown Status + + This rule is deprecated and is likely to be removed in future releases. Instead of this rule, use one of the mutation specific rules.

    ]]> -
    - MINOR - DEPRECATED - BUG - aggregate - pitest - test - test-quality - mutation -
    - - mutant.coverage - Mutation Coverage below threshold - - - - pitest - test - test-quality - mutation - coverage - - mutant.coverage.threshold - - + MINOR + DEPRECATED + BUG + aggregate + pitest + test + test-quality + mutation + + + mutant.coverage + Mutation Coverage below threshold + + + + pitest + test + test-quality + mutation + coverage + + mutant.coverage.threshold + + - - 80.0 - - - - +
    + 80.0 + +
    diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/MutationAnalysisPluginTest.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/MutationAnalysisPluginTest.java index 1c276c5..2d03efc 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/MutationAnalysisPluginTest.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/MutationAnalysisPluginTest.java @@ -20,8 +20,8 @@ package ch.devcon5.sonar.plugins.mutationanalysis; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationAnalysisMetrics; import ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationDensityComputer; @@ -37,7 +37,7 @@ import ch.devcon5.sonar.plugins.mutationanalysis.rules.KotlinRulesDefinition; import ch.devcon5.sonar.plugins.mutationanalysis.sensors.PitestSensor; import ch.devcon5.sonar.plugins.mutationanalysis.testharness.TestConfiguration; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.sonar.api.Plugin; import org.sonar.api.SonarEdition; import org.sonar.api.SonarQubeSide; @@ -45,54 +45,48 @@ import org.sonar.api.internal.SonarRuntimeImpl; import org.sonar.api.utils.Version; -public class MutationAnalysisPluginTest { - - - private SonarRuntime sonarRuntime = SonarRuntimeImpl.forSonarQube(Version.create(7, 3), SonarQubeSide.SCANNER, SonarEdition.COMMUNITY); - - - @Test - public void testDefine() throws Exception { - - MutationAnalysisPlugin plugin = new MutationAnalysisPlugin(); - Plugin.Context context = new Plugin.Context(sonarRuntime); - - plugin.define(context); - - assertTrue(context.getExtensions().contains(PitestReportParser.class)); - assertTrue(context.getExtensions().contains(PitestReportParser.class)); - assertTrue(context.getExtensions().contains(ReportFinder.class)); - assertTrue(context.getExtensions().contains(JavaRulesDefinition.class)); - assertTrue(context.getExtensions().contains(KotlinRulesDefinition.class)); - assertTrue(context.getExtensions().contains(JavaProfileDefinition.class)); - assertTrue(context.getExtensions().contains(KotlinProfileDefinition.class)); - assertTrue(context.getExtensions().contains(PitestSensor.class)); - assertTrue(context.getExtensions().contains(MutationAnalysisMetrics.class)); - assertTrue(context.getExtensions().contains(MutationScoreComputer.class)); - assertTrue(context.getExtensions().contains(MutationDensityComputer.class)); - assertTrue(context.getExtensions().contains(TotalMutationsComputer.class)); - assertTrue(context.getExtensions().contains(TestKillRatioComputer.class)); - assertTrue(context.getExtensions().contains(QuantitativeMeasureComputer.class)); - - } - - @Test - public void experimentalFeaturesEnabled_default_false() throws Exception { - - TestConfiguration configuration = new TestConfiguration(); - - assertFalse(MutationAnalysisPlugin.isExperimentalFeaturesEnabled(configuration)); - - } - - @Test - public void experimentalFeaturesEnabled_configured_true() throws Exception { - - TestConfiguration configuration = new TestConfiguration(); - configuration.set("dc5.mutationAnalysis.experimentalFeatures.enabled", true); - - assertTrue(MutationAnalysisPlugin.isExperimentalFeaturesEnabled(configuration)); - - } +class MutationAnalysisPluginTest { + + private final SonarRuntime sonarRuntime = SonarRuntimeImpl.forSonarQube( + Version.create(8, 9), SonarQubeSide.SCANNER, SonarEdition.COMMUNITY); + + + @Test + void testDefine() { + MutationAnalysisPlugin plugin = new MutationAnalysisPlugin(); + Plugin.Context context = new Plugin.Context(sonarRuntime); + + plugin.define(context); + + assertTrue(context.getExtensions().contains(PitestReportParser.class)); + assertTrue(context.getExtensions().contains(PitestReportParser.class)); + assertTrue(context.getExtensions().contains(ReportFinder.class)); + assertTrue(context.getExtensions().contains(JavaRulesDefinition.class)); + assertTrue(context.getExtensions().contains(KotlinRulesDefinition.class)); + assertTrue(context.getExtensions().contains(JavaProfileDefinition.class)); + assertTrue(context.getExtensions().contains(KotlinProfileDefinition.class)); + assertTrue(context.getExtensions().contains(PitestSensor.class)); + assertTrue(context.getExtensions().contains(MutationAnalysisMetrics.class)); + assertTrue(context.getExtensions().contains(MutationScoreComputer.class)); + assertTrue(context.getExtensions().contains(MutationDensityComputer.class)); + assertTrue(context.getExtensions().contains(TotalMutationsComputer.class)); + assertTrue(context.getExtensions().contains(TestKillRatioComputer.class)); + assertTrue(context.getExtensions().contains(QuantitativeMeasureComputer.class)); + } + + @Test + void experimentalFeaturesEnabled_default_false() { + TestConfiguration configuration = new TestConfiguration(); + + assertFalse(MutationAnalysisPlugin.isExperimentalFeaturesEnabled(configuration)); + } + + @Test + void experimentalFeaturesEnabled_configured_true() { + TestConfiguration configuration = new TestConfiguration(); + configuration.set("dc5.mutationAnalysis.experimentalFeatures.enabled", true); + + assertTrue(MutationAnalysisPlugin.isExperimentalFeaturesEnabled(configuration)); + } } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/StreamsTest.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/StreamsTest.java index f0f7357..0888511 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/StreamsTest.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/StreamsTest.java @@ -20,28 +20,28 @@ package ch.devcon5.sonar.plugins.mutationanalysis; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Arrays; import java.util.stream.Stream; +import org.junit.jupiter.api.Test; -import org.junit.Test; +class StreamsTest { -public class StreamsTest { + @Test + void sequentialStream() { + Stream stream = Streams.sequentialStream(Arrays.asList("one", "two")); + assertFalse(stream.isParallel()); + assertEquals(2, stream.count()); + } - @Test - public void sequentialStream() { - Stream stream = Streams.sequentialStream(Arrays.asList("one", "two")); - assertFalse(stream.isParallel()); - assertEquals(2, stream.count()); - } + @Test + void parallelStream() { + Stream stream = Streams.parallelStream(Arrays.asList("one", "two")); + assertTrue(stream.isParallel()); + assertEquals(2, stream.count()); + } - @Test - public void parallelStream() { - Stream stream = Streams.parallelStream(Arrays.asList("one", "two")); - assertTrue(stream.isParallel()); - assertEquals(2, stream.count()); - } } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationAnalysisMetricsTest.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationAnalysisMetricsTest.java index a22c4d0..415dfab 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationAnalysisMetricsTest.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationAnalysisMetricsTest.java @@ -20,24 +20,24 @@ package ch.devcon5.sonar.plugins.mutationanalysis.metrics; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import java.util.List; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; -import org.junit.Test; +import java.util.List; +import org.junit.jupiter.api.Test; import org.sonar.api.measures.Metric; /** - * + * Mutation Analysis Metrics Tests */ -public class MutationAnalysisMetricsTest { +class MutationAnalysisMetricsTest { - @Test - public void testGetMetrics() throws Exception { + @Test + void testGetMetrics() { + final List metrics = new MutationAnalysisMetrics().getMetrics(); + assertNotNull(metrics); + assertFalse(metrics.isEmpty()); + } - final List metrics = new MutationAnalysisMetrics().getMetrics(); - assertNotNull(metrics); - assertFalse(metrics.isEmpty()); - } } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationDensityComputerTest.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationDensityComputerTest.java index 92c4768..59ba9d3 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationDensityComputerTest.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationDensityComputerTest.java @@ -22,49 +22,48 @@ import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.MUTATIONS_DENSITY_KEY; import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.MUTATIONS_TOTAL_KEY; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.sonar.api.measures.CoreMetrics.LINES_TO_COVER_KEY; -import java.util.Arrays; - import ch.devcon5.sonar.plugins.mutationanalysis.testharness.MeasureComputerTestHarness; -import org.junit.Before; -import org.junit.Test; +import java.util.Arrays; +import java.util.Collections; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.sonar.api.ce.measure.Measure; import org.sonar.api.ce.measure.MeasureComputer; import org.sonar.api.ce.measure.test.TestMeasureComputerContext; import org.sonar.api.ce.measure.test.TestMeasureComputerDefinitionContext; /** - * + * Mutation Density Computer Tests */ -public class MutationDensityComputerTest { +class MutationDensityComputerTest { private MeasureComputerTestHarness harness; private MutationDensityComputer computer; - @Before + @BeforeEach public void setUp() throws Exception { this.harness = MeasureComputerTestHarness.createFor(MutationDensityComputer.class); this.computer = harness.getComputer(); } @Test - public void define() { - + void define() { final TestMeasureComputerDefinitionContext context = new TestMeasureComputerDefinitionContext(); final MeasureComputer.MeasureComputerDefinition def = computer.define(context); assertTrue(def.getInputMetrics() - .containsAll(Arrays.asList(MUTATIONS_TOTAL_KEY, LINES_TO_COVER_KEY))); - assertTrue(def.getOutputMetrics().containsAll(Arrays.asList(MUTATIONS_DENSITY_KEY))); + .containsAll(Arrays.asList(MUTATIONS_TOTAL_KEY, LINES_TO_COVER_KEY))); + assertTrue(def.getOutputMetrics().containsAll(Collections.singletonList(MUTATIONS_DENSITY_KEY))); } @Test - public void compute_experimentalFeaturesDisabled_noMeasure() { + void compute_experimentalFeaturesDisabled_noMeasure() { harness.enableExperimentalFeatures(false); final TestMeasureComputerContext measureContext = harness.createMeasureContextForSourceFile("compKey"); @@ -78,7 +77,7 @@ public void compute_experimentalFeaturesDisabled_noMeasure() { } @Test - public void compute_noMutations_noMeasure() { + void compute_noMutations_noMeasure() { final TestMeasureComputerContext measureContext = harness.createMeasureContextForSourceFile("compKey"); computer.compute(measureContext); @@ -87,7 +86,7 @@ public void compute_noMutations_noMeasure() { } @Test - public void compute_withInputMeausresMutations_correctOutputMeasure() { + void compute_withInputMeausresMutations_correctOutputMeasure() { final TestMeasureComputerContext measureContext = harness.createMeasureContextForSourceFile("compKey"); measureContext.addInputMeasure(MUTATIONS_TOTAL_KEY, 30); @@ -100,7 +99,7 @@ public void compute_withInputMeausresMutations_correctOutputMeasure() { } @Test - public void compute_ZeroMutationsAndZeroLines_noDensityMetric() { + void compute_ZeroMutationsAndZeroLines_noDensityMetric() { final TestMeasureComputerContext measureContext = harness.createMeasureContextForSourceFile("compKey"); measureContext.addInputMeasure(MUTATIONS_TOTAL_KEY, 0); @@ -112,7 +111,7 @@ public void compute_ZeroMutationsAndZeroLines_noDensityMetric() { } @Test - public void compute_noLineMetrics_densitySetToZero() { + void compute_noLineMetrics_densitySetToZero() { final TestMeasureComputerContext measureContext = harness.createMeasureContextForSourceFile("compKey"); measureContext.addInputMeasure(MUTATIONS_TOTAL_KEY, 30); @@ -122,4 +121,5 @@ public void compute_noLineMetrics_densitySetToZero() { Measure density = measureContext.getMeasure(MUTATIONS_DENSITY_KEY); assertEquals(0.0, density.getDoubleValue(), 0.05); } + } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationMetricsTest.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationMetricsTest.java index 0417647..e231c84 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationMetricsTest.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationMetricsTest.java @@ -20,48 +20,43 @@ package ch.devcon5.sonar.plugins.mutationanalysis.metrics; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import java.io.Serializable; import java.util.List; - -import org.junit.*; +import org.junit.jupiter.api.Test; import org.sonar.api.measures.Metric; -public class MutationMetricsTest { - - @Test - public void testMetricConstants() throws Exception { - - assertNotNull(MutationMetrics.MUTATIONS_COVERAGE); - assertNotNull(MutationMetrics.MUTATIONS_TEST_STRENGTH); - assertNotNull(MutationMetrics.MUTATIONS_DATA); - assertNotNull(MutationMetrics.MUTATIONS_DETECTED); - assertNotNull(MutationMetrics.MUTATIONS_KILLED); - assertNotNull(MutationMetrics.MUTATIONS_MEMORY_ERROR); - assertNotNull(MutationMetrics.MUTATIONS_NO_COVERAGE); - assertNotNull(MutationMetrics.MUTATIONS_SURVIVED); - assertNotNull(MutationMetrics.MUTATIONS_TIMED_OUT); - assertNotNull(MutationMetrics.MUTATIONS_TOTAL); - assertNotNull(MutationMetrics.MUTATIONS_UNKNOWN); - } - - - - @Test - public void testGetQuantitativeMetrics() throws Exception { - - final List> metrics = MutationMetrics.getQuantitativeMetrics(); - assertNotNull(metrics); - assertFalse(metrics.isEmpty()); - } - - @Test - public void testGetSensorMetrics() throws Exception { - - final List> metrics = MutationMetrics.getSensorMetrics(); - assertNotNull(metrics); - assertFalse(metrics.isEmpty()); - } +class MutationMetricsTest { + + @Test + void testMetricConstants() { + assertNotNull(MutationMetrics.MUTATIONS_COVERAGE); + assertNotNull(MutationMetrics.MUTATIONS_TEST_STRENGTH); + assertNotNull(MutationMetrics.MUTATIONS_DATA); + assertNotNull(MutationMetrics.MUTATIONS_DETECTED); + assertNotNull(MutationMetrics.MUTATIONS_KILLED); + assertNotNull(MutationMetrics.MUTATIONS_MEMORY_ERROR); + assertNotNull(MutationMetrics.MUTATIONS_NO_COVERAGE); + assertNotNull(MutationMetrics.MUTATIONS_SURVIVED); + assertNotNull(MutationMetrics.MUTATIONS_TIMED_OUT); + assertNotNull(MutationMetrics.MUTATIONS_TOTAL); + assertNotNull(MutationMetrics.MUTATIONS_UNKNOWN); + } + + @Test + void testGetQuantitativeMetrics() { + final List> metrics = MutationMetrics.getQuantitativeMetrics(); + assertNotNull(metrics); + assertFalse(metrics.isEmpty()); + } + + @Test + void testGetSensorMetrics() { + final List> metrics = MutationMetrics.getSensorMetrics(); + assertNotNull(metrics); + assertFalse(metrics.isEmpty()); + } } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationScoreComputerTest.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationScoreComputerTest.java index 6095be5..fb4268a 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationScoreComputerTest.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationScoreComputerTest.java @@ -20,27 +20,31 @@ package ch.devcon5.sonar.plugins.mutationanalysis.metrics; -import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.*; -import static org.junit.Assert.*; - -import java.util.Arrays; -import java.util.HashSet; +import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.MUTATIONS_COVERAGE_KEY; +import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.MUTATIONS_DETECTED_KEY; +import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.MUTATIONS_SURVIVED_KEY; +import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.MUTATIONS_TEST_STRENGTH_KEY; +import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.MUTATIONS_TOTAL_KEY; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import ch.devcon5.sonar.plugins.mutationanalysis.MutationAnalysisPlugin; import ch.devcon5.sonar.plugins.mutationanalysis.testharness.MeasureComputerTestHarness; -import org.junit.*; +import java.util.Arrays; +import java.util.HashSet; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.sonar.api.ce.measure.MeasureComputer; import org.sonar.api.ce.measure.test.TestMeasureComputerContext; import org.sonar.api.ce.measure.test.TestMeasureComputerDefinitionContext; -public class MutationScoreComputerTest { +class MutationScoreComputerTest { private MutationScoreComputer computer; private MeasureComputerTestHarness harness; - @Before + @BeforeEach public void setUp() { - this.harness = MeasureComputerTestHarness.createFor(MutationScoreComputer.class); this.computer = harness.getComputer(); setForceMissingCoverageToZero(null); @@ -48,19 +52,18 @@ public void setUp() { } @Test - public void define() { - + void define() { final TestMeasureComputerDefinitionContext context = new TestMeasureComputerDefinitionContext(); - final MeasureComputer.MeasureComputerDefinition def = computer.define(context); - assertEquals(def.getInputMetrics(), new HashSet<>(Arrays.asList(MUTATIONS_DETECTED_KEY, MUTATIONS_TOTAL_KEY, MUTATIONS_SURVIVED_KEY))); - assertEquals(def.getOutputMetrics(), new HashSet<>(Arrays.asList(MUTATIONS_COVERAGE_KEY, MUTATIONS_TEST_STRENGTH_KEY))); + assertEquals(def.getInputMetrics(), + new HashSet<>(Arrays.asList(MUTATIONS_DETECTED_KEY, MUTATIONS_TOTAL_KEY, MUTATIONS_SURVIVED_KEY))); + assertEquals(def.getOutputMetrics(), + new HashSet<>(Arrays.asList(MUTATIONS_COVERAGE_KEY, MUTATIONS_TEST_STRENGTH_KEY))); } @Test - public void compute_noMutations_forceToZeroNotConfigured_defaultsToFalse_noMeasure() { - + void compute_noMutations_forceToZeroNotConfigured_defaultsToFalse_noMeasure() { final TestMeasureComputerContext measureContext = harness.createMeasureContextForSourceFile("compKey"); computer.compute(measureContext); @@ -69,8 +72,7 @@ public void compute_noMutations_forceToZeroNotConfigured_defaultsToFalse_noMeasu } @Test - public void compute_noMutations_ForceTo0Disabled_noMeasure() { - + void compute_noMutations_ForceTo0Disabled_noMeasure() { setForceMissingCoverageToZero(false); final TestMeasureComputerContext measureContext = harness.createMeasureContextForSourceFile("compKey"); @@ -80,8 +82,7 @@ public void compute_noMutations_ForceTo0Disabled_noMeasure() { } @Test - public void compute_noMutations_forceTo0Enabled_noMeasure() { - + void compute_noMutations_forceTo0Enabled_noMeasure() { setForceMissingCoverageToZero(true); final TestMeasureComputerContext measureContext = harness.createMeasureContextForSourceFile("compKey"); @@ -92,8 +93,7 @@ public void compute_noMutations_forceTo0Enabled_noMeasure() { } @Test - public void compute_0totalMutations_to_100percentCoverage() { - + void compute_0totalMutations_to_100percentCoverage() { final TestMeasureComputerContext measureContext = harness.createMeasureContextForSourceFile("compKey"); measureContext.addInputMeasure(MUTATIONS_TOTAL_KEY, 0); @@ -105,8 +105,7 @@ public void compute_0totalMutations_to_100percentCoverage() { } @Test - public void compute_3of5Mutations_to_60percentCoverage() { - + void compute_3of5Mutations_to_60percentCoverage() { final TestMeasureComputerContext measureContext = harness.createMeasureContextForSourceFile("compKey"); measureContext.addInputMeasure(MUTATIONS_TOTAL_KEY, 5); @@ -115,12 +114,10 @@ public void compute_3of5Mutations_to_60percentCoverage() { computer.compute(measureContext); assertEquals(60.0, measureContext.getMeasure(MUTATIONS_COVERAGE_KEY).getDoubleValue(), 0.05); - } @Test - public void compute_1of5MutationsKilled_to_20percentTestStrength() { - + void compute_1of5MutationsKilled_to_20percentTestStrength() { final TestMeasureComputerContext measureContext = harness.createMeasureContextForSourceFile("compKey"); measureContext.addInputMeasure(MUTATIONS_TOTAL_KEY, 5); @@ -130,12 +127,10 @@ public void compute_1of5MutationsKilled_to_20percentTestStrength() { computer.compute(measureContext); assertEquals(20.0, measureContext.getMeasure(MUTATIONS_TEST_STRENGTH_KEY).getDoubleValue(), 0.05); - } @Test - public void compute_SurvivedAndDetectedAreInconsitent_testStrengthDefaultsTo0() { - + void compute_SurvivedAndDetectedAreInconsitent_testStrengthDefaultsTo0() { final TestMeasureComputerContext measureContext = harness.createMeasureContextForSourceFile("compKey"); measureContext.addInputMeasure(MUTATIONS_TOTAL_KEY, 1); @@ -145,12 +140,10 @@ public void compute_SurvivedAndDetectedAreInconsitent_testStrengthDefaultsTo0() computer.compute(measureContext); assertEquals(0.0, measureContext.getMeasure(MUTATIONS_TEST_STRENGTH_KEY).getDoubleValue(), 0.05); - } @Test - public void compute_5of5MutationsKilled_missingSurvivedMetric_to_100percentTestStrength() { - + void compute_5of5MutationsKilled_missingSurvivedMetric_to_100percentTestStrength() { final TestMeasureComputerContext measureContext = harness.createMeasureContextForSourceFile("compKey"); //we dont set the MUTATIONS_SURVIVED_KEY metric (defaulting to 0) @@ -160,13 +153,11 @@ public void compute_5of5MutationsKilled_missingSurvivedMetric_to_100percentTestS computer.compute(measureContext); assertEquals(100.0, measureContext.getMeasure(MUTATIONS_TEST_STRENGTH_KEY).getDoubleValue(), 0.05); - } @Test - public void compute_NoCoveredElements_0percentCoverage() { - + void compute_NoCoveredElements_0percentCoverage() { final TestMeasureComputerContext measureContext = harness.createMeasureContextForSourceFile("compKey"); measureContext.addInputMeasure(MUTATIONS_TOTAL_KEY, 5); @@ -175,11 +166,9 @@ public void compute_NoCoveredElements_0percentCoverage() { assertEquals(0.0, measureContext.getMeasure(MUTATIONS_COVERAGE_KEY).getDoubleValue(), 0.05); assertEquals(0.0, measureContext.getMeasure(MUTATIONS_TEST_STRENGTH_KEY).getDoubleValue(), 0.05); - } private void setForceMissingCoverageToZero(Boolean enabled) { - harness.getConfig().set(MutationAnalysisPlugin.FORCE_MISSING_COVERAGE_TO_ZERO, enabled); } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/QuantitativeMeasureComputerTest.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/QuantitativeMeasureComputerTest.java index 57f571e..59b5a4f 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/QuantitativeMeasureComputerTest.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/QuantitativeMeasureComputerTest.java @@ -20,70 +20,77 @@ package ch.devcon5.sonar.plugins.mutationanalysis.metrics; -import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.*; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.util.Arrays; +import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.MUTATIONS_ALIVE_KEY; +import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.MUTATIONS_DETECTED_KEY; +import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.MUTATIONS_KILLED_KEY; +import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.MUTATIONS_MEMORY_ERROR_KEY; +import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.MUTATIONS_NO_COVERAGE_KEY; +import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.MUTATIONS_SURVIVED_KEY; +import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.MUTATIONS_TIMED_OUT_KEY; +import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.MUTATIONS_TOTAL_KEY; +import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.MUTATIONS_UNKNOWN_KEY; +import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.TEST_KILLS_KEY; +import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.UTILITY_GLOBAL_ALIVE_KEY; +import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.UTILITY_GLOBAL_MUTATIONS_KEY; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import ch.devcon5.sonar.plugins.mutationanalysis.testharness.MeasureComputerTestHarness; -import org.junit.Before; -import org.junit.Test; +import java.util.Arrays; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.sonar.api.ce.measure.MeasureComputer; import org.sonar.api.ce.measure.test.TestMeasureComputerContext; import org.sonar.api.ce.measure.test.TestMeasureComputerDefinitionContext; /** - * + * Quantitative Measure Computer Tests */ -public class QuantitativeMeasureComputerTest { +class QuantitativeMeasureComputerTest { private MeasureComputerTestHarness harness; private QuantitativeMeasureComputer computer; - @Before + @BeforeEach public void setUp() throws Exception { this.harness = MeasureComputerTestHarness.createFor(QuantitativeMeasureComputer.class); this.computer = harness.getComputer(); } @Test - public void define() { - + void define() { final TestMeasureComputerDefinitionContext context = new TestMeasureComputerDefinitionContext(); - final MeasureComputer.MeasureComputerDefinition def = computer.define(context); assertTrue(def.getOutputMetrics().containsAll(Arrays.asList(MUTATIONS_TOTAL_KEY, - MUTATIONS_NO_COVERAGE_KEY, - MUTATIONS_DETECTED_KEY, - MUTATIONS_ALIVE_KEY, - MUTATIONS_KILLED_KEY, - MUTATIONS_UNKNOWN_KEY, - MUTATIONS_TIMED_OUT_KEY, - MUTATIONS_MEMORY_ERROR_KEY, - MUTATIONS_SURVIVED_KEY, - TEST_KILLS_KEY, - UTILITY_GLOBAL_MUTATIONS_KEY, - UTILITY_GLOBAL_ALIVE_KEY))); + MUTATIONS_NO_COVERAGE_KEY, + MUTATIONS_DETECTED_KEY, + MUTATIONS_ALIVE_KEY, + MUTATIONS_KILLED_KEY, + MUTATIONS_UNKNOWN_KEY, + MUTATIONS_TIMED_OUT_KEY, + MUTATIONS_MEMORY_ERROR_KEY, + MUTATIONS_SURVIVED_KEY, + TEST_KILLS_KEY, + UTILITY_GLOBAL_MUTATIONS_KEY, + UTILITY_GLOBAL_ALIVE_KEY))); } @Test - public void compute_sumChildMeasures() { + void compute_sumChildMeasures() { final TestMeasureComputerContext measureContext = harness.createMeasureContextForSourceFile("compKey"); - - measureContext.addChildrenMeasures(MUTATIONS_TOTAL_KEY, 1,2,3); - measureContext.addChildrenMeasures(MUTATIONS_NO_COVERAGE_KEY, 4,5,6); - measureContext.addChildrenMeasures(MUTATIONS_DETECTED_KEY, 7,8,9); - measureContext.addChildrenMeasures(MUTATIONS_KILLED_KEY, 10,11,12); - measureContext.addChildrenMeasures(MUTATIONS_TIMED_OUT_KEY, 13,14,15); - measureContext.addChildrenMeasures(MUTATIONS_MEMORY_ERROR_KEY, 16,17,18); - measureContext.addChildrenMeasures(MUTATIONS_SURVIVED_KEY, 19,20,21); - measureContext.addChildrenMeasures(TEST_KILLS_KEY, 22,23,24); - measureContext.addChildrenMeasures(UTILITY_GLOBAL_MUTATIONS_KEY, 10,10,10); - measureContext.addChildrenMeasures(UTILITY_GLOBAL_ALIVE_KEY, 20,20,20); + measureContext.addChildrenMeasures(MUTATIONS_TOTAL_KEY, 1, 2, 3); + measureContext.addChildrenMeasures(MUTATIONS_NO_COVERAGE_KEY, 4, 5, 6); + measureContext.addChildrenMeasures(MUTATIONS_DETECTED_KEY, 7, 8, 9); + measureContext.addChildrenMeasures(MUTATIONS_KILLED_KEY, 10, 11, 12); + measureContext.addChildrenMeasures(MUTATIONS_TIMED_OUT_KEY, 13, 14, 15); + measureContext.addChildrenMeasures(MUTATIONS_MEMORY_ERROR_KEY, 16, 17, 18); + measureContext.addChildrenMeasures(MUTATIONS_SURVIVED_KEY, 19, 20, 21); + measureContext.addChildrenMeasures(TEST_KILLS_KEY, 22, 23, 24); + measureContext.addChildrenMeasures(UTILITY_GLOBAL_MUTATIONS_KEY, 10, 10, 10); + measureContext.addChildrenMeasures(UTILITY_GLOBAL_ALIVE_KEY, 20, 20, 20); computer.compute(measureContext); @@ -100,19 +107,18 @@ public void compute_sumChildMeasures() { } @Test - public void compute_childMeasuresAre0_noValuePropagated() { + void compute_childMeasuresAre0_noValuePropagated() { final TestMeasureComputerContext measureContext = harness.createMeasureContextForSourceFile("compKey"); - - measureContext.addChildrenMeasures(MUTATIONS_TOTAL_KEY, 0,0,0); - measureContext.addChildrenMeasures(MUTATIONS_NO_COVERAGE_KEY, 0,0,0); - measureContext.addChildrenMeasures(MUTATIONS_DETECTED_KEY, 0,0,0); - measureContext.addChildrenMeasures(MUTATIONS_KILLED_KEY, 0,0,0); - measureContext.addChildrenMeasures(MUTATIONS_TIMED_OUT_KEY, 0,0,0); - measureContext.addChildrenMeasures(MUTATIONS_MEMORY_ERROR_KEY, 0,0,0); - measureContext.addChildrenMeasures(MUTATIONS_SURVIVED_KEY, 0,0,0); - measureContext.addChildrenMeasures(TEST_KILLS_KEY, 0,0,0); - measureContext.addChildrenMeasures(UTILITY_GLOBAL_MUTATIONS_KEY, 0,0,0); - measureContext.addChildrenMeasures(UTILITY_GLOBAL_ALIVE_KEY, 0,0,0); + measureContext.addChildrenMeasures(MUTATIONS_TOTAL_KEY, 0, 0, 0); + measureContext.addChildrenMeasures(MUTATIONS_NO_COVERAGE_KEY, 0, 0, 0); + measureContext.addChildrenMeasures(MUTATIONS_DETECTED_KEY, 0, 0, 0); + measureContext.addChildrenMeasures(MUTATIONS_KILLED_KEY, 0, 0, 0); + measureContext.addChildrenMeasures(MUTATIONS_TIMED_OUT_KEY, 0, 0, 0); + measureContext.addChildrenMeasures(MUTATIONS_MEMORY_ERROR_KEY, 0, 0, 0); + measureContext.addChildrenMeasures(MUTATIONS_SURVIVED_KEY, 0, 0, 0); + measureContext.addChildrenMeasures(TEST_KILLS_KEY, 0, 0, 0); + measureContext.addChildrenMeasures(UTILITY_GLOBAL_MUTATIONS_KEY, 0, 0, 0); + measureContext.addChildrenMeasures(UTILITY_GLOBAL_ALIVE_KEY, 0, 0, 0); computer.compute(measureContext); @@ -129,13 +135,12 @@ public void compute_childMeasuresAre0_noValuePropagated() { } @Test - public void compute_skipPreExistingMeasures() { + void compute_skipPreExistingMeasures() { final TestMeasureComputerContext measureContext = harness.createMeasureContextForSourceFile("compKey"); - measureContext.addMeasure(MUTATIONS_TOTAL_KEY, 999); measureContext.addMeasure(UTILITY_GLOBAL_MUTATIONS_KEY, 888); - measureContext.addChildrenMeasures(MUTATIONS_TOTAL_KEY, 16,17,18); - measureContext.addChildrenMeasures(UTILITY_GLOBAL_MUTATIONS_KEY, 10,10,10); + measureContext.addChildrenMeasures(MUTATIONS_TOTAL_KEY, 16, 17, 18); + measureContext.addChildrenMeasures(UTILITY_GLOBAL_MUTATIONS_KEY, 10, 10, 10); computer.compute(measureContext); @@ -145,7 +150,7 @@ public void compute_skipPreExistingMeasures() { @Test - public void compute_childMeasuresAreNull_noValuePropagated() { + void compute_childMeasuresAreNull_noValuePropagated() { final TestMeasureComputerContext measureContext = harness.createMeasureContextForSourceFile("compKey"); computer.compute(measureContext); @@ -161,4 +166,5 @@ public void compute_childMeasuresAreNull_noValuePropagated() { assertNull(measureContext.getMeasure(UTILITY_GLOBAL_MUTATIONS_KEY)); assertNull(measureContext.getMeasure(UTILITY_GLOBAL_ALIVE_KEY)); } + } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/ResourceMutationMetricsTest.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/ResourceMutationMetricsTest.java index 4ad55e2..fd50453 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/ResourceMutationMetricsTest.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/ResourceMutationMetricsTest.java @@ -19,218 +19,206 @@ */ package ch.devcon5.sonar.plugins.mutationanalysis.metrics; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.nio.file.Paths; -import java.util.Collection; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant; import ch.devcon5.sonar.plugins.mutationanalysis.model.MutationOperators; -import org.junit.Test; +import java.nio.file.Paths; +import java.util.Collection; +import org.junit.jupiter.api.Test; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultIndexedFile; import org.sonar.api.batch.fs.internal.DefaultInputFile; -public class ResourceMutationMetricsTest { - - private InputFile resource = new DefaultInputFile(new DefaultIndexedFile("test", Paths.get("."), "src/main/java/example/Test.java", "java"), x -> {}); - - private Mutant mutant = defaultMutantBuilder().mutantStatus(Mutant.State.UNKNOWN).build(); - - private Mutant.Builder defaultMutantBuilder() { - return Mutant.builder() - .inSourceFile("Test.java") - .inClass("example.Test") - .inMethod("helloWorld") - .withMethodParameters("(Ljava/lang/Object;)Z") - .usingMutator(MutationOperators.find("MATH")); - } - - private ResourceMutationMetrics subject = new ResourceMutationMetrics(resource); - - @Test - public void testDefaults() throws Exception { - ResourceMutationMetrics rmm = new ResourceMutationMetrics(resource); - - assertEquals(0, rmm.getMutationsDetected()); - assertEquals(0, rmm.getMutationsTotal()); - assertEquals(0, rmm.getMutationsKilled()); - assertEquals(0, rmm.getMutationsMemoryError()); - assertEquals(0, rmm.getMutationsNoCoverage()); - assertEquals(0, rmm.getMutationsSurvived()); - assertEquals(0, rmm.getMutationsTimedOut()); - assertEquals(0, rmm.getMutationsUnknown()); - assertEquals(0.0, rmm.getMutationCoverage(), 0.0001); - assertTrue(rmm.getMutants().isEmpty()); - - } - - @Test - public void testAddMutant() throws Exception { - - // act - subject.addMutant(mutant); - subject.addMutant(mutant); - subject.addMutant(mutant); - - // assert - final Collection mutants = subject.getMutants(); - assertNotNull(mutants); - assertEquals(3, mutants.size()); - - } - - @Test - public void testGetMutationsTotal() throws Exception { - - // prepare - subject.addMutant(mutant); - subject.addMutant(mutant); - subject.addMutant(mutant); - - // act - final int value = subject.getMutationsTotal(); - - // assert - assertEquals(3, value); - } - - @Test - public void testGetMutationsNoCoverage() throws Exception { - - // prepare - Mutant mutant = defaultMutantBuilder().mutantStatus(Mutant.State.NO_COVERAGE).build(); - - subject.addMutant(mutant); - - // act - final int value = subject.getMutationsNoCoverage(); - - // assert - assertEquals(1, value); - } - - @Test - public void testGetMutationsKilled() throws Exception { - - // prepare - Mutant mutant = defaultMutantBuilder().mutantStatus(Mutant.State.KILLED).killedBy("ATest").build(); - subject.addMutant(mutant); - - // act - final int value = subject.getMutationsKilled(); - - // assert - assertEquals(1, value); - } - - @Test - public void testGetMutationsSurvived() throws Exception { - - // prepare - Mutant mutant = defaultMutantBuilder().mutantStatus(Mutant.State.SURVIVED).build(); - subject.addMutant(mutant); - - // act - final int value = subject.getMutationsSurvived(); - - // assert - assertEquals(1, value); - } - - @Test - public void testGetMutationsMemoryError() throws Exception { - - // prepare - Mutant mutant = defaultMutantBuilder().mutantStatus(Mutant.State.MEMORY_ERROR).killedBy("ATest").build(); - subject.addMutant(mutant); - - // act - final int value = subject.getMutationsMemoryError(); - - // assert - assertEquals(1, value); - } - - @Test - public void testGetMutationsTimedOut() throws Exception { - - // prepare - Mutant mutant = defaultMutantBuilder().mutantStatus(Mutant.State.TIMED_OUT).killedBy("ATest").build(); - subject.addMutant(mutant); - - // act - final int value = subject.getMutationsTimedOut(); - - // assert - assertEquals(1, value); - } - - @Test - public void testGetMutationsUnknown() throws Exception { - - // prepare - Mutant mutant = defaultMutantBuilder().mutantStatus(Mutant.State.UNKNOWN).build(); - subject.addMutant(mutant); - - // act - final int value = subject.getMutationsUnknown(); - - // assert - assertEquals(1, value); - } - - @Test - public void testGetMutationsDetected() throws Exception { - - // prepare - subject.addMutant(defaultMutantBuilder().mutantStatus(Mutant.State.KILLED).killedBy("ATest").build()); - subject.addMutant(defaultMutantBuilder().mutantStatus(Mutant.State.SURVIVED).build()); - subject.addMutant(defaultMutantBuilder().mutantStatus(Mutant.State.NO_COVERAGE).build()); - - // act - final int value = subject.getMutationsDetected(); - - // assert - assertEquals(1, value); - } - - @Test - public void testGetMutationCoverage() throws Exception { - - // prepare - subject.addMutant(defaultMutantBuilder().mutantStatus(Mutant.State.KILLED).killedBy("ATest").build()); - subject.addMutant(defaultMutantBuilder().mutantStatus(Mutant.State.SURVIVED).build()); - subject.addMutant(defaultMutantBuilder().mutantStatus(Mutant.State.NO_COVERAGE).build()); - - // act - final double value = subject.getMutationCoverage(); - - // assert - assertEquals(100.0 / 3.0, value, 0.000001); - } - - @Test - public void testGetMutationCoverage_fullCoverage() throws Exception { - - // prepare - subject.addMutant(defaultMutantBuilder().mutantStatus(Mutant.State.KILLED).killedBy("ATest").build()); - subject.addMutant(defaultMutantBuilder().mutantStatus(Mutant.State.SURVIVED).build()); - subject.addMutant(defaultMutantBuilder().mutantStatus(Mutant.State.NO_COVERAGE).build()); - - // act - final double value = subject.getMutationCoverage(); - - // assert - assertEquals(33.33, value, 0.01); - } - - @Test - public void testGetResource() throws Exception { - - assertEquals(resource, subject.getResource()); - } +class ResourceMutationMetricsTest { + + private final InputFile resource = new DefaultInputFile( + new DefaultIndexedFile("test", Paths.get("."), "src/main/java/example/Test.java", "java"), x -> { + }); + + private final Mutant mutant = defaultMutantBuilder() + .mutantStatus(Mutant.State.UNKNOWN) + .build(); + + private Mutant.Builder defaultMutantBuilder() { + return Mutant.builder() + .inSourceFile("Test.java") + .inClass("example.Test") + .inMethod("helloWorld") + .withMethodParameters("(Ljava/lang/Object;)Z") + .usingMutator(MutationOperators.find("MATH")); + } + + private final ResourceMutationMetrics subject = new ResourceMutationMetrics(resource); + + @Test + void testDefaults() { + ResourceMutationMetrics rmm = new ResourceMutationMetrics(resource); + assertEquals(0, rmm.getMutationsDetected()); + assertEquals(0, rmm.getMutationsTotal()); + assertEquals(0, rmm.getMutationsKilled()); + assertEquals(0, rmm.getMutationsMemoryError()); + assertEquals(0, rmm.getMutationsNoCoverage()); + assertEquals(0, rmm.getMutationsSurvived()); + assertEquals(0, rmm.getMutationsTimedOut()); + assertEquals(0, rmm.getMutationsUnknown()); + assertEquals(0.0, rmm.getMutationCoverage(), 0.0001); + assertTrue(rmm.getMutants().isEmpty()); + } + + @Test + void testAddMutant() { + // act + subject.addMutant(mutant); + subject.addMutant(mutant); + subject.addMutant(mutant); + + // assert + final Collection mutants = subject.getMutants(); + assertNotNull(mutants); + assertEquals(3, mutants.size()); + } + + @Test + void testGetMutationsTotal() { + // prepare + subject.addMutant(mutant); + subject.addMutant(mutant); + subject.addMutant(mutant); + + // act + final int value = subject.getMutationsTotal(); + + // assert + assertEquals(3, value); + } + + @Test + void testGetMutationsNoCoverage() { + // prepare + Mutant mutant = defaultMutantBuilder().mutantStatus(Mutant.State.NO_COVERAGE).build(); + + subject.addMutant(mutant); + + // act + final int value = subject.getMutationsNoCoverage(); + + // assert + assertEquals(1, value); + } + + @Test + void testGetMutationsKilled() { + // prepare + Mutant mutant = defaultMutantBuilder().mutantStatus(Mutant.State.KILLED).killedBy("ATest").build(); + subject.addMutant(mutant); + + // act + final int value = subject.getMutationsKilled(); + + // assert + assertEquals(1, value); + } + + @Test + void testGetMutationsSurvived() { + // prepare + Mutant mutant = defaultMutantBuilder().mutantStatus(Mutant.State.SURVIVED).build(); + subject.addMutant(mutant); + + // act + final int value = subject.getMutationsSurvived(); + + // assert + assertEquals(1, value); + } + + @Test + void testGetMutationsMemoryError() { + // prepare + Mutant mutant = defaultMutantBuilder().mutantStatus(Mutant.State.MEMORY_ERROR).killedBy("ATest").build(); + subject.addMutant(mutant); + + // act + final int value = subject.getMutationsMemoryError(); + + // assert + assertEquals(1, value); + } + + @Test + void testGetMutationsTimedOut() { + // prepare + Mutant mutant = defaultMutantBuilder().mutantStatus(Mutant.State.TIMED_OUT).killedBy("ATest").build(); + subject.addMutant(mutant); + + // act + final int value = subject.getMutationsTimedOut(); + + // assert + assertEquals(1, value); + } + + @Test + void testGetMutationsUnknown() { + // prepare + Mutant mutant = defaultMutantBuilder().mutantStatus(Mutant.State.UNKNOWN).build(); + subject.addMutant(mutant); + + // act + final int value = subject.getMutationsUnknown(); + + // assert + assertEquals(1, value); + } + + @Test + void testGetMutationsDetected() { + // prepare + subject.addMutant(defaultMutantBuilder().mutantStatus(Mutant.State.KILLED).killedBy("ATest").build()); + subject.addMutant(defaultMutantBuilder().mutantStatus(Mutant.State.SURVIVED).build()); + subject.addMutant(defaultMutantBuilder().mutantStatus(Mutant.State.NO_COVERAGE).build()); + + // act + final int value = subject.getMutationsDetected(); + + // assert + assertEquals(1, value); + } + + @Test + void testGetMutationCoverage() { + // prepare + subject.addMutant(defaultMutantBuilder().mutantStatus(Mutant.State.KILLED).killedBy("ATest").build()); + subject.addMutant(defaultMutantBuilder().mutantStatus(Mutant.State.SURVIVED).build()); + subject.addMutant(defaultMutantBuilder().mutantStatus(Mutant.State.NO_COVERAGE).build()); + + // act + final double value = subject.getMutationCoverage(); + + // assert + assertEquals(100.0 / 3.0, value, 0.000001); + } + + @Test + void testGetMutationCoverage_fullCoverage() { + // prepare + subject.addMutant(defaultMutantBuilder().mutantStatus(Mutant.State.KILLED).killedBy("ATest").build()); + subject.addMutant(defaultMutantBuilder().mutantStatus(Mutant.State.SURVIVED).build()); + subject.addMutant(defaultMutantBuilder().mutantStatus(Mutant.State.NO_COVERAGE).build()); + + // act + final double value = subject.getMutationCoverage(); + + // assert + assertEquals(33.33, value, 0.01); + } + + @Test + void testGetResource() { + assertEquals(resource, subject.getResource()); + } } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/TestKillRatioComputerTest.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/TestKillRatioComputerTest.java index 3d3d6c0..3e9920e 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/TestKillRatioComputerTest.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/TestKillRatioComputerTest.java @@ -23,15 +23,15 @@ import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.TEST_KILLS_KEY; import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.TEST_KILL_RATIO_KEY; import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.UTILITY_GLOBAL_MUTATIONS_KEY; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.util.Arrays; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import ch.devcon5.sonar.plugins.mutationanalysis.testharness.MeasureComputerTestHarness; -import org.junit.Before; -import org.junit.Test; +import java.util.Arrays; +import java.util.Collections; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.sonar.api.ce.measure.Component; import org.sonar.api.ce.measure.Measure; import org.sonar.api.ce.measure.MeasureComputer; @@ -39,36 +39,31 @@ import org.sonar.api.ce.measure.test.TestMeasureComputerDefinitionContext; /** - * + * Test Kill Ratio Computer Tests */ -public class TestKillRatioComputerTest { +class TestKillRatioComputerTest { private MeasureComputerTestHarness harness; private TestKillRatioComputer computer; - @Before + @BeforeEach public void setUp() throws Exception { this.harness = MeasureComputerTestHarness.createFor(TestKillRatioComputer.class); this.computer = harness.getComputer(); } - - @Test - public void define() { + void define() { final TestMeasureComputerDefinitionContext context = new TestMeasureComputerDefinitionContext(); - final MeasureComputer.MeasureComputerDefinition def = computer.define(context); - assertTrue(def.getInputMetrics() - .containsAll(Arrays.asList(UTILITY_GLOBAL_MUTATIONS_KEY, TEST_KILLS_KEY))); - assertTrue(def.getOutputMetrics().containsAll(Arrays.asList(TEST_KILL_RATIO_KEY))); + assertTrue(def.getInputMetrics().containsAll(Arrays.asList(UTILITY_GLOBAL_MUTATIONS_KEY, TEST_KILLS_KEY))); + assertTrue(def.getOutputMetrics().containsAll(Collections.singletonList(TEST_KILL_RATIO_KEY))); } @Test - public void compute_noInputMeasures_noOutputMeasure() { - + void compute_noInputMeasures_noOutputMeasure() { final TestMeasureComputerContext measureContext = harness.createMeasureContextForUnitTest("compKey"); computer.compute(measureContext); @@ -77,12 +72,10 @@ public void compute_noInputMeasures_noOutputMeasure() { //no measure is created if component contains no/0 input values assertNull(total); - } @Test - public void compute_experimentalFeaturesDisable_noOutputMeasure() { - + void compute_experimentalFeaturesDisable_noOutputMeasure() { harness.enableExperimentalFeatures(false); final TestMeasureComputerContext measureContext = harness.createMeasureContextForUnitTest("compKey"); @@ -95,12 +88,10 @@ public void compute_experimentalFeaturesDisable_noOutputMeasure() { //no measure is created if component contains no/0 input values assertNull(total); - } @Test - public void compute_directory_aggregatedOutputMeasure() { - + void compute_directory_aggregatedOutputMeasure() { final TestMeasureComputerContext measureContext = harness.createMeasureContext("compKey", Component.Type.DIRECTORY); measureContext.addChildrenMeasures(UTILITY_GLOBAL_MUTATIONS_KEY, 10); measureContext.addChildrenMeasures(TEST_KILLS_KEY, 1); @@ -110,12 +101,10 @@ public void compute_directory_aggregatedOutputMeasure() { Measure ratio = measureContext.getMeasure(TEST_KILL_RATIO_KEY); assertEquals(10.0, ratio.getDoubleValue(), 0.05); - } @Test - public void compute_sourceFileIsNoUnitTest_noOutputMeasure() { - + void compute_sourceFileIsNoUnitTest_noOutputMeasure() { final TestMeasureComputerContext measureContext = harness.createMeasureContext("compKey", Component.Type.FILE); measureContext.addInputMeasure(UTILITY_GLOBAL_MUTATIONS_KEY, 10); measureContext.addInputMeasure(TEST_KILLS_KEY, 1); @@ -126,12 +115,10 @@ public void compute_sourceFileIsNoUnitTest_noOutputMeasure() { //no measure is created if component contains no/0 input values assertNull(total); - } @Test - public void compute_oneInputMeasure_computesOutputMeasure() { - + void compute_oneInputMeasure_computesOutputMeasure() { final TestMeasureComputerContext measureContext = harness.createMeasureContextForUnitTest("compKey"); measureContext.addInputMeasure(UTILITY_GLOBAL_MUTATIONS_KEY, 10); @@ -140,14 +127,11 @@ public void compute_oneInputMeasure_computesOutputMeasure() { computer.compute(measureContext); Measure ratio = measureContext.getMeasure(TEST_KILL_RATIO_KEY); - assertEquals(10.0, ratio.getDoubleValue(), 0.05); - } @Test - public void compute_globalMutations0_noOutputMeasure() { - + void compute_globalMutations0_noOutputMeasure() { final TestMeasureComputerContext measureContext = harness.createMeasureContextForUnitTest("compKey"); measureContext.addInputMeasure(UTILITY_GLOBAL_MUTATIONS_KEY, 0); @@ -156,39 +140,32 @@ public void compute_globalMutations0_noOutputMeasure() { computer.compute(measureContext); Measure ratio = measureContext.getMeasure(TEST_KILL_RATIO_KEY); - assertNull(ratio); - } @Test - public void compute_noGlobalMetrics_noOutputMeasure() { - + void compute_noGlobalMetrics_noOutputMeasure() { final TestMeasureComputerContext measureContext = harness.createMeasureContextForUnitTest("compKey"); - measureContext.addChildrenMeasures(TEST_KILLS_KEY, 3,2, 1); + measureContext.addChildrenMeasures(TEST_KILLS_KEY, 3, 2, 1); computer.compute(measureContext); Measure ratio = measureContext.getMeasure(TEST_KILL_RATIO_KEY); - assertNull(ratio); - } @Test - public void compute_childInputMeasure_computesOutputMeasure() { - + void compute_childInputMeasure_computesOutputMeasure() { final TestMeasureComputerContext measureContext = harness.createMeasureContextForUnitTest("compKey"); measureContext.addChildrenMeasures(UTILITY_GLOBAL_MUTATIONS_KEY, 10, 10, 10, 10); - measureContext.addChildrenMeasures(TEST_KILLS_KEY, 3,2, 1); + measureContext.addChildrenMeasures(TEST_KILLS_KEY, 3, 2, 1); computer.compute(measureContext); Measure ratio = measureContext.getMeasure(TEST_KILL_RATIO_KEY); - assertEquals(60.0, ratio.getDoubleValue(), 0.05); - } + } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/TotalMutationsComputerTest.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/TotalMutationsComputerTest.java index 7a95b0c..31ab995 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/TotalMutationsComputerTest.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/TotalMutationsComputerTest.java @@ -20,22 +20,21 @@ package ch.devcon5.sonar.plugins.mutationanalysis.metrics; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.util.Arrays; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import ch.devcon5.sonar.plugins.mutationanalysis.testharness.MeasureComputerTestHarness; -import org.junit.Before; -import org.junit.Test; +import java.util.Arrays; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.sonar.api.ce.measure.Measure; import org.sonar.api.ce.measure.MeasureComputer; import org.sonar.api.ce.measure.test.TestMeasureComputerContext; import org.sonar.api.ce.measure.test.TestMeasureComputerDefinitionContext; /** - * + *Total Mutations Computer Tests */ public class TotalMutationsComputerTest { @@ -51,27 +50,25 @@ public class TotalMutationsComputerTest { private TotalMutationsComputer computer; - @Before + @BeforeEach public void setUp() throws Exception { this.harness = MeasureComputerTestHarness.createFor(TotalMutationsComputer.class); this.computer = harness.getComputer(); } @Test - public void define() { - + void define() { final TestMeasureComputerDefinitionContext context = new TestMeasureComputerDefinitionContext(); - final MeasureComputer.MeasureComputerDefinition def = computer.define(context); assertTrue(def.getInputMetrics().containsAll(Arrays.asList(GLOBAL_MUTATIONS, GLOBAL_ALIVE, MUTATIONS_TOTAL, MUTATIONS_ALIVE))); assertTrue(def.getOutputMetrics().containsAll(Arrays.asList(TOTAL_PERCENT, ALIVE_PERCENT))); - } @Test - public void compute_onModuleRootFolder_noComputation() throws Exception { - final TestMeasureComputerContext measureContext = addValidInputMeasures(harness.createMeasureContextForDirectory("module:/")); + void compute_onModuleRootFolder_noComputation() { + final TestMeasureComputerContext measureContext = addValidInputMeasures( + harness.createMeasureContextForDirectory("module:/")); computer.compute(measureContext); @@ -79,32 +76,39 @@ public void compute_onModuleRootFolder_noComputation() throws Exception { } @Test - public void compute_onPomFile_noComputation() throws Exception { - final TestMeasureComputerContext measureContext = addValidInputMeasures(harness.createMeasureContextForSourceFile("module:pom.xml")); + void compute_onPomFile_noComputation() { + final TestMeasureComputerContext measureContext = addValidInputMeasures( + harness.createMeasureContextForSourceFile("module:pom.xml")); computer.compute(measureContext); assertNoComputation(measureContext); } + @Test - public void compute_onUnitTest_noComputation() throws Exception { - final TestMeasureComputerContext measureContext = addValidInputMeasures(harness.createMeasureContextForUnitTest("module:src/test/Test.java")); + void compute_onUnitTest_noComputation() { + final TestMeasureComputerContext measureContext = addValidInputMeasures( + harness.createMeasureContextForUnitTest("module:src/test/Test.java")); computer.compute(measureContext); assertNoComputation(measureContext); } + @Test - public void compute_onTestSrcFolder_noComputation() throws Exception { - final TestMeasureComputerContext measureContext = addValidInputMeasures(harness.createMeasureContextForDirectory("module:src/test/")); + void compute_onTestSrcFolder_noComputation() { + final TestMeasureComputerContext measureContext = addValidInputMeasures( + harness.createMeasureContextForDirectory("module:src/test/")); computer.compute(measureContext); assertNoComputation(measureContext); } + @Test - public void compute_onSourceInTestFolder_noComputation() throws Exception { - final TestMeasureComputerContext measureContext = addValidInputMeasures(harness.createMeasureContextForSourceFile("module:src/test/NoTest.java")); + void compute_onSourceInTestFolder_noComputation() { + final TestMeasureComputerContext measureContext = addValidInputMeasures( + harness.createMeasureContextForSourceFile("module:src/test/NoTest.java")); computer.compute(measureContext); @@ -112,21 +116,19 @@ public void compute_onSourceInTestFolder_noComputation() throws Exception { } @Test - public void compute_experimentalFeaturesDisabled_noMeasure() { - + void compute_experimentalFeaturesDisabled_noMeasure() { harness.enableExperimentalFeatures(false); - final TestMeasureComputerContext measureContext = addValidInputMeasures(harness.createMeasureContextForSourceFile("compKey")); + final TestMeasureComputerContext measureContext = addValidInputMeasures( + harness.createMeasureContextForSourceFile("compKey")); computer.compute(measureContext); assertNoComputation(measureContext); - } @Test - public void compute_noInputMeasures_noOutputValues() { - + void compute_noInputMeasures_noOutputValues() { final TestMeasureComputerContext measureContext = harness.createMeasureContextForSourceFile("compKey"); computer.compute(measureContext); @@ -135,8 +137,7 @@ public void compute_noInputMeasures_noOutputValues() { } @Test - public void compute_noGlobalMutations_noOutputValues() { - + void compute_noGlobalMutations_noOutputValues() { final TestMeasureComputerContext measureContext = harness.createMeasureContextForSourceFile("compKey"); measureContext.addInputMeasure(GLOBAL_MUTATIONS, 0); @@ -151,8 +152,7 @@ public void compute_noGlobalMutations_noOutputValues() { } @Test - public void compute_onSourceFile_withComponentInputMeasures_correctOutputValues() { - + void compute_onSourceFile_withComponentInputMeasures_correctOutputValues() { final TestMeasureComputerContext measureContext = harness.createMeasureContextForSourceFile("compKey"); //80% of all mutations are in this component @@ -174,13 +174,12 @@ public void compute_onSourceFile_withComponentInputMeasures_correctOutputValues( } @Test - public void compute_onModule_withChildInputMeasure_correctOutputValues() { - + void compute_onModule_withChildInputMeasure_correctOutputValues() { final TestMeasureComputerContext measureContext = harness.createMeasureContextForModule("compKey"); //80% of all mutations are in the child components - measureContext.addChildrenMeasures(GLOBAL_MUTATIONS, 10,10,10,10); - measureContext.addChildrenMeasures(MUTATIONS_TOTAL, 4,3,1); + measureContext.addChildrenMeasures(GLOBAL_MUTATIONS, 10, 10, 10, 10); + measureContext.addChildrenMeasures(MUTATIONS_TOTAL, 4, 3, 1); //60% of all alive mutations are in the child components measureContext.addChildrenMeasures(GLOBAL_ALIVE, 5, 5, 5, 5); @@ -193,17 +192,15 @@ public void compute_onModule_withChildInputMeasure_correctOutputValues() { assertEquals(80.0, total.getDoubleValue(), 0.05); assertEquals(60.0, alive.getDoubleValue(), 0.05); - } @Test - public void compute_onFolder_withChildInputMeasure_correctOutputValues() { - + void compute_onFolder_withChildInputMeasure_correctOutputValues() { final TestMeasureComputerContext measureContext = harness.createMeasureContextForDirectory("module:src/main/"); //80% of all mutations are in the child components - measureContext.addChildrenMeasures(GLOBAL_MUTATIONS, 10,10,10,10); - measureContext.addChildrenMeasures(MUTATIONS_TOTAL, 4,3,1); + measureContext.addChildrenMeasures(GLOBAL_MUTATIONS, 10, 10, 10, 10); + measureContext.addChildrenMeasures(MUTATIONS_TOTAL, 4, 3, 1); //60% of all alive mutations are in the child components measureContext.addChildrenMeasures(GLOBAL_ALIVE, 5, 5, 5, 5); @@ -216,12 +213,12 @@ public void compute_onFolder_withChildInputMeasure_correctOutputValues() { assertEquals(80.0, total.getDoubleValue(), 0.05); assertEquals(60.0, alive.getDoubleValue(), 0.05); - } /** * Assertion to check if no computation took place - * @param measureContext + * + * @param measureContext the context from which to get measures */ void assertNoComputation(final TestMeasureComputerContext measureContext) { assertNull(measureContext.getMeasure(TOTAL_PERCENT)); diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/BuilderTest.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/BuilderTest.java index b0d25c5..0b7ba2d 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/BuilderTest.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/BuilderTest.java @@ -20,499 +20,494 @@ package ch.devcon5.sonar.plugins.mutationanalysis.model; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import ch.devcon5.sonar.plugins.mutationanalysis.testharness.LogRecordingAppender; import org.apache.logging.log4j.Level; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -public class BuilderTest { - - @Rule - public ExpectedException expected = ExpectedException.none(); - private Mutant.Builder subject; - private LogRecordingAppender appender; - - @Before - public void setUp() throws Exception { - - subject = new Mutant.Builder(); - appender = new LogRecordingAppender(); - } - - @After - public void tearDown() throws Exception { - appender.close(); - } - - @Test - public void testBuild_withMinimalArguments() throws Exception { - - //@formatter:off - final Mutant mutant = subject - .mutantStatus(Mutant.State.KILLED) - .inSourceFile("someSource.java") - .inClass("some.package.SomeClass") - .inMethod("aMethod") - .withMethodParameters("methodDescription") - .usingMutator(MutationOperators.find("ARGUMENT_PROPAGATION")) - .killedBy("aTest") - .build(); - // @formatter:on - assertNotNull(mutant); - assertEquals(Mutant.State.KILLED, mutant.getState()); - assertEquals("someSource.java", mutant.getSourceFile()); - assertEquals("some.package.SomeClass", mutant.getMutatedClass()); - assertEquals("aMethod", mutant.getMutatedMethod()); - assertEquals("methodDescription", mutant.getMethodDescription()); - // implicit default values - assertEquals(0, mutant.getLineNumber()); - assertEquals(0, mutant.getIndex()); - assertTrue(mutant.isDetected()); - assertEquals("aTest", mutant.getKillingTest()); - - final MutationOperator mutationOperator = MutationOperators.find("ARGUMENT_PROPAGATION"); - assertNotNull(mutationOperator); - assertEquals(mutationOperator, mutant.getMutationOperator()); - assertEquals("", mutant.getMutatorSuffix()); - - } - - @Test - public void testBuild_detected() throws Exception { - - //@formatter:off - final Mutant mutant = subject - .mutantStatus(Mutant.State.KILLED) - .inSourceFile("someSource.java") - .inClass("some.package.SomeClass") - .inMethod("aMethod") - .withMethodParameters("methodDescription") - .usingMutator(MutationOperators.find("ARGUMENT_PROPAGATION")) - .killedBy("aTest") - .build(); - // @formatter:on - assertNotNull(mutant); - assertTrue(mutant.isDetected()); - assertEquals(Mutant.State.KILLED, mutant.getState()); - assertEquals("someSource.java", mutant.getSourceFile()); - assertEquals("some.package.SomeClass", mutant.getMutatedClass()); - assertEquals("aMethod", mutant.getMutatedMethod()); - assertEquals("methodDescription", mutant.getMethodDescription()); - // implicit default values - assertEquals(0, mutant.getLineNumber()); - assertEquals(0, mutant.getIndex()); - assertEquals("aTest", mutant.getKillingTest()); - - final MutationOperator mutationOperator = MutationOperators.find("ARGUMENT_PROPAGATION"); - assertNotNull(mutationOperator); - assertEquals(mutationOperator, mutant.getMutationOperator()); - assertEquals("", mutant.getMutatorSuffix()); - - } - - @Test - public void testBuild_StatusAsString() throws Exception { - - final Mutant mutant = subject.mutantStatus("KILLED") - .inSourceFile("someSource.java") - .inClass("some.package.SomeClass") - .inMethod("aMethod") - .withMethodParameters("methodDescription") - .usingMutator(MutationOperators.find("ARGUMENT_PROPAGATION")) - .killedBy("aTest") - .build(); - assertNotNull(mutant); - assertTrue(mutant.isDetected()); - assertEquals(Mutant.State.KILLED, mutant.getState()); - assertEquals("someSource.java", mutant.getSourceFile()); - assertEquals("some.package.SomeClass", mutant.getMutatedClass()); - assertEquals("aMethod", mutant.getMutatedMethod()); - assertEquals("methodDescription", mutant.getMethodDescription()); - // implicit default values - assertEquals(0, mutant.getLineNumber()); - assertEquals(0, mutant.getIndex()); - assertEquals("aTest", mutant.getKillingTest()); - - final MutationOperator mutationOperator = MutationOperators.find("ARGUMENT_PROPAGATION"); - assertNotNull(mutationOperator); - assertEquals(mutationOperator, mutant.getMutationOperator()); - assertEquals("", mutant.getMutatorSuffix()); - - } - - @Test - public void testBuild_nullStatusAsString_isUnknownState() throws Exception { - - final Mutant mutant = subject.mutantStatus((String) null) - .inSourceFile("someSource.java") - .inClass("some.package.SomeClass") - .inMethod("aMethod") - .withMethodParameters("methodDescription") - .usingMutator(MutationOperators.find("ARGUMENT_PROPAGATION")) - .killedBy("aTest") - .build(); - assertNotNull(mutant); - assertEquals(Mutant.State.UNKNOWN, mutant.getState()); - assertEquals("someSource.java", mutant.getSourceFile()); - assertEquals("some.package.SomeClass", mutant.getMutatedClass()); - assertEquals("aMethod", mutant.getMutatedMethod()); - assertEquals("methodDescription", mutant.getMethodDescription()); - // implicit default values - assertEquals(0, mutant.getLineNumber()); - assertEquals(0, mutant.getIndex()); - assertEquals("", mutant.getKillingTest()); - - final MutationOperator mutationOperator = MutationOperators.find("ARGUMENT_PROPAGATION"); - assertNotNull(mutationOperator); - assertEquals(mutationOperator, mutant.getMutationOperator()); - assertEquals("", mutant.getMutatorSuffix()); - - } - - @Test - public void testBuild_unknownMutatorAsString() throws Exception { - - final Mutant mutant = subject.mutantStatus(Mutant.State.UNKNOWN) - .inSourceFile("someSource.java") - .inClass("some.package.SomeClass") - .inMethod("aMethod") - .withMethodParameters("methodDescription") - .usingMutator("xyzabc") - .killedBy("aTest") - .build(); - assertNotNull(mutant); - assertEquals(Mutant.State.UNKNOWN, mutant.getState()); - assertEquals("someSource.java", mutant.getSourceFile()); - assertEquals("some.package.SomeClass", mutant.getMutatedClass()); - assertEquals("aMethod", mutant.getMutatedMethod()); - assertEquals("methodDescription", mutant.getMethodDescription()); - // implicit default values - assertEquals(0, mutant.getLineNumber()); - assertEquals(0, mutant.getIndex()); - assertEquals("", mutant.getKillingTest()); - - final MutationOperator mutationOperator = MutationOperators.UNKNOWN; - assertNotNull(mutationOperator); - assertEquals(mutationOperator, mutant.getMutationOperator()); - assertEquals("", mutant.getMutatorSuffix()); - - assertTrue(appender.getEvents().stream().anyMatch(e -> e.getLevel() == Level.WARN && "Found unknown mutation operator: xyzabc".equals(e.getMessage().getFormattedMessage()))); - - } - - @Test - public void testBuild_unknownMutator() throws Exception { - - final Mutant mutant = subject.mutantStatus(Mutant.State.UNKNOWN) - .inSourceFile("someSource.java") - .inClass("some.package.SomeClass") - .inMethod("aMethod") - .withMethodParameters("methodDescription") - .usingMutator(MutationOperators.UNKNOWN) - .killedBy("aTest") - .build(); - assertNotNull(mutant); - assertEquals(Mutant.State.UNKNOWN, mutant.getState()); - assertEquals("someSource.java", mutant.getSourceFile()); - assertEquals("some.package.SomeClass", mutant.getMutatedClass()); - assertEquals("aMethod", mutant.getMutatedMethod()); - assertEquals("methodDescription", mutant.getMethodDescription()); - // implicit default values - assertEquals(0, mutant.getLineNumber()); - assertEquals(0, mutant.getIndex()); - assertEquals("", mutant.getKillingTest()); - - final MutationOperator mutationOperator = MutationOperators.UNKNOWN; - assertNotNull(mutationOperator); - assertEquals(mutationOperator, mutant.getMutationOperator()); - assertEquals("", mutant.getMutatorSuffix()); - - } - - @Test - public void testBuild_MutatorAsStringId() throws Exception { - - final Mutant mutant = subject.mutantStatus(Mutant.State.KILLED) - .inSourceFile("someSource.java") - .inClass("some.package.SomeClass") - .inMethod("aMethod") - .withMethodParameters("methodDescription") - .usingMutator("ARGUMENT_PROPAGATION") - .killedBy("aTest") - .build(); - assertNotNull(mutant); - assertEquals(Mutant.State.KILLED, mutant.getState()); - assertEquals("someSource.java", mutant.getSourceFile()); - assertEquals("some.package.SomeClass", mutant.getMutatedClass()); - assertEquals("aMethod", mutant.getMutatedMethod()); - assertEquals("methodDescription", mutant.getMethodDescription()); - // implicit default values - assertEquals(0, mutant.getLineNumber()); - assertEquals(0, mutant.getIndex()); - assertTrue(mutant.isDetected()); - assertEquals("aTest", mutant.getKillingTest()); - - final MutationOperator mutationOperator = MutationOperators.find("ARGUMENT_PROPAGATION"); - assertNotNull(mutationOperator); - assertEquals(mutationOperator, mutant.getMutationOperator()); - assertEquals("", mutant.getMutatorSuffix()); - - } - - @Test - public void testBuild_MutatorAsStringWithSuffix() throws Exception { - - final Mutant mutant = subject.mutantStatus(Mutant.State.KILLED) - .inSourceFile("someSource.java") - .inClass("some.package.SomeClass") - .inMethod("aMethod") - .withMethodParameters("methodDescription") - .usingMutator("org.pitest.mutationtest.engine.gregor.mutators.RemoveConditionalMutator_A_SUFFIX") - .killedBy("aTest") - .build(); - assertNotNull(mutant); - assertEquals(Mutant.State.KILLED, mutant.getState()); - assertEquals("someSource.java", mutant.getSourceFile()); - assertEquals("some.package.SomeClass", mutant.getMutatedClass()); - assertEquals("aMethod", mutant.getMutatedMethod()); - assertEquals("methodDescription", mutant.getMethodDescription()); - // implicit default values - assertEquals(0, mutant.getLineNumber()); - assertEquals(0, mutant.getIndex()); - assertTrue(mutant.isDetected()); - assertEquals("aTest", mutant.getKillingTest()); - - final MutationOperator mutationOperator = MutationOperators.find("org.pitest.mutationtest.engine.gregor.mutators.RemoveConditionalMutator"); - assertNotNull(mutationOperator); - assertEquals(mutationOperator, mutant.getMutationOperator()); - assertEquals("A_SUFFIX", mutant.getMutatorSuffix()); - - } - - @Test - public void testBuild_invalidStatus() throws Exception { - - final Mutant mutant = subject.mutantStatus("invalid") - .inSourceFile("someSource.java") - .inClass("some.package.SomeClass") - .inMethod("aMethod") - .withMethodParameters("methodDescription") - .usingMutator(MutationOperators.find("ARGUMENT_PROPAGATION")) - .build(); - assertNotNull(mutant); - assertFalse(mutant.isDetected()); - assertEquals(Mutant.State.UNKNOWN, mutant.getState()); - assertEquals("someSource.java", mutant.getSourceFile()); - assertEquals("some.package.SomeClass", mutant.getMutatedClass()); - assertEquals("aMethod", mutant.getMutatedMethod()); - assertEquals("methodDescription", mutant.getMethodDescription()); - // implicit default values - assertEquals(0, mutant.getLineNumber()); - assertEquals(0, mutant.getIndex()); - assertEquals("", mutant.getKillingTest()); - - final MutationOperator mutationOperator = MutationOperators.find("ARGUMENT_PROPAGATION"); - assertNotNull(mutationOperator); - assertEquals(mutationOperator, mutant.getMutationOperator()); - assertEquals("", mutant.getMutatorSuffix()); - - } - - @Test - public void testBuild_withLineAndIndex() throws Exception { - - final Mutant mutant = subject.mutantStatus(Mutant.State.KILLED) - .inSourceFile("someSource.java") - .inClass("some.package.SomeClass") - .inMethod("aMethod") - .inLine(123) - .atIndex(456) - .withMethodParameters("methodDescription") - .usingMutator(MutationOperators.find("ARGUMENT_PROPAGATION")) - .killedBy("aTest") - .build(); - assertNotNull(mutant); - assertEquals(Mutant.State.KILLED, mutant.getState()); - assertEquals("someSource.java", mutant.getSourceFile()); - assertEquals("some.package.SomeClass", mutant.getMutatedClass()); - assertEquals("aMethod", mutant.getMutatedMethod()); - assertEquals("methodDescription", mutant.getMethodDescription()); - // implicit default values - assertEquals(123, mutant.getLineNumber()); - assertEquals(456, mutant.getIndex()); - assertTrue(mutant.isDetected()); - assertEquals("aTest", mutant.getKillingTest()); - - final MutationOperator mutationOperator = MutationOperators.find("ARGUMENT_PROPAGATION"); - assertNotNull(mutationOperator); - assertEquals(mutationOperator, mutant.getMutationOperator()); - assertEquals("", mutant.getMutatorSuffix()); - - } - - @Test - public void testBuild_withKillingTest() throws Exception { - - final Mutant mutant = subject.mutantStatus(Mutant.State.KILLED) - .inSourceFile("someSource.java") - .inClass("some.package.SomeClass") - .inMethod("aMethod") - .withMethodParameters("methodDescription") - .usingMutator(MutationOperators.find("ARGUMENT_PROPAGATION")) - .killedBy("killingTest") - .build(); - assertNotNull(mutant); - assertEquals(Mutant.State.KILLED, mutant.getState()); - assertEquals("someSource.java", mutant.getSourceFile()); - assertEquals("some.package.SomeClass", mutant.getMutatedClass()); - assertEquals("aMethod", mutant.getMutatedMethod()); - assertEquals("methodDescription", mutant.getMethodDescription()); - assertEquals("killingTest", mutant.getKillingTest()); - // implicit default values - assertEquals(0, mutant.getLineNumber()); - assertEquals(0, mutant.getIndex()); - assertTrue(mutant.isDetected()); - - final MutationOperator mutationOperator = MutationOperators.find("ARGUMENT_PROPAGATION"); - assertNotNull(mutationOperator); - assertEquals(mutationOperator, mutant.getMutationOperator()); - assertEquals("", mutant.getMutatorSuffix()); - - } - - @Test - public void test_null_state_NullPointerException() throws Exception { - expected.expect(IllegalArgumentException.class); - expected.expectMessage("state must be set"); - Mutant.builder() - .inSourceFile("aFile.java") - .inClass("AClass") - .inMethod("aMethod") - .withMethodParameters("desc") - .inLine(1) - .atIndex(0) - .usingMutator(MutationOperators.UNKNOWN) - .killedBy("aTest") - .build(); - - } - - @Test - public void test_null_sourceFile_NullPointerException() throws Exception { - expected.expect(IllegalArgumentException.class); - expected.expectMessage("sourceFile must be set"); - Mutant.builder() - .mutantStatus(Mutant.State.KILLED) - .inClass("AClass") - .inMethod("aMethod") - .withMethodParameters("desc") - .inLine(1) - .atIndex(0) - .usingMutator(MutationOperators.UNKNOWN) - .killedBy("aTest") - .build(); - } - - @Test - public void test_null_mutatedClass_NullPointerException() throws Exception { - expected.expect(IllegalArgumentException.class); - expected.expectMessage("mutatedClass must be set"); - Mutant.builder() - .mutantStatus(Mutant.State.KILLED) - .inSourceFile("aFile.java") - .inMethod("aMethod") - .withMethodParameters("desc") - .inLine(1) - .atIndex(0) - .usingMutator(MutationOperators.UNKNOWN) - .killedBy("aTest") - .build(); - } - - @Test - public void test_null_mutatedMethod_NullPointerException() throws Exception { - expected.expect(IllegalArgumentException.class); - expected.expectMessage("mutatedMethod must be set"); - Mutant.builder() - .mutantStatus(Mutant.State.KILLED) - .inSourceFile("aFile.java") - .inClass("AClass") - .withMethodParameters("desc") - .inLine(1) - .atIndex(0) - .usingMutator(MutationOperators.UNKNOWN) - .killedBy("aTest") - .build(); - } - - @Test - public void test_null_mutatedMethodDesc_NullPointerException() throws Exception { - expected.expect(IllegalArgumentException.class); - expected.expectMessage("methodDescription must be set"); - Mutant.builder() - .mutantStatus(Mutant.State.KILLED) - .inSourceFile("aFile.java") - .inClass("AClass") - .inMethod("aMethod") - .inLine(1) - .atIndex(0) - .usingMutator(MutationOperators.UNKNOWN) - .killedBy("aTest") - .build(); - } - - @Test - public void test_null_mutationOperator_NullPointerException() throws Exception { - expected.expect(IllegalArgumentException.class); - expected.expectMessage("mutationOperator must be set"); - Mutant.builder() - .mutantStatus(Mutant.State.KILLED) - .inSourceFile("aFile.java") - .inClass("AClass") - .inMethod("aMethod") - .withMethodParameters("desc") - .inLine(1) - .atIndex(0) - .killedBy("aTest") - .build(); - } - - @Test - public void test_null_killingTest_NullPointerException() throws Exception { - expected.expect(IllegalArgumentException.class); - expected.expectMessage("killingTest must be set"); - Mutant.builder() - .mutantStatus(Mutant.State.KILLED) - .inSourceFile("aFile.java") - .inClass("AClass") - .inMethod("aMethod") - .withMethodParameters("desc") - .inLine(1) - .atIndex(0) - .usingMutator(MutationOperators.UNKNOWN) - .build(); - } - - @Test - public void test_null_killingTest_notDetected() throws Exception { - final Mutant mutant = Mutant.builder() - .mutantStatus(Mutant.State.NO_COVERAGE) - .inSourceFile("aFile.java") - .inClass("AClass") - .inMethod("aMethod") - .withMethodParameters("desc") - .inLine(1) - .atIndex(0) - .usingMutator(MutationOperators.UNKNOWN) - .build(); - assertFalse(mutant.isDetected()); - } +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class BuilderTest { + + private Mutant.Builder subject; + private LogRecordingAppender appender; + + @BeforeEach + public void setUp() { + subject = new Mutant.Builder(); + appender = new LogRecordingAppender(); + } + + @AfterEach + public void tearDown() throws Exception { + appender.close(); + } + + @Test + void testBuild_withMinimalArguments() { + //@formatter:off + final Mutant mutant = subject + .mutantStatus(Mutant.State.KILLED) + .inSourceFile("someSource.java") + .inClass("some.package.SomeClass") + .inMethod("aMethod") + .withMethodParameters("methodDescription") + .usingMutator(MutationOperators.find("ARGUMENT_PROPAGATION")) + .killedBy("aTest") + .build(); + + // @formatter:on + assertNotNull(mutant); + assertEquals(Mutant.State.KILLED, mutant.getState()); + assertEquals("someSource.java", mutant.getSourceFile()); + assertEquals("some.package.SomeClass", mutant.getMutatedClass()); + assertEquals("aMethod", mutant.getMutatedMethod()); + assertEquals("methodDescription", mutant.getMethodDescription()); + + // implicit default values + assertEquals(0, mutant.getLineNumber()); + assertEquals(0, mutant.getIndex()); + assertTrue(mutant.isDetected()); + assertEquals("aTest", mutant.getKillingTest()); + + final MutationOperator mutationOperator = MutationOperators.find("ARGUMENT_PROPAGATION"); + assertNotNull(mutationOperator); + assertEquals(mutationOperator, mutant.getMutationOperator()); + assertEquals("", mutant.getMutatorSuffix()); + } + + @Test + void testBuild_detected() { + //@formatter:off + final Mutant mutant = subject + .mutantStatus(Mutant.State.KILLED) + .inSourceFile("someSource.java") + .inClass("some.package.SomeClass") + .inMethod("aMethod") + .withMethodParameters("methodDescription") + .usingMutator(MutationOperators.find("ARGUMENT_PROPAGATION")) + .killedBy("aTest") + .build(); + // @formatter:on + assertNotNull(mutant); + assertTrue(mutant.isDetected()); + assertEquals(Mutant.State.KILLED, mutant.getState()); + assertEquals("someSource.java", mutant.getSourceFile()); + assertEquals("some.package.SomeClass", mutant.getMutatedClass()); + assertEquals("aMethod", mutant.getMutatedMethod()); + assertEquals("methodDescription", mutant.getMethodDescription()); + // implicit default values + assertEquals(0, mutant.getLineNumber()); + assertEquals(0, mutant.getIndex()); + assertEquals("aTest", mutant.getKillingTest()); + + final MutationOperator mutationOperator = MutationOperators.find("ARGUMENT_PROPAGATION"); + assertNotNull(mutationOperator); + assertEquals(mutationOperator, mutant.getMutationOperator()); + assertEquals("", mutant.getMutatorSuffix()); + } + + @Test + void testBuild_StatusAsString() { + final Mutant mutant = subject.mutantStatus("KILLED") + .inSourceFile("someSource.java") + .inClass("some.package.SomeClass") + .inMethod("aMethod") + .withMethodParameters("methodDescription") + .usingMutator(MutationOperators.find("ARGUMENT_PROPAGATION")) + .killedBy("aTest") + .build(); + assertNotNull(mutant); + assertTrue(mutant.isDetected()); + assertEquals(Mutant.State.KILLED, mutant.getState()); + assertEquals("someSource.java", mutant.getSourceFile()); + assertEquals("some.package.SomeClass", mutant.getMutatedClass()); + assertEquals("aMethod", mutant.getMutatedMethod()); + assertEquals("methodDescription", mutant.getMethodDescription()); + + // implicit default values + assertEquals(0, mutant.getLineNumber()); + assertEquals(0, mutant.getIndex()); + assertEquals("aTest", mutant.getKillingTest()); + + final MutationOperator mutationOperator = MutationOperators.find("ARGUMENT_PROPAGATION"); + assertNotNull(mutationOperator); + assertEquals(mutationOperator, mutant.getMutationOperator()); + assertEquals("", mutant.getMutatorSuffix()); + } + + @Test + void testBuild_nullStatusAsString_isUnknownState() { + final Mutant mutant = subject.mutantStatus((String) null) + .inSourceFile("someSource.java") + .inClass("some.package.SomeClass") + .inMethod("aMethod") + .withMethodParameters("methodDescription") + .usingMutator(MutationOperators.find("ARGUMENT_PROPAGATION")) + .killedBy("aTest") + .build(); + assertNotNull(mutant); + assertEquals(Mutant.State.UNKNOWN, mutant.getState()); + assertEquals("someSource.java", mutant.getSourceFile()); + assertEquals("some.package.SomeClass", mutant.getMutatedClass()); + assertEquals("aMethod", mutant.getMutatedMethod()); + assertEquals("methodDescription", mutant.getMethodDescription()); + + // implicit default values + assertEquals(0, mutant.getLineNumber()); + assertEquals(0, mutant.getIndex()); + assertEquals("", mutant.getKillingTest()); + + final MutationOperator mutationOperator = MutationOperators.find("ARGUMENT_PROPAGATION"); + assertNotNull(mutationOperator); + assertEquals(mutationOperator, mutant.getMutationOperator()); + assertEquals("", mutant.getMutatorSuffix()); + } + + @Test + void testBuild_unknownMutatorAsString() { + final Mutant mutant = subject.mutantStatus(Mutant.State.UNKNOWN) + .inSourceFile("someSource.java") + .inClass("some.package.SomeClass") + .inMethod("aMethod") + .withMethodParameters("methodDescription") + .usingMutator("xyzabc") + .killedBy("aTest") + .build(); + assertNotNull(mutant); + assertEquals(Mutant.State.UNKNOWN, mutant.getState()); + assertEquals("someSource.java", mutant.getSourceFile()); + assertEquals("some.package.SomeClass", mutant.getMutatedClass()); + assertEquals("aMethod", mutant.getMutatedMethod()); + assertEquals("methodDescription", mutant.getMethodDescription()); + + // implicit default values + assertEquals(0, mutant.getLineNumber()); + assertEquals(0, mutant.getIndex()); + assertEquals("", mutant.getKillingTest()); + + final MutationOperator mutationOperator = MutationOperators.UNKNOWN; + assertNotNull(mutationOperator); + assertEquals(mutationOperator, mutant.getMutationOperator()); + assertEquals("", mutant.getMutatorSuffix()); + + assertTrue(appender.getEvents().stream().anyMatch( + e -> e.getLevel() == Level.WARN && "Found unknown mutation operator: xyzabc".equals( + e.getMessage().getFormattedMessage()))); + } + + @Test + void testBuild_unknownMutator() { + final Mutant mutant = subject.mutantStatus(Mutant.State.UNKNOWN) + .inSourceFile("someSource.java") + .inClass("some.package.SomeClass") + .inMethod("aMethod") + .withMethodParameters("methodDescription") + .usingMutator(MutationOperators.UNKNOWN) + .killedBy("aTest") + .build(); + assertNotNull(mutant); + assertEquals(Mutant.State.UNKNOWN, mutant.getState()); + assertEquals("someSource.java", mutant.getSourceFile()); + assertEquals("some.package.SomeClass", mutant.getMutatedClass()); + assertEquals("aMethod", mutant.getMutatedMethod()); + assertEquals("methodDescription", mutant.getMethodDescription()); + + // implicit default values + assertEquals(0, mutant.getLineNumber()); + assertEquals(0, mutant.getIndex()); + assertEquals("", mutant.getKillingTest()); + + final MutationOperator mutationOperator = MutationOperators.UNKNOWN; + assertNotNull(mutationOperator); + assertEquals(mutationOperator, mutant.getMutationOperator()); + assertEquals("", mutant.getMutatorSuffix()); + } + + @Test + void testBuild_MutatorAsStringId() { + final Mutant mutant = subject.mutantStatus(Mutant.State.KILLED) + .inSourceFile("someSource.java") + .inClass("some.package.SomeClass") + .inMethod("aMethod") + .withMethodParameters("methodDescription") + .usingMutator("ARGUMENT_PROPAGATION") + .killedBy("aTest") + .build(); + assertNotNull(mutant); + assertEquals(Mutant.State.KILLED, mutant.getState()); + assertEquals("someSource.java", mutant.getSourceFile()); + assertEquals("some.package.SomeClass", mutant.getMutatedClass()); + assertEquals("aMethod", mutant.getMutatedMethod()); + assertEquals("methodDescription", mutant.getMethodDescription()); + + // implicit default values + assertEquals(0, mutant.getLineNumber()); + assertEquals(0, mutant.getIndex()); + assertTrue(mutant.isDetected()); + assertEquals("aTest", mutant.getKillingTest()); + + final MutationOperator mutationOperator = MutationOperators.find("ARGUMENT_PROPAGATION"); + assertNotNull(mutationOperator); + assertEquals(mutationOperator, mutant.getMutationOperator()); + assertEquals("", mutant.getMutatorSuffix()); + } + + @Test + void testBuild_MutatorAsStringWithSuffix() { + final Mutant mutant = subject.mutantStatus(Mutant.State.KILLED) + .inSourceFile("someSource.java") + .inClass("some.package.SomeClass") + .inMethod("aMethod") + .withMethodParameters("methodDescription") + .usingMutator("org.pitest.mutationtest.engine.gregor.mutators.RemoveConditionalMutator_A_SUFFIX") + .killedBy("aTest") + .build(); + assertNotNull(mutant); + assertEquals(Mutant.State.KILLED, mutant.getState()); + assertEquals("someSource.java", mutant.getSourceFile()); + assertEquals("some.package.SomeClass", mutant.getMutatedClass()); + assertEquals("aMethod", mutant.getMutatedMethod()); + assertEquals("methodDescription", mutant.getMethodDescription()); + + // implicit default values + assertEquals(0, mutant.getLineNumber()); + assertEquals(0, mutant.getIndex()); + assertTrue(mutant.isDetected()); + assertEquals("aTest", mutant.getKillingTest()); + + final MutationOperator mutationOperator = MutationOperators.find( + "org.pitest.mutationtest.engine.gregor.mutators.RemoveConditionalMutator"); + assertNotNull(mutationOperator); + assertEquals(mutationOperator, mutant.getMutationOperator()); + assertEquals("A_SUFFIX", mutant.getMutatorSuffix()); + } + + @Test + void testBuild_invalidStatus() { + final Mutant mutant = subject.mutantStatus("invalid") + .inSourceFile("someSource.java") + .inClass("some.package.SomeClass") + .inMethod("aMethod") + .withMethodParameters("methodDescription") + .usingMutator(MutationOperators.find("ARGUMENT_PROPAGATION")) + .build(); + assertNotNull(mutant); + assertFalse(mutant.isDetected()); + assertEquals(Mutant.State.UNKNOWN, mutant.getState()); + assertEquals("someSource.java", mutant.getSourceFile()); + assertEquals("some.package.SomeClass", mutant.getMutatedClass()); + assertEquals("aMethod", mutant.getMutatedMethod()); + assertEquals("methodDescription", mutant.getMethodDescription()); + + // implicit default values + assertEquals(0, mutant.getLineNumber()); + assertEquals(0, mutant.getIndex()); + assertEquals("", mutant.getKillingTest()); + + final MutationOperator mutationOperator = MutationOperators.find("ARGUMENT_PROPAGATION"); + assertNotNull(mutationOperator); + assertEquals(mutationOperator, mutant.getMutationOperator()); + assertEquals("", mutant.getMutatorSuffix()); + } + + @Test + void testBuild_withLineAndIndex() { + final Mutant mutant = subject.mutantStatus(Mutant.State.KILLED) + .inSourceFile("someSource.java") + .inClass("some.package.SomeClass") + .inMethod("aMethod") + .inLine(123) + .atIndex(456) + .withMethodParameters("methodDescription") + .usingMutator(MutationOperators.find("ARGUMENT_PROPAGATION")) + .killedBy("aTest") + .build(); + assertNotNull(mutant); + assertEquals(Mutant.State.KILLED, mutant.getState()); + assertEquals("someSource.java", mutant.getSourceFile()); + assertEquals("some.package.SomeClass", mutant.getMutatedClass()); + assertEquals("aMethod", mutant.getMutatedMethod()); + assertEquals("methodDescription", mutant.getMethodDescription()); + + // implicit default values + assertEquals(123, mutant.getLineNumber()); + assertEquals(456, mutant.getIndex()); + assertTrue(mutant.isDetected()); + assertEquals("aTest", mutant.getKillingTest()); + + final MutationOperator mutationOperator = MutationOperators.find("ARGUMENT_PROPAGATION"); + assertNotNull(mutationOperator); + assertEquals(mutationOperator, mutant.getMutationOperator()); + assertEquals("", mutant.getMutatorSuffix()); + } + + @Test + void testBuild_withKillingTest() { + final Mutant mutant = subject.mutantStatus(Mutant.State.KILLED) + .inSourceFile("someSource.java") + .inClass("some.package.SomeClass") + .inMethod("aMethod") + .withMethodParameters("methodDescription") + .usingMutator(MutationOperators.find("ARGUMENT_PROPAGATION")) + .killedBy("killingTest") + .build(); + assertNotNull(mutant); + assertEquals(Mutant.State.KILLED, mutant.getState()); + assertEquals("someSource.java", mutant.getSourceFile()); + assertEquals("some.package.SomeClass", mutant.getMutatedClass()); + assertEquals("aMethod", mutant.getMutatedMethod()); + assertEquals("methodDescription", mutant.getMethodDescription()); + assertEquals("killingTest", mutant.getKillingTest()); + + // implicit default values + assertEquals(0, mutant.getLineNumber()); + assertEquals(0, mutant.getIndex()); + assertTrue(mutant.isDetected()); + + final MutationOperator mutationOperator = MutationOperators.find("ARGUMENT_PROPAGATION"); + assertNotNull(mutationOperator); + assertEquals(mutationOperator, mutant.getMutationOperator()); + assertEquals("", mutant.getMutatorSuffix()); + } + + @Test + void test_null_state_NullPointerException() { + Mutant.Builder builder = Mutant.builder() + .inSourceFile("aFile.java") + .inClass("AClass") + .inMethod("aMethod") + .withMethodParameters("desc") + .inLine(1) + .atIndex(0) + .usingMutator(MutationOperators.UNKNOWN) + .killedBy("aTest"); + + IllegalArgumentException thrownException = assertThrows(IllegalArgumentException.class, builder::build); + + assertEquals("state must be set", thrownException.getMessage()); + } + + @Test + void test_null_sourceFile_NullPointerException() { + Mutant.Builder builder = Mutant.builder() + .mutantStatus(Mutant.State.KILLED) + .inClass("AClass") + .inMethod("aMethod") + .withMethodParameters("desc") + .inLine(1) + .atIndex(0) + .usingMutator(MutationOperators.UNKNOWN) + .killedBy("aTest"); + + IllegalArgumentException thrownException = assertThrows(IllegalArgumentException.class, builder::build); + + assertEquals("sourceFile must be set", thrownException.getMessage()); + } + + @Test + void test_null_mutatedClass_NullPointerException() { + Mutant.Builder builder = Mutant.builder() + .mutantStatus(Mutant.State.KILLED) + .inSourceFile("aFile.java") + .inMethod("aMethod") + .withMethodParameters("desc") + .inLine(1) + .atIndex(0) + .usingMutator(MutationOperators.UNKNOWN) + .killedBy("aTest"); + + IllegalArgumentException thrownException = assertThrows(IllegalArgumentException.class, builder::build); + + assertEquals("mutatedClass must be set", thrownException.getMessage()); + } + + @Test + void test_null_mutatedMethod_NullPointerException() { + Mutant.Builder builder = Mutant.builder() + .mutantStatus(Mutant.State.KILLED) + .inSourceFile("aFile.java") + .inClass("AClass") + .withMethodParameters("desc") + .inLine(1) + .atIndex(0) + .usingMutator(MutationOperators.UNKNOWN) + .killedBy("aTest"); + + IllegalArgumentException thrownException = assertThrows(IllegalArgumentException.class, builder::build); + + assertEquals("mutatedMethod must be set", thrownException.getMessage()); + } + + @Test + void test_null_mutatedMethodDesc_NullPointerException() { + Mutant.Builder builder = Mutant.builder() + .mutantStatus(Mutant.State.KILLED) + .inSourceFile("aFile.java") + .inClass("AClass") + .inMethod("aMethod") + .inLine(1) + .atIndex(0) + .usingMutator(MutationOperators.UNKNOWN) + .killedBy("aTest"); + + IllegalArgumentException thrownException = assertThrows(IllegalArgumentException.class, builder::build); + + assertEquals("methodDescription must be set", thrownException.getMessage()); + } + + @Test + void test_null_mutationOperator_NullPointerException() { + Mutant.Builder builder = Mutant.builder() + .mutantStatus(Mutant.State.KILLED) + .inSourceFile("aFile.java") + .inClass("AClass") + .inMethod("aMethod") + .withMethodParameters("desc") + .inLine(1) + .atIndex(0) + .killedBy("aTest"); + + IllegalArgumentException thrownException = assertThrows(IllegalArgumentException.class, builder::build); + + assertEquals("mutationOperator must be set", thrownException.getMessage()); + } + + @Test + void test_null_killingTest_NullPointerException() { + Mutant.Builder builder = Mutant.builder() + .mutantStatus(Mutant.State.KILLED) + .inSourceFile("aFile.java") + .inClass("AClass") + .inMethod("aMethod") + .withMethodParameters("desc") + .inLine(1) + .atIndex(0) + .usingMutator(MutationOperators.UNKNOWN); + + IllegalArgumentException thrownException = assertThrows(IllegalArgumentException.class, builder::build); + + assertEquals("killingTest must be set", thrownException.getMessage()); + } + + @Test + void test_null_killingTest_notDetected() { + final Mutant mutant = Mutant.builder() + .mutantStatus(Mutant.State.NO_COVERAGE) + .inSourceFile("aFile.java") + .inClass("AClass") + .inMethod("aMethod") + .withMethodParameters("desc") + .inLine(1) + .atIndex(0) + .usingMutator(MutationOperators.UNKNOWN) + .build(); + assertFalse(mutant.isDetected()); + } + } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/MutantTest.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/MutantTest.java index a70cd54..0470edd 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/MutantTest.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/MutantTest.java @@ -20,489 +20,438 @@ package ch.devcon5.sonar.plugins.mutationanalysis.model; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class MutantTest { public static Mutant newUndetectedMutant() { - return Mutant.builder() - .mutantStatus(Mutant.State.NO_COVERAGE) - .inSourceFile("SomeClass.java") - .inClass("com.foo.bar.SomeClass") - .inMethod("anyMethod") - .withMethodParameters("anyMethodDesc") - .inLine(8) - .usingMutator(MutationOperators.find("INVERT_NEGS")) - .atIndex(10) - .numberOfTestsRun(123) - .killedBy("com.foo.bar.SomeClassKillingTest") - .build(); + .mutantStatus(Mutant.State.NO_COVERAGE) + .inSourceFile("SomeClass.java") + .inClass("com.foo.bar.SomeClass") + .inMethod("anyMethod") + .withMethodParameters("anyMethodDesc") + .inLine(8) + .usingMutator(MutationOperators.find("INVERT_NEGS")) + .atIndex(10) + .numberOfTestsRun(123) + .killedBy("com.foo.bar.SomeClassKillingTest") + .build(); } public static Mutant newDetectedMutant() { - return Mutant.builder() - .mutantStatus(Mutant.State.KILLED) - .inSourceFile("SomeClass.java") - .inClass("com.foo.bar.SomeClass") - .inMethod("anyMethod") - .withMethodParameters("anyMethodDesc") - .inLine(17) - .usingMutator(MutationOperators.find("INVERT_NEGS")) - .atIndex(5) - .numberOfTestsRun(256) - .killedBy("com.foo.bar.SomeClassKillingTest") - .build(); - } - - public static Mutant newSurvivedMutantWithSuffix(){ + .mutantStatus(Mutant.State.KILLED) + .inSourceFile("SomeClass.java") + .inClass("com.foo.bar.SomeClass") + .inMethod("anyMethod") + .withMethodParameters("anyMethodDesc") + .inLine(17) + .usingMutator(MutationOperators.find("INVERT_NEGS")) + .atIndex(5) + .numberOfTestsRun(256) + .killedBy("com.foo.bar.SomeClassKillingTest") + .build(); + } + + public static Mutant newSurvivedMutantWithSuffix() { return Mutant.builder() - .mutantStatus(Mutant.State.SURVIVED) - .inSourceFile("SomeClass.java") - .inClass("com.foo.bar.SomeClass") - .inMethod("anyMethod") - .withMethodParameters("anyMethodDesc") - .inLine(8) - .usingMutator("org.pitest.mutationtest.engine.gregor.mutators.RemoveConditionalMutator_EQUAL_ELSE") - .atIndex(10) - .killedBy("com.foo.bar.SomeClassKillingTest") - .numberOfTestsRun(42) - .withDescription("removed conditional - replaced equality check with false") - .build(); - } - - - @Test - public void testIsDetected_true() throws Exception { - + .mutantStatus(Mutant.State.SURVIVED) + .inSourceFile("SomeClass.java") + .inClass("com.foo.bar.SomeClass") + .inMethod("anyMethod") + .withMethodParameters("anyMethodDesc") + .inLine(8) + .usingMutator("org.pitest.mutationtest.engine.gregor.mutators.RemoveConditionalMutator_EQUAL_ELSE") + .atIndex(10) + .killedBy("com.foo.bar.SomeClassKillingTest") + .numberOfTestsRun(42) + .withDescription("removed conditional - replaced equality check with false") + .build(); + } + + @Test + void testIsDetected_true() { assertTrue(newDetectedMutant().isDetected()); } @Test - public void testIsDetected_false() throws Exception { - + void testIsDetected_false() { assertFalse(newUndetectedMutant().isDetected()); } @Test - public void testGetMutantStatus_killed() throws Exception { - + void testGetMutantStatus_killed() { assertEquals(Mutant.State.KILLED, newDetectedMutant().getState()); } @Test - public void testGetMutantStatus_noCoverage() throws Exception { - + void testGetMutantStatus_noCoverage() { assertEquals(Mutant.State.NO_COVERAGE, newUndetectedMutant().getState()); } @Test - public void testGetSourceFile() throws Exception { - + void testGetSourceFile() { assertEquals("SomeClass.java", newDetectedMutant().getSourceFile()); } @Test - public void testGetState_killed() throws Exception { - + void testGetState_killed() { assertEquals(Mutant.State.KILLED, newDetectedMutant().getState()); } @Test - public void testGetMutatedClass() throws Exception { - + void testGetMutatedClass() { assertEquals("com.foo.bar.SomeClass", newDetectedMutant().getMutatedClass()); } @Test - public void testGetMutatedMethod() throws Exception { - + void testGetMutatedMethod() { assertEquals("anyMethod", newDetectedMutant().getMutatedMethod()); } @Test - public void testGetMethodDescription() throws Exception { - + void testGetMethodDescription() { assertEquals("anyMethodDesc", newDetectedMutant().getMethodDescription()); } @Test - public void testGetDescription_defaultEmpty() throws Exception { - + void testGetDescription_defaultEmpty() { assertFalse(newDetectedMutant().getDescription().isPresent()); } @Test - public void testGetDescription() throws Exception { - + void testGetDescription() { + assertTrue(newSurvivedMutantWithSuffix().getDescription().isPresent()); assertEquals("removed conditional - replaced equality check with false", newSurvivedMutantWithSuffix().getDescription().get()); } @Test - public void testGetLineNumber() throws Exception { - + void testGetLineNumber() { assertEquals(17, newDetectedMutant().getLineNumber()); assertEquals(8, newUndetectedMutant().getLineNumber()); } @Test - public void testGetNumberOfTestsRun() throws Exception { - + void testGetNumberOfTestsRun() { assertEquals(256, newDetectedMutant().getNumberOfTestsRun()); } - @Test - public void testGetMutator() throws Exception { - + void testGetMutator() { final MutationOperator mutationOperator = MutationOperators.find("org.pitest.mutationtest.engine.gregor.mutators.InvertNegsMutator"); assertNotNull(mutationOperator); assertEquals(mutationOperator, newDetectedMutant().getMutationOperator()); } @Test - public void testGetMutatorSuffix_emptySuffix() throws Exception { - + void testGetMutatorSuffix_emptySuffix() { assertEquals("", newDetectedMutant().getMutatorSuffix()); - } @Test - public void testGetMutatorSuffix_nonEmptySuffix() throws Exception { - + void testGetMutatorSuffix_nonEmptySuffix() { Mutant mutant = newSurvivedMutantWithSuffix(); - assertEquals("EQUAL_ELSE", mutant.getMutatorSuffix()); - } @Test - public void testGetIndex() throws Exception { - + void testGetIndex() { assertEquals(5, newDetectedMutant().getIndex()); assertEquals(10, newUndetectedMutant().getIndex()); } @Test - public void testGetKillingTest() throws Exception { - + void testGetKillingTest() { assertEquals("com.foo.bar.SomeClassKillingTest", newDetectedMutant().getKillingTest()); } @Test - public void testToString() throws Exception { - + void testToString() { assertEquals("Mutant [sourceFile=SomeClass.java, " - + "mutatedClass=com.foo.bar.SomeClass, " - + "mutatedMethod=anyMethod, " - + "methodDescription=anyMethodDesc, " - + "lineNumber=17, " - + "state=KILLED, " - + "mutationOperator=Invert Negs Mutator, " - + "numberOfTestsRun=256, " - + "killingTest=com.foo.bar.SomeClassKillingTest]", - newDetectedMutant().toString()); - + + "mutatedClass=com.foo.bar.SomeClass, " + + "mutatedMethod=anyMethod, " + + "methodDescription=anyMethodDesc, " + + "lineNumber=17, " + + "state=KILLED, " + + "mutationOperator=Invert Negs Mutator, " + + "numberOfTestsRun=256, " + + "killingTest=com.foo.bar.SomeClassKillingTest]", + newDetectedMutant().toString()); } + @Test - public void testToString_withDescription() throws Exception { + void testToString_withDescription() { assertEquals("Mutant [sourceFile=SomeClass.java, " - + "mutatedClass=com.foo.bar.SomeClass, " - + "mutatedMethod=anyMethod, " - + "methodDescription=anyMethodDesc, " - + "lineNumber=8, " - + "state=SURVIVED, " - + "mutationOperator=Remove Conditional Mutator, " - + "numberOfTestsRun=42, " - + "killingTest=, " - + "description=removed conditional - replaced equality check with false]", - newSurvivedMutantWithSuffix().toString()); - + + "mutatedClass=com.foo.bar.SomeClass, " + + "mutatedMethod=anyMethod, " + + "methodDescription=anyMethodDesc, " + + "lineNumber=8, " + + "state=SURVIVED, " + + "mutationOperator=Remove Conditional Mutator, " + + "numberOfTestsRun=42, " + + "killingTest=, " + + "description=removed conditional - replaced equality check with false]", + newSurvivedMutantWithSuffix().toString()); } @Test - public void testEquals_same_true() throws Exception { - + void testEquals_same_true() { Mutant mutant = newDetectedMutant(); assertEquals(mutant, mutant); } @Test - public void testEquals_clone_true() throws Exception { - + void testEquals_clone_true() { final Mutant expected = newDetectedMutant(); final Mutant twin = newDetectedMutant(); assertEquals(expected, twin); } @Test - public void testEquals_differentDetected_false() throws Exception { - + void testEquals_differentDetected_false() { final Mutant expected = newDetectedMutant(); final Mutant other = newUndetectedMutant(); - assertNotEquals(expected, other); } @Test - public void testEquals_differentStatus_false() throws Exception { - + void testEquals_differentStatus_false() { final Mutant expected = newDetectedMutant(); final Mutant other = Mutant.builder() - .mutantStatus(Mutant.State.MEMORY_ERROR) - .inSourceFile(expected.getSourceFile()) - .inClass(expected.getMutatedClass()) - .inMethod(expected.getMutatedMethod()) - .inLine(expected.getLineNumber()) - .withMethodParameters(expected.getMethodDescription()) - .usingMutator(expected.getMutationOperator()) - .atIndex(expected.getIndex()) - .numberOfTestsRun(expected.getNumberOfTestsRun()) - .killedBy(expected.getKillingTest()) - .build(); - + .mutantStatus(Mutant.State.MEMORY_ERROR) + .inSourceFile(expected.getSourceFile()) + .inClass(expected.getMutatedClass()) + .inMethod(expected.getMutatedMethod()) + .inLine(expected.getLineNumber()) + .withMethodParameters(expected.getMethodDescription()) + .usingMutator(expected.getMutationOperator()) + .atIndex(expected.getIndex()) + .numberOfTestsRun(expected.getNumberOfTestsRun()) + .killedBy(expected.getKillingTest()) + .build(); assertNotEquals(expected, other); } @Test - public void testEquals_differentSourceFile_false() throws Exception { - + void testEquals_differentSourceFile_false() { final Mutant expected = newDetectedMutant(); final Mutant other = Mutant.builder() - .mutantStatus(expected.getState()) - .inSourceFile("other") - .inClass(expected.getMutatedClass()) - .inMethod(expected.getMutatedMethod()) - .inLine(expected.getLineNumber()) - .withMethodParameters(expected.getMethodDescription()) - .usingMutator(expected.getMutationOperator()) - .atIndex(expected.getIndex()) - .numberOfTestsRun(expected.getNumberOfTestsRun()) - .killedBy(expected.getKillingTest()) - .build(); + .mutantStatus(expected.getState()) + .inSourceFile("other") + .inClass(expected.getMutatedClass()) + .inMethod(expected.getMutatedMethod()) + .inLine(expected.getLineNumber()) + .withMethodParameters(expected.getMethodDescription()) + .usingMutator(expected.getMutationOperator()) + .atIndex(expected.getIndex()) + .numberOfTestsRun(expected.getNumberOfTestsRun()) + .killedBy(expected.getKillingTest()) + .build(); assertNotEquals(expected, other); } @Test - public void testEquals_differentMutatedClass_false() throws Exception { - + void testEquals_differentMutatedClass_false() { final Mutant expected = newDetectedMutant(); final Mutant other = Mutant.builder() - .mutantStatus(expected.getState()) - .inSourceFile(expected.getSourceFile()) - .inClass("com.otherClass") - .inMethod(expected.getMutatedMethod()) - .inLine(expected.getLineNumber()) - .withMethodParameters(expected.getMethodDescription()) - .usingMutator(expected.getMutationOperator()) - .atIndex(expected.getIndex()) - .numberOfTestsRun(expected.getNumberOfTestsRun()) - .killedBy(expected.getKillingTest()) - .build(); - + .mutantStatus(expected.getState()) + .inSourceFile(expected.getSourceFile()) + .inClass("com.otherClass") + .inMethod(expected.getMutatedMethod()) + .inLine(expected.getLineNumber()) + .withMethodParameters(expected.getMethodDescription()) + .usingMutator(expected.getMutationOperator()) + .atIndex(expected.getIndex()) + .numberOfTestsRun(expected.getNumberOfTestsRun()) + .killedBy(expected.getKillingTest()) + .build(); assertNotEquals(expected, other); } @Test - public void testEquals_differentMutatedMethod_false() throws Exception { - + void testEquals_differentMutatedMethod_false() { final Mutant expected = newDetectedMutant(); final Mutant other = Mutant.builder() - .mutantStatus(expected.getState()) - .inSourceFile(expected.getSourceFile()) - .inClass(expected.getMutatedClass()) - .inMethod("otherMethod") - .inLine(expected.getLineNumber()) - .withMethodParameters(expected.getMethodDescription()) - .usingMutator(expected.getMutationOperator()) - .atIndex(expected.getIndex()) - .numberOfTestsRun(expected.getNumberOfTestsRun()) - .killedBy(expected.getKillingTest()) - .build(); + .mutantStatus(expected.getState()) + .inSourceFile(expected.getSourceFile()) + .inClass(expected.getMutatedClass()) + .inMethod("otherMethod") + .inLine(expected.getLineNumber()) + .withMethodParameters(expected.getMethodDescription()) + .usingMutator(expected.getMutationOperator()) + .atIndex(expected.getIndex()) + .numberOfTestsRun(expected.getNumberOfTestsRun()) + .killedBy(expected.getKillingTest()) + .build(); assertNotEquals(expected, other); } @Test - public void testEquals_differentMethodDescription_false() throws Exception { - + void testEquals_differentMethodDescription_false() { final Mutant expected = newDetectedMutant(); final Mutant other = Mutant.builder() - .mutantStatus(expected.getState()) - .inSourceFile(expected.getSourceFile()) - .inClass(expected.getMutatedClass()) - .inMethod(expected.getMutatedMethod()) - .inLine(expected.getLineNumber()) - .withMethodParameters("()") - .usingMutator(expected.getMutationOperator()) - .atIndex(expected.getIndex()) - .numberOfTestsRun(expected.getNumberOfTestsRun()) - .killedBy(expected.getKillingTest()) - .build(); - + .mutantStatus(expected.getState()) + .inSourceFile(expected.getSourceFile()) + .inClass(expected.getMutatedClass()) + .inMethod(expected.getMutatedMethod()) + .inLine(expected.getLineNumber()) + .withMethodParameters("()") + .usingMutator(expected.getMutationOperator()) + .atIndex(expected.getIndex()) + .numberOfTestsRun(expected.getNumberOfTestsRun()) + .killedBy(expected.getKillingTest()) + .build(); assertNotEquals(expected, other); } @Test - public void testEquals_differentLineNumber_false() throws Exception { - + void testEquals_differentLineNumber_false() { final Mutant expected = newDetectedMutant(); final Mutant other = Mutant.builder() - .mutantStatus(expected.getState()) - .inSourceFile(expected.getSourceFile()) - .inClass(expected.getMutatedClass()) - .inMethod(expected.getMutatedMethod()) - .inLine(127) - .withMethodParameters(expected.getMethodDescription()) - .usingMutator(expected.getMutationOperator()) - .atIndex(expected.getIndex()) - .numberOfTestsRun(expected.getNumberOfTestsRun()) - .killedBy(expected.getKillingTest()) - .build(); - + .mutantStatus(expected.getState()) + .inSourceFile(expected.getSourceFile()) + .inClass(expected.getMutatedClass()) + .inMethod(expected.getMutatedMethod()) + .inLine(127) + .withMethodParameters(expected.getMethodDescription()) + .usingMutator(expected.getMutationOperator()) + .atIndex(expected.getIndex()) + .numberOfTestsRun(expected.getNumberOfTestsRun()) + .killedBy(expected.getKillingTest()) + .build(); assertNotEquals(expected, other); } @Test - public void testEquals_differentMutator_false() throws Exception { - + void testEquals_differentMutator_false() { final Mutant expected = newDetectedMutant(); final Mutant other = Mutant.builder() - .mutantStatus(expected.getState()) - .inSourceFile(expected.getSourceFile()) - .inClass(expected.getMutatedClass()) - .inMethod(expected.getMutatedMethod()) - .inLine(expected.getLineNumber()) - .withMethodParameters(expected.getMethodDescription()) - .usingMutator(MutationOperators.find("ARGUMENT_PROPAGATION")) - .atIndex(expected.getIndex()) - .numberOfTestsRun(expected.getNumberOfTestsRun()) - .killedBy(expected.getKillingTest()) - .build(); - + .mutantStatus(expected.getState()) + .inSourceFile(expected.getSourceFile()) + .inClass(expected.getMutatedClass()) + .inMethod(expected.getMutatedMethod()) + .inLine(expected.getLineNumber()) + .withMethodParameters(expected.getMethodDescription()) + .usingMutator(MutationOperators.find("ARGUMENT_PROPAGATION")) + .atIndex(expected.getIndex()) + .numberOfTestsRun(expected.getNumberOfTestsRun()) + .killedBy(expected.getKillingTest()) + .build(); assertNotEquals(expected, other); } @Test - public void testEquals_differentSuffix_false() throws Exception { - + void testEquals_differentSuffix_false() { final Mutant expected = newSurvivedMutantWithSuffix(); final Mutant other = Mutant.builder() - .mutantStatus(expected.getState()) - .inSourceFile(expected.getSourceFile()) - .inClass(expected.getMutatedClass()) - .inMethod(expected.getMutatedMethod()) - .inLine(expected.getLineNumber()) - .withMethodParameters(expected.getMethodDescription()) - .usingMutator("org.pitest.mutationtest.engine.gregor.mutators.RemoveConditionalMutator_EQUAL_IF") - .withDescription(expected.getDescription().get()) - .atIndex(expected.getIndex()) - .numberOfTestsRun(expected.getNumberOfTestsRun()) - .killedBy(expected.getKillingTest()) - .build(); - + .mutantStatus(expected.getState()) + .inSourceFile(expected.getSourceFile()) + .inClass(expected.getMutatedClass()) + .inMethod(expected.getMutatedMethod()) + .inLine(expected.getLineNumber()) + .withMethodParameters(expected.getMethodDescription()) + .usingMutator("org.pitest.mutationtest.engine.gregor.mutators.RemoveConditionalMutator_EQUAL_IF") + .withDescription(expected.getDescription().get()) + .atIndex(expected.getIndex()) + .numberOfTestsRun(expected.getNumberOfTestsRun()) + .killedBy(expected.getKillingTest()) + .build(); assertNotEquals(expected, other); } @Test - public void testEquals_differentNumberOfTestsRun_false() throws Exception { - + void testEquals_differentNumberOfTestsRun_false() { final Mutant expected = newDetectedMutant(); final Mutant other = Mutant.builder() - .mutantStatus(expected.getState()) - .inSourceFile(expected.getSourceFile()) - .inClass(expected.getMutatedClass()) - .inMethod(expected.getMutatedMethod()) - .inLine(expected.getLineNumber()) - .withMethodParameters(expected.getMethodDescription()) - .usingMutator(expected.getMutationOperator()) - .atIndex(expected.getIndex()) - .numberOfTestsRun(-1) - .killedBy(expected.getKillingTest()) - .build(); - + .mutantStatus(expected.getState()) + .inSourceFile(expected.getSourceFile()) + .inClass(expected.getMutatedClass()) + .inMethod(expected.getMutatedMethod()) + .inLine(expected.getLineNumber()) + .withMethodParameters(expected.getMethodDescription()) + .usingMutator(expected.getMutationOperator()) + .atIndex(expected.getIndex()) + .numberOfTestsRun(-1) + .killedBy(expected.getKillingTest()) + .build(); assertNotEquals(expected, other); } @Test - public void testEquals_differentIndex_false() throws Exception { - + void testEquals_differentIndex_false() { final Mutant expected = newDetectedMutant(); final Mutant other = Mutant.builder() - .mutantStatus(expected.getState()) - .inSourceFile(expected.getSourceFile()) - .inClass(expected.getMutatedClass()) - .inMethod(expected.getMutatedMethod()) - .inLine(expected.getLineNumber()) - .withMethodParameters(expected.getMethodDescription()) - .usingMutator(expected.getMutationOperator()) - .atIndex(127) - .numberOfTestsRun(expected.getNumberOfTestsRun()) - .killedBy(expected.getKillingTest()) - .build(); - + .mutantStatus(expected.getState()) + .inSourceFile(expected.getSourceFile()) + .inClass(expected.getMutatedClass()) + .inMethod(expected.getMutatedMethod()) + .inLine(expected.getLineNumber()) + .withMethodParameters(expected.getMethodDescription()) + .usingMutator(expected.getMutationOperator()) + .atIndex(127) + .numberOfTestsRun(expected.getNumberOfTestsRun()) + .killedBy(expected.getKillingTest()) + .build(); assertNotEquals(expected, other); } @Test - public void testEquals_differentKillingTest_false() throws Exception { - + void testEquals_differentKillingTest_false() { final Mutant expected = newDetectedMutant(); final Mutant twin = Mutant.builder() - .mutantStatus(expected.getState()) - .inSourceFile(expected.getSourceFile()) - .inClass(expected.getMutatedClass()) - .inMethod(expected.getMutatedMethod()) - .inLine(expected.getLineNumber()) - .withMethodParameters(expected.getMethodDescription()) - .usingMutator(expected.getMutationOperator()) - .atIndex(expected.getIndex()) - .numberOfTestsRun(expected.getNumberOfTestsRun()) - .killedBy("otherTest") - .build(); - + .mutantStatus(expected.getState()) + .inSourceFile(expected.getSourceFile()) + .inClass(expected.getMutatedClass()) + .inMethod(expected.getMutatedMethod()) + .inLine(expected.getLineNumber()) + .withMethodParameters(expected.getMethodDescription()) + .usingMutator(expected.getMutationOperator()) + .atIndex(expected.getIndex()) + .numberOfTestsRun(expected.getNumberOfTestsRun()) + .killedBy("otherTest") + .build(); assertNotEquals(expected, twin); } @Test - public void testEquals_differentDescription_false() throws Exception { - + void testEquals_differentDescription_false() { final Mutant expected = newDetectedMutant(); final Mutant twin = Mutant.builder() - .mutantStatus(expected.getState()) - .inSourceFile(expected.getSourceFile()) - .inClass(expected.getMutatedClass()) - .inMethod(expected.getMutatedMethod()) - .inLine(expected.getLineNumber()) - .withMethodParameters(expected.getMethodDescription()) - .usingMutator(expected.getMutationOperator()) - .atIndex(expected.getIndex()) - .numberOfTestsRun(expected.getNumberOfTestsRun()) - .killedBy(expected.getKillingTest()) - .withDescription("other Description") - .build(); - + .mutantStatus(expected.getState()) + .inSourceFile(expected.getSourceFile()) + .inClass(expected.getMutatedClass()) + .inMethod(expected.getMutatedMethod()) + .inLine(expected.getLineNumber()) + .withMethodParameters(expected.getMethodDescription()) + .usingMutator(expected.getMutationOperator()) + .atIndex(expected.getIndex()) + .numberOfTestsRun(expected.getNumberOfTestsRun()) + .killedBy(expected.getKillingTest()) + .withDescription("other Description") + .build(); assertNotEquals(expected, twin); } @Test - public void testEquals_null_false() throws Exception { - - assertNotEquals(newDetectedMutant(), null); + void testEquals_null_false() { + assertNotEquals(null, newDetectedMutant()); } @Test - public void testEquals_otherObject_false() throws Exception { - - assertNotEquals(newDetectedMutant(), new Object()); + void testEquals_otherObject_false() { + assertNotEquals(new Object(), newDetectedMutant()); } @Test - public void testHashCode_detected_reproducible() throws Exception { - + void testHashCode_detected_reproducible() { // for the same object we always have the same hashCode Mutant mutant = newDetectedMutant(); final int prime = 31; @@ -520,13 +469,11 @@ public void testHashCode_detected_reproducible() throws Exception { refCode = prime * refCode + mutant.getKillingTest().hashCode(); refCode = prime * refCode + mutant.getNumberOfTestsRun(); refCode = prime * refCode + mutant.getDescription().hashCode(); - assertEquals(refCode, mutant.hashCode()); } @Test - public void testHashCode_undetected_reproducible() throws Exception { - + void testHashCode_undetected_reproducible() { // for the same object we always have the same hashCode Mutant mutant = newSurvivedMutantWithSuffix(); final int prime = 31; @@ -544,50 +491,41 @@ public void testHashCode_undetected_reproducible() throws Exception { refCode = prime * refCode + mutant.getKillingTest().hashCode(); refCode = prime * refCode + mutant.getNumberOfTestsRun(); refCode = prime * refCode + mutant.getDescription().hashCode(); - assertEquals(refCode, mutant.hashCode()); } @Test - public void testHashCode_sameMutant() throws Exception { - + void testHashCode_sameMutant() { Mutant mutant = newDetectedMutant(); assertEquals(mutant.hashCode(), mutant.hashCode()); } @Test - public void testHashCode_equalMutant_same() throws Exception { - + void testHashCode_equalMutant_same() { assertEquals(newDetectedMutant().hashCode(), newDetectedMutant().hashCode()); } @Test - public void testGetTestDescriptor_detected_nonEmptySpec() throws Exception { - + void testGetTestDescriptor_detected_nonEmptySpec() { Mutant mutant = newDetectedMutant(); TestDescriptor td = mutant.getTestDescriptor(); - assertEquals("com.foo.bar.SomeClassKillingTest", td.getSpec()); } @Test - public void testGetTestDescriptor_undetected_emptySpec() throws Exception { - + void testGetTestDescriptor_undetected_emptySpec() { Mutant mutant = newUndetectedMutant(); TestDescriptor td = mutant.getTestDescriptor(); - assertEquals("", td.getSpec()); } @Test - public void testHashCode_otherMutantObject_different() throws Exception { - + void testHashCode_otherMutantObject_different() { assertNotEquals(newDetectedMutant().hashCode(), newUndetectedMutant().hashCode()); } @Test - public void testBuilder() throws Exception { - + void testBuilder() { final Mutant.Builder builder = Mutant.builder(); assertNotNull(builder); } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/MutationOperatorTest.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/MutationOperatorTest.java index 36dafb4..35ca507 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/MutationOperatorTest.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/MutationOperatorTest.java @@ -22,203 +22,196 @@ import static java.util.Collections.emptySet; import static java.util.Collections.singleton; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.net.URL; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.HashSet; import java.util.Optional; import java.util.Set; - import org.apache.commons.io.IOUtils; -import org.junit.Test; - -public class MutationOperatorTest { - - @Test(expected = NullPointerException.class) - public void testMutator_nullId_exception() throws Exception { - - new MutationOperator(null, "", emptySet(), "", new URL("file:///")); - - } - - @Test(expected = NullPointerException.class) - public void testMutator_nullName_exception() throws Exception { - - new MutationOperator("", null, emptySet(), "", new URL("file:///")); - - } - - @Test(expected = NullPointerException.class) - public void testMutator_nullClassNames_exception() throws Exception { - - new MutationOperator("", "", null, "", new URL("file:///")); - - } - - @Test(expected = NullPointerException.class) - public void testMutator_nullViolationDescription_exception() throws Exception { - - new MutationOperator("", "", emptySet(), null, new URL("file:///")); - - } - - @Test - public void testMutator_nullArguments() throws Exception { - - final MutationOperator mutationOperator = new MutationOperator("id", "name", emptySet(), "violationDescription", null); - assertEquals("id", mutationOperator.getId()); - assertEquals("name", mutationOperator.getName()); - assertEquals("violationDescription", mutationOperator.getViolationDescription()); - assertEquals(emptySet(), mutationOperator.getClassNames()); - assertNotNull(mutationOperator.getMutagenDescriptionLocation()); - assertFalse(mutationOperator.getMutagenDescriptionLocation().isPresent()); - assertNotNull(mutationOperator.getMutagenDescriptionLocation()); - assertEquals(Optional.empty(), mutationOperator.getMutagenDescriptionLocation()); - assertNotNull(mutationOperator.getMutagenDescriptionLocation()); - - } - - @Test - public void testGetId() throws Exception { - - final MutationOperator mutationOperator = MutationOperators.find("ARGUMENT_PROPAGATION"); - assertEquals("ARGUMENT_PROPAGATION", mutationOperator.getId()); - } - - @Test - public void testGetMutatorDescriptionLocation() throws Exception { - - final MutationOperator mutationOperator = MutationOperators.find("ARGUMENT_PROPAGATION"); - final Optional descriptorLocation = mutationOperator.getMutagenDescriptionLocation(); - assertNotNull(descriptorLocation); - assertTrue(descriptorLocation.isPresent()); - } - - @Test - public void testGetMutatorDescription() throws Exception { - - final MutationOperator mutationOperator = MutationOperators.find("ARGUMENT_PROPAGATION"); - final String desc = mutationOperator.getMutagenDescription(); - assertNotNull(desc); - } - - @Test - public void testGetMutatorDescription_exceptionOccured_noDescription() throws Exception { - - final MutationOperator mutationOperator = new MutationOperator("test", "aName", singleton("aClass"), "aViolationDescription", new URL("file://localhost:1")); - final String desc = mutationOperator.getMutagenDescription(); - assertEquals("No description", desc); - } - - @Test - public void testGetViolationDescription() throws Exception { - - final MutationOperator mutationOperator = MutationOperators.find("ARGUMENT_PROPAGATION"); - final String violationDesc = mutationOperator.getViolationDescription(); - assertNotNull(violationDesc); - } - - @Test - public void testGetName() throws Exception { - - final MutationOperator mutationOperator = MutationOperators.find("ARGUMENT_PROPAGATION"); - final String name = mutationOperator.getName(); - assertEquals("Argument Propagation Mutator", name); - } - - @Test - public void testGetClassNames() throws Exception { - - final MutationOperator mutationOperator = MutationOperators.find("ARGUMENT_PROPAGATION"); - final Set classNames = mutationOperator.getClassNames(); - assertEquals( - new HashSet<>( - Arrays.asList( - "org.pitest.mutationtest.engine.gregor.mutators.ArgumentPropagationMutator", - "org.pitest.mutationtest.engine.gregor.mutators.experimental.ArgumentPropagationMutator" - ) - ), - classNames); - } - - @Test - public void testGetClassNames_fromCustomOperator() throws Exception { - - final MutationOperator mutationOperator = new MutationOperator("test", "aName", singleton("aClass"), "aViolationDescription", new URL("file://localhost:1")); - final Set classNames = mutationOperator.getClassNames(); - assertEquals(singleton("aClass"), classNames); - } - - @Test - public void testEquals_null_false() throws Exception { - - final MutationOperator argumentPropagation = MutationOperators.find("ARGUMENT_PROPAGATION"); - assertNotEquals(argumentPropagation, null); - } - - @Test - public void testEquals_different_false() throws Exception { - - final MutationOperator argumentPropagation = MutationOperators.find("ARGUMENT_PROPAGATION"); - final MutationOperator conditionalsBoundary = MutationOperators.find("CONDITIONALS_BOUNDARY"); - assertNotEquals(argumentPropagation, conditionalsBoundary); - } - - @Test - public void testEquals_differentClass_false() throws Exception { - - final MutationOperator argumentPropagation = MutationOperators.find("ARGUMENT_PROPAGATION"); - assertNotEquals(argumentPropagation, new Object()); - } - - @Test - public void testEquals_same_true() throws Exception { - - final MutationOperator argumentPropagation = MutationOperators.find("ARGUMENT_PROPAGATION"); - assertEquals(argumentPropagation, argumentPropagation); - } - - @Test - public void testEquals_equalsId_true() throws Exception { - - final MutationOperator argumentPropagation = MutationOperators.find("ARGUMENT_PROPAGATION"); - final MutationOperator other = new MutationOperator("ARGUMENT_PROPAGATION", "someName", singleton("someClass"), "someDescription", new URL("file:///")); - assertEquals(argumentPropagation, other); - } - - @Test - public void testHashCode_reproducible() throws Exception { - - final MutationOperator mutationOperator = MutationOperators.find("ARGUMENT_PROPAGATION"); - final int expectedHashCode = 31 + mutationOperator.getId().hashCode(); - - assertEquals(expectedHashCode, mutationOperator.hashCode()); - - } - - @Test - public void testHashCode_sameMutator() throws Exception { - - final MutationOperator argumentPropagation = MutationOperators.find("ARGUMENT_PROPAGATION"); - assertEquals(argumentPropagation.hashCode(), argumentPropagation.hashCode()); - } - - @Test - public void testHashCode_otherMutatorObject() throws Exception { - - final MutationOperator argumentPropagation = MutationOperators.find("ARGUMENT_PROPAGATION"); - final MutationOperator conditionalsBoundary = MutationOperators.find("CONDITIONALS_BOUNDARY"); - - assertNotEquals(argumentPropagation.hashCode(), conditionalsBoundary.hashCode()); - } - - @Test - public void testGetMutagenDescription() throws Exception { - String expected = IOUtils.toString(MutationOperator.class.getResourceAsStream("/ch/devcon5/sonar/plugins/mutationanalysis/model/ARGUMENT_PROPAGATION.html")); - final MutationOperator argumentPropagation = MutationOperators.find("ARGUMENT_PROPAGATION"); - String actual = argumentPropagation.getMutagenDescription(); - assertEquals(expected, actual); - } +import org.junit.jupiter.api.Test; + +class MutationOperatorTest { + + @Test() + void testMutator_nullId_exception() throws Exception { + Set empty = emptySet(); + URL url = new URL("file:///"); + assertThrows(NullPointerException.class, () -> + new MutationOperator(null, "", empty, "", url)); + } + + @Test() + void testMutator_nullName_exception() throws Exception { + Set empty = emptySet(); + URL url = new URL("file:///"); + assertThrows(NullPointerException.class, () -> + new MutationOperator("", null, empty, "", url)); + } + + @Test() + void testMutator_nullClassNames_exception() throws Exception { + URL url = new URL("file:///"); + assertThrows(NullPointerException.class, () -> + new MutationOperator("", "", null, "", url)); + } + + @Test() + void testMutator_nullViolationDescription_exception() throws Exception { + Set empty = emptySet(); + URL url = new URL("file:///"); + assertThrows(NullPointerException.class, () -> + new MutationOperator("", "", empty, null, url)); + } + + @Test + void testMutator_nullArguments() { + final MutationOperator mutationOperator = new MutationOperator("id", "name", emptySet(), "violationDescription", + null); + assertEquals("id", mutationOperator.getId()); + assertEquals("name", mutationOperator.getName()); + assertEquals("violationDescription", mutationOperator.getViolationDescription()); + assertEquals(emptySet(), mutationOperator.getClassNames()); + assertNotNull(mutationOperator.getMutagenDescriptionLocation()); + assertFalse(mutationOperator.getMutagenDescriptionLocation().isPresent()); + assertNotNull(mutationOperator.getMutagenDescriptionLocation()); + assertEquals(Optional.empty(), mutationOperator.getMutagenDescriptionLocation()); + assertNotNull(mutationOperator.getMutagenDescriptionLocation()); + + } + + @Test + void testGetId() { + final MutationOperator mutationOperator = MutationOperators.find("ARGUMENT_PROPAGATION"); + assertEquals("ARGUMENT_PROPAGATION", mutationOperator.getId()); + } + + @Test + void testGetMutatorDescriptionLocation() { + final MutationOperator mutationOperator = MutationOperators.find("ARGUMENT_PROPAGATION"); + final Optional descriptorLocation = mutationOperator.getMutagenDescriptionLocation(); + assertNotNull(descriptorLocation); + assertTrue(descriptorLocation.isPresent()); + } + + @Test + void testGetMutatorDescription() { + final MutationOperator mutationOperator = MutationOperators.find("ARGUMENT_PROPAGATION"); + final String desc = mutationOperator.getMutagenDescription(); + assertNotNull(desc); + } + + @Test + void testGetMutatorDescription_exceptionOccurred_noDescription() throws Exception { + final MutationOperator mutationOperator = new MutationOperator("test", "aName", singleton("aClass"), + "aViolationDescription", new URL("file://localhost:1")); + final String desc = mutationOperator.getMutagenDescription(); + assertEquals("No description", desc); + } + + @Test + void testGetViolationDescription() { + final MutationOperator mutationOperator = MutationOperators.find("ARGUMENT_PROPAGATION"); + final String violationDesc = mutationOperator.getViolationDescription(); + assertNotNull(violationDesc); + } + + @Test + void testGetName() { + final MutationOperator mutationOperator = MutationOperators.find("ARGUMENT_PROPAGATION"); + final String name = mutationOperator.getName(); + assertEquals("Argument Propagation Mutator", name); + } + + @Test + void testGetClassNames() { + final MutationOperator mutationOperator = MutationOperators.find("ARGUMENT_PROPAGATION"); + final Set classNames = mutationOperator.getClassNames(); + assertEquals( + new HashSet<>( + Arrays.asList( + "org.pitest.mutationtest.engine.gregor.mutators.ArgumentPropagationMutator", + "org.pitest.mutationtest.engine.gregor.mutators.experimental.ArgumentPropagationMutator" + ) + ), + classNames); + } + + @Test + void testGetClassNames_fromCustomOperator() throws Exception { + final MutationOperator mutationOperator = new MutationOperator("test", "aName", singleton("aClass"), + "aViolationDescription", new URL("file://localhost:1")); + final Set classNames = mutationOperator.getClassNames(); + assertEquals(singleton("aClass"), classNames); + } + + @Test + void testEquals_null_false() { + final MutationOperator argumentPropagation = MutationOperators.find("ARGUMENT_PROPAGATION"); + assertNotEquals(null, argumentPropagation); + } + + @Test + void testEquals_different_false() { + final MutationOperator argumentPropagation = MutationOperators.find("ARGUMENT_PROPAGATION"); + final MutationOperator conditionalsBoundary = MutationOperators.find("CONDITIONALS_BOUNDARY"); + assertNotEquals(argumentPropagation, conditionalsBoundary); + } + + @Test + void testEquals_differentClass_false() { + final MutationOperator argumentPropagation = MutationOperators.find("ARGUMENT_PROPAGATION"); + assertNotEquals(new Object(), argumentPropagation); + } + + @Test + void testEquals_same_true() { + final MutationOperator argumentPropagation = MutationOperators.find("ARGUMENT_PROPAGATION"); + assertEquals(argumentPropagation, argumentPropagation); + } + + @Test + void testEquals_equalsId_true() throws Exception { + final MutationOperator argumentPropagation = MutationOperators.find("ARGUMENT_PROPAGATION"); + final MutationOperator other = new MutationOperator("ARGUMENT_PROPAGATION", "someName", singleton("someClass"), + "someDescription", new URL("file:///")); + assertEquals(argumentPropagation, other); + } + + @Test + void testHashCode_reproducible() { + final MutationOperator mutationOperator = MutationOperators.find("ARGUMENT_PROPAGATION"); + final int expectedHashCode = 31 + mutationOperator.getId().hashCode(); + assertEquals(expectedHashCode, mutationOperator.hashCode()); + } + + @Test + void testHashCode_sameMutator() { + final MutationOperator argumentPropagation = MutationOperators.find("ARGUMENT_PROPAGATION"); + assertEquals(argumentPropagation.hashCode(), argumentPropagation.hashCode()); + } + + @Test + void testHashCode_otherMutatorObject() { + final MutationOperator argumentPropagation = MutationOperators.find("ARGUMENT_PROPAGATION"); + final MutationOperator conditionalsBoundary = MutationOperators.find("CONDITIONALS_BOUNDARY"); + assertNotEquals(argumentPropagation.hashCode(), conditionalsBoundary.hashCode()); + } + + @Test + void testGetMutagenDescription() throws Exception { + String expected = IOUtils.toString(MutationOperator.class.getResourceAsStream("/ch/devcon5/sonar/plugins" + + "/mutationanalysis/model/ARGUMENT_PROPAGATION.html"), StandardCharsets.UTF_8); + final MutationOperator argumentPropagation = MutationOperators.find("ARGUMENT_PROPAGATION"); + String actual = argumentPropagation.getMutagenDescription(); + assertEquals(expected, actual); + } } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/MutationOperatorsTest.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/MutationOperatorsTest.java index d9dd8b7..8a43df7 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/MutationOperatorsTest.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/MutationOperatorsTest.java @@ -20,121 +20,49 @@ package ch.devcon5.sonar.plugins.mutationanalysis.model; -import static java.util.Collections.singleton; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; -import java.util.Arrays; import java.util.Collection; import java.util.HashSet; - -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; /** - * + * Mutation Operators Test */ -public class MutationOperatorsTest { - - @Test - public void testFind_knownMutator_byID() throws Exception { - - final MutationOperator mutationOperator = MutationOperators.find("ARGUMENT_PROPAGATION"); - assertNotNull(mutationOperator); - assertEquals("ARGUMENT_PROPAGATION", mutationOperator.getId()); - assertEquals( - new HashSet<>( - Arrays.asList( - "org.pitest.mutationtest.engine.gregor.mutators.ArgumentPropagationMutator", - "org.pitest.mutationtest.engine.gregor.mutators.experimental.ArgumentPropagationMutator" - ) - ), - mutationOperator.getClassNames()); - assertNotNull(mutationOperator.getViolationDescription()); - } - - @Test - public void testFind_knownMutator_byClassName() throws Exception { - - final MutationOperator mutationOperator = MutationOperators - .find("org.pitest.mutationtest.engine.gregor.mutators.ArgumentPropagationMutator"); - assertNotNull(mutationOperator); - assertEquals("ARGUMENT_PROPAGATION", mutationOperator.getId()); - assertEquals( - new HashSet<>( - Arrays.asList( - "org.pitest.mutationtest.engine.gregor.mutators.ArgumentPropagationMutator", - "org.pitest.mutationtest.engine.gregor.mutators.experimental.ArgumentPropagationMutator" - ) - ), - mutationOperator.getClassNames()); - assertNotNull(mutationOperator.getViolationDescription()); - } - - @Test - public void testFind_knownMutator_byClassName_v2() throws Exception { - - final MutationOperator mutationOperator = MutationOperators - .find("org.pitest.mutationtest.engine.gregor.mutators.experimental.ArgumentPropagationMutator"); - assertNotNull(mutationOperator); - assertEquals("ARGUMENT_PROPAGATION", mutationOperator.getId()); - assertEquals( - new HashSet<>( - Arrays.asList( - "org.pitest.mutationtest.engine.gregor.mutators.ArgumentPropagationMutator", - "org.pitest.mutationtest.engine.gregor.mutators.experimental.ArgumentPropagationMutator" - ) - ), - mutationOperator.getClassNames()); - assertNotNull(mutationOperator.getViolationDescription()); - } - - @Test - public void testFind_knownMutator_byClassNameWithSuffix() throws Exception { - - final MutationOperator mutationOperator = MutationOperators - .find("org.pitest.mutationtest.engine.gregor.mutators.ArgumentPropagationMutator_WITH_SUFFIX"); - assertNotNull(mutationOperator); - assertEquals("ARGUMENT_PROPAGATION", mutationOperator.getId()); - assertEquals( - new HashSet<>( - Arrays.asList( - "org.pitest.mutationtest.engine.gregor.mutators.ArgumentPropagationMutator", - "org.pitest.mutationtest.engine.gregor.mutators.experimental.ArgumentPropagationMutator" - ) - ), - mutationOperator.getClassNames()); - assertNotNull(mutationOperator.getViolationDescription()); - } - - @Test - public void testFind_knownMutator_byClassNameWithSuffix_v2() throws Exception { - - final MutationOperator mutationOperator = MutationOperators - .find("org.pitest.mutationtest.engine.gregor.mutators.experimental.ArgumentPropagationMutator_WITH_SUFFIX"); - assertNotNull(mutationOperator); - assertEquals("ARGUMENT_PROPAGATION", mutationOperator.getId()); - assertEquals( - new HashSet<>( - Arrays.asList( - "org.pitest.mutationtest.engine.gregor.mutators.ArgumentPropagationMutator", - "org.pitest.mutationtest.engine.gregor.mutators.experimental.ArgumentPropagationMutator" - ) - ), - mutationOperator.getClassNames()); - assertNotNull(mutationOperator.getViolationDescription()); - } - - @Test - public void testAllMutators() throws Exception { - - // act - final Collection mutationOperators = MutationOperators.allMutationOperators(); - - // assert - assertNotNull(mutationOperators); - assertFalse(mutationOperators.isEmpty()); - assertEquals(23, mutationOperators.size()); +class MutationOperatorsTest { + + @ParameterizedTest + @ValueSource(strings = { + "ARGUMENT_PROPAGATION", + "org.pitest.mutationtest.engine.gregor.mutators.ArgumentPropagationMutator", + "org.pitest.mutationtest.engine.gregor.mutators.experimental.ArgumentPropagationMutator", + "org.pitest.mutationtest.engine.gregor.mutators.ArgumentPropagationMutator_WITH_SUFFIX", + "org.pitest.mutationtest.engine.gregor.mutators.experimental.ArgumentPropagationMutator_WITH_SUFFIX" + }) + void testFind_knownMutator_byID(String mutagenKey) { + final MutationOperator mutationOperator = MutationOperators.find(mutagenKey); + assertNotNull(mutationOperator); + assertEquals("ARGUMENT_PROPAGATION", mutationOperator.getId()); + assertEquals(new HashSet() {{ + add("org.pitest.mutationtest.engine.gregor.mutators.ArgumentPropagationMutator"); + add("org.pitest.mutationtest.engine.gregor.mutators.experimental.ArgumentPropagationMutator"); + }}, mutationOperator.getClassNames()); + assertNotNull(mutationOperator.getViolationDescription()); + } + + @Test + void testAllMutators() { + // act + final Collection mutationOperators = MutationOperators.allMutationOperators(); + + // assert + assertNotNull(mutationOperators); + assertFalse(mutationOperators.isEmpty()); + assertEquals(23, mutationOperators.size()); + } - } } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/StateTest.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/StateTest.java index d9f548f..9764911 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/StateTest.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/StateTest.java @@ -20,46 +20,42 @@ package ch.devcon5.sonar.plugins.mutationanalysis.model; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - -public class StateTest { - - @Test - public void testIsAlive_enumValues() throws Exception { - - assertTrue(Mutant.State.NO_COVERAGE.isAlive()); - assertTrue(Mutant.State.UNKNOWN.isAlive()); - assertTrue(Mutant.State.SURVIVED.isAlive()); - assertFalse(Mutant.State.MEMORY_ERROR.isAlive()); - assertFalse(Mutant.State.TIMED_OUT.isAlive()); - assertFalse(Mutant.State.KILLED.isAlive()); - } - - @Test - public void testParse_enumValues() throws Exception { - - assertEquals(Mutant.State.NO_COVERAGE, Mutant.State.parse("NO_COVERAGE")); - assertEquals(Mutant.State.KILLED, Mutant.State.parse("KILLED")); - assertEquals(Mutant.State.SURVIVED, Mutant.State.parse("SURVIVED")); - assertEquals(Mutant.State.MEMORY_ERROR, Mutant.State.parse("MEMORY_ERROR")); - assertEquals(Mutant.State.TIMED_OUT, Mutant.State.parse("TIMED_OUT")); - assertEquals(Mutant.State.UNKNOWN, Mutant.State.parse("UNKNOWN")); - } - - @Test - public void testParse_null_unknown() throws Exception { - - assertEquals(Mutant.State.UNKNOWN, Mutant.State.parse(null)); - } - - @Test - public void testParse_unknown_unknown() throws Exception { - - assertEquals(Mutant.State.UNKNOWN, Mutant.State.parse("xxx")); - } +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class StateTest { + + @Test + void testIsAlive_enumValues() { + assertTrue(Mutant.State.NO_COVERAGE.isAlive()); + assertTrue(Mutant.State.UNKNOWN.isAlive()); + assertTrue(Mutant.State.SURVIVED.isAlive()); + assertFalse(Mutant.State.MEMORY_ERROR.isAlive()); + assertFalse(Mutant.State.TIMED_OUT.isAlive()); + assertFalse(Mutant.State.KILLED.isAlive()); + } + + @Test + void testParse_enumValues() { + assertEquals(Mutant.State.NO_COVERAGE, Mutant.State.parse("NO_COVERAGE")); + assertEquals(Mutant.State.KILLED, Mutant.State.parse("KILLED")); + assertEquals(Mutant.State.SURVIVED, Mutant.State.parse("SURVIVED")); + assertEquals(Mutant.State.MEMORY_ERROR, Mutant.State.parse("MEMORY_ERROR")); + assertEquals(Mutant.State.TIMED_OUT, Mutant.State.parse("TIMED_OUT")); + assertEquals(Mutant.State.UNKNOWN, Mutant.State.parse("UNKNOWN")); + } + + @Test + void testParse_null_unknown() { + assertEquals(Mutant.State.UNKNOWN, Mutant.State.parse(null)); + } + + @Test + void testParse_unknown_unknown() { + assertEquals(Mutant.State.UNKNOWN, Mutant.State.parse("xxx")); + } } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/TestDescriptorTest.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/TestDescriptorTest.java index 4bf436a..5737fd1 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/TestDescriptorTest.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/TestDescriptorTest.java @@ -20,116 +20,88 @@ package ch.devcon5.sonar.plugins.mutationanalysis.model; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import java.util.Objects; - -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; /** - * + * Test Descriptor Tests */ -public class TestDescriptorTest { - - @Test - public void standardClass() { - - TestDescriptor td = new TestDescriptor("some.package.ClassName.testMethod(some.package.ClassName)"); - assertEquals("some.package.ClassName", td.getClassName()); - assertEquals("testMethod", td.getMethodName()); - } - - @Test - public void standardClass_noMethod() { - - TestDescriptor td = new TestDescriptor("some.package.ClassName"); - assertEquals("some.package.ClassName", td.getClassName()); - assertEquals("unknown", td.getMethodName()); - } - - - @Test - public void nestedClass() { - - TestDescriptor td = new TestDescriptor("some.package.ClassName$SubTest.testMethod(some.package.ClassName$SubTest)"); +class TestDescriptorTest { + + @ParameterizedTest + @ValueSource(strings = { + "some.package.ClassName.testMethod(some.package.ClassName)", + "some.package.ClassName", + "some.package.ClassName$SubTest.testMethod(some.package.ClassName$SubTest)", + "some.package.ClassName$SubTest" + }) + void standardClassOrNested_methodOrNot(String spec) { + TestDescriptor td = new TestDescriptor(spec); assertEquals("some.package.ClassName", td.getClassName()); - assertEquals("testMethod", td.getMethodName()); + assertEquals(spec.contains("testMethod") ? "testMethod" : "unknown", td.getMethodName()); } @Test - public void nestedClass_noMethod() { - - TestDescriptor td = new TestDescriptor("some.package.ClassName$SubTest"); - assertEquals("some.package.ClassName", td.getClassName()); - assertEquals("unknown", td.getMethodName()); - } - - @Test - public void unknownSpec() throws Exception { + void unknownSpec() { TestDescriptor td = new TestDescriptor("some/package/ClassName"); - assertEquals("some/package/ClassName", td.getSpec()); assertEquals("some/package/ClassName", td.getClassName()); assertEquals("unknown", td.getMethodName()); - } @Test - public void getSpec() { + void getSpec() { TestDescriptor td = new TestDescriptor("some.package.ClassName.testMethod(some.package.ClassName)"); - assertEquals("some.package.ClassName.testMethod(some.package.ClassName)", td.getSpec()); } @Test - public void test_toString() { + void test_toString() { TestDescriptor td = new TestDescriptor("some.package.ClassName.testMethod(some.package.ClassName)"); assertEquals("TestDescriptor{class='some.package.ClassName', method='testMethod'}", td.toString()); - } @Test - public void test_equals() { + void test_equals() { TestDescriptor td1 = new TestDescriptor("some.package.ClassName.testMethod_one(some.package.ClassName)"); TestDescriptor td2 = new TestDescriptor("some.package.ClassName.testMethod_two(some.package.ClassName)"); - assertEquals(td1, td2); } @Test - public void test_equals_sameRef() { + void test_equals_sameRef() { TestDescriptor td1 = new TestDescriptor("some.package.ClassName.testMethod_one(some.package.ClassName)"); - assertEquals(td1, td1); } @Test - public void test_equals_null() { + void test_equals_null() { TestDescriptor td1 = new TestDescriptor("some.package.ClassName.testMethod_one(some.package.ClassName)"); - - assertNotEquals(td1, null); + assertNotEquals(null, td1); } @Test - public void test_equals_differentClass() { + void test_equals_differentClass() { TestDescriptor td1 = new TestDescriptor("some.package.ClassName.testMethod_one(some.package.ClassName)"); - - assertNotEquals(td1, new Object()); + assertNotEquals(new Object(), td1); } @Test - public void test_equals_not() { + void test_equals_not() { TestDescriptor td1 = new TestDescriptor("some.package.ClassName.testMethod_one(some.package.ClassName)"); TestDescriptor td2 = new TestDescriptor("some.otherpackage.ClassName.testMethod_two(some.package.ClassName)"); - assertNotEquals(td1, td2); } @Test - public void test_hashCode() { + void test_hashCode() { TestDescriptor td = new TestDescriptor("some.package.ClassName.testMethod_one(some.package.ClassName)"); assertEquals(Objects.hash("some.package.ClassName"), td.hashCode()); - } + } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/report/PitestReportParserTest.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/report/PitestReportParserTest.java index 5dc1a19..b631a38 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/report/PitestReportParserTest.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/report/PitestReportParserTest.java @@ -20,11 +20,13 @@ package ch.devcon5.sonar.plugins.mutationanalysis.report; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; -import javax.xml.stream.XMLStreamException; +import ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant; +import ch.devcon5.sonar.plugins.mutationanalysis.model.MutationOperators; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; @@ -34,199 +36,171 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collection; - -import ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant; -import ch.devcon5.sonar.plugins.mutationanalysis.model.MutationOperators; +import javax.xml.stream.XMLStreamException; import org.apache.commons.io.IOUtils; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.rules.TemporaryFolder; - -public class PitestReportParserTest { - - @Rule - public TemporaryFolder folder = new TemporaryFolder(); - - @Rule - public ExpectedException expect = ExpectedException.none(); - - private PitestReportParser subject; - - @Before - public void setUp() { - - subject = new PitestReportParser(); - } - - @Test - public void parseReport_findMutants_withoutDescription() throws IOException, URISyntaxException { - - // prepare - final Path report = Paths.get(getClass().getResource("PitestReportParserTest_mutations.xml").toURI()); - - // act - final Collection mutants = subject.parseMutants(report); - - // assert - assertEquals(3, mutants.size()); - - assertTrue(mutants.contains(Mutant.builder() - .mutantStatus(Mutant.State.KILLED) - .inSourceFile("Mutant.java") - .inClass("ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant") - .inMethod("equals") - .withMethodParameters("(Ljava/lang/Object;)Z") - .inLine(162) - .atIndex(5) - .numberOfTestsRun(0) - .usingMutator(MutationOperators.find("org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator")) - .killedBy( - "ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest.testEquals_different_false(ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest)") - .build())); - - assertTrue(mutants.contains(Mutant.builder() - .mutantStatus(Mutant.State.SURVIVED) - .inSourceFile("Mutant.java") - .inClass("ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant") - .inMethod("equals") - .withMethodParameters("(Ljava/lang/Object;)Z") - .inLine(172) - .atIndex(43) - .usingMutator(MutationOperators.find("org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator")) - .killedBy("") - .build())); - assertTrue(mutants.contains(Mutant.builder() - .mutantStatus(Mutant.State.NO_COVERAGE) - .inSourceFile("Mutant.java") - .inClass("ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant") - .inMethod("equals") - .withMethodParameters("(Ljava/lang/Object;)Z") - .inLine(175) - .atIndex(55) - .usingMutator(MutationOperators.find("org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator")) - .killedBy("") - .build())); - - assertEquals(0, mutants.stream().filter(m -> m.getState() == Mutant.State.UNKNOWN).count()); - assertEquals(0, mutants.stream().filter(m -> m.getState() == Mutant.State.MEMORY_ERROR).count()); - assertEquals(0, mutants.stream().filter(m -> m.getState() == Mutant.State.TIMED_OUT).count()); - - } - - @Test - public void parseReport_findMutants_withDescriptions() throws IOException, URISyntaxException { - - // prepare - final Path report = Paths.get(getClass().getResource("PitestReportParserTest_mutationsWithDescriptions.xml").toURI()); - final Mutant expected = Mutant.builder() - .mutantStatus(Mutant.State.KILLED) - .inSourceFile("Mutant.java") - .inClass("ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant") - .inMethod("equals") - .withMethodParameters("(Ljava/lang/Object;)Z") - .inLine(268) - .atIndex(8) - .usingMutator("org.pitest.mutationtest.engine.gregor.mutators.InlineConstantMutator") - .killedBy("ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest.testEquals_same_true(ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest)") - .withDescription("Substituted 1 with 0") - .build(); - - // act - final Collection mutants = subject.parseMutants(report); - - // assert - assertEquals(expected, mutants.iterator().next()); - } - - @Test - public void parseReport_findMutants_withNumberOfTests() throws IOException, URISyntaxException { - - // prepare - final Path report = Paths.get(getClass().getResource("PitestReportParserTest_mutationsWithNumTests.xml").toURI()); - final Mutant expected = Mutant.builder() - .mutantStatus(Mutant.State.KILLED) - .inSourceFile("Mutant.java") - .inClass("ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant") - .inMethod("equals") - .withMethodParameters("(Ljava/lang/Object;)Z") - .inLine(162) - .atIndex(5) - .numberOfTestsRun(40) - .usingMutator("org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator") - .killedBy("ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest.testEquals_different_false(ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest)") - .build(); - - // act - final Collection mutants = subject.parseMutants(report); - - // assert - assertEquals(expected, mutants.iterator().next()); - } - - @Test - public void parseReport_emptyFile_emptyList() throws Exception { - - Path emptyFile = folder.newFile().toPath(); - - Collection result = subject.parseMutants(emptyFile); - - assertTrue(result.isEmpty()); - } - - @Test - public void parseReport_brokenXml_emptyList() throws Exception { - - final Path report = Paths.get(getClass().getResource("PitestReportParserTest_broken.xml").toURI()); - - Collection result = subject.parseMutants(report); - - assertTrue(result.isEmpty()); - } - - @Test - public void parseReport_nonExistingFile_emptyList() throws Exception { - - Path missingFile = Paths.get("anyNonExistingPath"); - - Collection result = subject.parseMutants(missingFile); - - assertTrue(result.isEmpty()); - } - - @Test - public void readMutants_brokenXml_exceptionWithDetails() throws Exception { - - expect.expect(XMLStreamException.class); - expect.expectMessage("ParseError at [row,col]:[22,16]\nMessage: sourceFile must be set"); - - subject.readMutants(getClass().getResourceAsStream("PitestReportParserTest_broken.xml")); - } - - - @Test - public void readMutants_XXE_attack_entityNotReplaced() throws Exception { - - //we prepare a secret file with content that should not be disclosed - //this file acts as a placeholder for any file with sensitive information such as /etc/passwd - final String expectedSecret = "MY_SECRET"; - File secretFile = folder.newFile("secret"); - Files.write(secretFile.toPath(), expectedSecret.getBytes(StandardCharsets.UTF_8)); - - //we forge a pitest report that should be processed by the plugin parser - //the attack is not hypothetical, especially in managed sonarqube instances where forged - //pitest reports may reveal sensitve information in the sonarqube results - String template = IOUtils.toString(getClass().getResourceAsStream("PitestReportParserTest_XXE.xml"),StandardCharsets.UTF_8); - String xxeAttack = template.replace("$SECRET$", secretFile.toURI().toURL().toString()); - - Collection mutants = subject.readMutants(new ByteArrayInputStream(xxeAttack.getBytes("UTF-8"))); - - //this code should never be executed as the the processing of the xml should - //already encounter an unresolvable entity (&xxe;), causing an exception - Mutant mutant = mutants.iterator().next(); - String actualSecret = mutant.getMethodDescription(); - assertNotEquals(expectedSecret, actualSecret); - } +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class PitestReportParserTest { + + @TempDir + public Path folder; + + private PitestReportParser subject; + + @BeforeEach + public void setUp() { + subject = new PitestReportParser(); + } + + @Test + void parseReport_findMutants_withoutDescription() throws IOException, URISyntaxException { + // prepare + final Path report = Paths.get(getClass().getResource("PitestReportParserTest_mutations.xml").toURI()); + + // act + final Collection mutants = subject.parseMutants(report); + + // assert + assertEquals(3, mutants.size()); + assertTrue(mutants.contains(Mutant.builder() + .mutantStatus(Mutant.State.KILLED) + .inSourceFile("Mutant.java") + .inClass("ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant") + .inMethod("equals") + .withMethodParameters("(Ljava/lang/Object;)Z") + .inLine(162) + .atIndex(5) + .numberOfTestsRun(0) + .usingMutator(MutationOperators.find("org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator")) + .killedBy("ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest.testEquals_different_false(ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest)") + .build())); + assertTrue(mutants.contains(Mutant.builder() + .mutantStatus(Mutant.State.SURVIVED) + .inSourceFile("Mutant.java") + .inClass("ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant") + .inMethod("equals") + .withMethodParameters("(Ljava/lang/Object;)Z") + .inLine(172) + .atIndex(43) + .usingMutator(MutationOperators.find("org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator")) + .killedBy("") + .build())); + assertTrue(mutants.contains(Mutant.builder() + .mutantStatus(Mutant.State.NO_COVERAGE) + .inSourceFile("Mutant.java") + .inClass("ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant") + .inMethod("equals") + .withMethodParameters("(Ljava/lang/Object;)Z") + .inLine(175) + .atIndex(55) + .usingMutator(MutationOperators.find("org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator")) + .killedBy("") + .build())); + + assertEquals(0, mutants.stream().filter(m -> m.getState() == Mutant.State.UNKNOWN).count()); + assertEquals(0, mutants.stream().filter(m -> m.getState() == Mutant.State.MEMORY_ERROR).count()); + assertEquals(0, mutants.stream().filter(m -> m.getState() == Mutant.State.TIMED_OUT).count()); + } + + @Test + void parseReport_findMutants_withDescriptions() throws IOException, URISyntaxException { + // prepare + final Path report = Paths.get( + getClass().getResource("PitestReportParserTest_mutationsWithDescriptions.xml").toURI()); + final Mutant expected = Mutant.builder() + .mutantStatus(Mutant.State.KILLED) + .inSourceFile("Mutant.java") + .inClass("ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant") + .inMethod("equals") + .withMethodParameters("(Ljava/lang/Object;)Z") + .inLine(268) + .atIndex(8) + .usingMutator("org.pitest.mutationtest.engine.gregor.mutators.InlineConstantMutator") + .killedBy("ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest.testEquals_same_true(ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest)") + .withDescription("Substituted 1 with 0") + .build(); + + // act + final Collection mutants = subject.parseMutants(report); + + // assert + assertEquals(expected, mutants.iterator().next()); + } + + @Test + void parseReport_findMutants_withNumberOfTests() throws IOException, URISyntaxException { + // prepare + final Path report = Paths.get(getClass().getResource("PitestReportParserTest_mutationsWithNumTests.xml").toURI()); + final Mutant expected = Mutant.builder() + .mutantStatus(Mutant.State.KILLED) + .inSourceFile("Mutant.java") + .inClass("ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant") + .inMethod("equals") + .withMethodParameters("(Ljava/lang/Object;)Z") + .inLine(162) + .atIndex(5) + .numberOfTestsRun(40) + .usingMutator("org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator") + .killedBy("ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest.testEquals_different_false(ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest)") + .build(); + + // act + final Collection mutants = subject.parseMutants(report); + + // assert + assertEquals(expected, mutants.iterator().next()); + } + + @Test + void parseReport_emptyFile_emptyList() throws Exception { + Path emptyFile = folder.toFile().toPath(); + Collection result = subject.parseMutants(emptyFile); + assertTrue(result.isEmpty()); + } + + @Test + void parseReport_brokenXml_emptyList() throws Exception { + final Path report = Paths.get(getClass().getResource("PitestReportParserTest_broken.xml").toURI()); + Collection result = subject.parseMutants(report); + assertTrue(result.isEmpty()); + } + + @Test + void parseReport_nonExistingFile_emptyList() throws Exception { + Path missingFile = Paths.get("anyNonExistingPath"); + Collection result = subject.parseMutants(missingFile); + assertTrue(result.isEmpty()); + } + + @Test + void readMutants_brokenXml_exceptionWithDetails() { + XMLStreamException thrownException = assertThrows(XMLStreamException.class, () -> + subject.readMutants(getClass().getResourceAsStream("PitestReportParserTest_broken.xml"))); + assertEquals("ParseError at [row,col]:[22,16]\nMessage: sourceFile must be set", thrownException.getMessage()); + } + + @Test + void readMutants_XXE_attack_entityNotReplaced() throws Exception { + //we prepare a secret file with content that should not be disclosed + //this file acts as a placeholder for any file with sensitive information such as /etc/passwd + final String expectedSecret = "MY_SECRET"; + File secretFile = folder.resolve("secret").toFile(); + Files.write(secretFile.toPath(), expectedSecret.getBytes(StandardCharsets.UTF_8)); + + //we forge a pitest report that should be processed by the plugin parser + //the attack is not hypothetical, especially in managed sonarqube instances where forged + //pitest reports may reveal sensitive information in the sonarqube results + String template = IOUtils.toString(getClass().getResourceAsStream("PitestReportParserTest_XXE.xml"), StandardCharsets.UTF_8); + String xxeAttack = template.replace("$SECRET$", secretFile.toURI().toURL().toString()); + Collection mutants = subject.readMutants(new ByteArrayInputStream(xxeAttack.getBytes(StandardCharsets.UTF_8))); + + //this code should never be executed as the processing of the xml should + //already encounter an unresolvable entity (&xxe;), causing an exception + Mutant mutant = mutants.iterator().next(); + String actualSecret = mutant.getMethodDescription(); + assertNotEquals(expectedSecret, actualSecret); + } } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/report/ReportFinderTest.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/report/ReportFinderTest.java index d0fa93c..66b1dfe 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/report/ReportFinderTest.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/report/ReportFinderTest.java @@ -20,8 +20,15 @@ package ch.devcon5.sonar.plugins.mutationanalysis.report; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import ch.devcon5.sonar.plugins.mutationanalysis.report.ReportFinder.ReportFinderVisitor; +import ch.devcon5.sonar.plugins.mutationanalysis.testharness.TestUtils; import java.io.File; import java.io.IOException; import java.nio.file.FileVisitResult; @@ -29,222 +36,195 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.FileTime; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class ReportFinderTest { + + @TempDir + public Path folder; + + private ReportFinder subject; + + @BeforeEach + public void setUp() throws Exception { + subject = new ReportFinder(); + } + + @Test + void testFindReport_existingReport() throws IOException { + // prepare + final File reportsFile = TestUtils.tempFileFromResource(folder, "target/pitest-reports/mutations.xml", + getClass(), "ReportFinderTest_mutations.xml"); + final Path directory = reportsFile.toPath().getParent(); + + // act + final Path report = subject.findReport(directory); + + // assert + assertEquals(reportsFile.toPath(), report); + } + + @Test + void testFindReport_noReportInDirectory_nullReportPath() throws IOException { + // act + final Path report = subject.findReport(folder); + + // assert + assertNull(report); + } + + @Test + void testFindReport_noReportDirectory_nullReportPath() throws IOException { + // prepare + final Path directory = Paths.get("nonexisting"); + + // act + final Path report = subject.findReport(directory); + + // assert + assertNull(report); + } + + @Test + void testFindReport_nullPath_nullReportPath() throws IOException { + final Path report = subject.findReport(null); + assertNull(report); + } + + @Test + void testIsNewer_newer_true() throws Exception { + // prepare + final Path older = Files.createFile(folder.resolve("testIsNewer_older_true_one")); + final Path newer = Files.createFile(folder.resolve("testIsNewer_older_true_two")); + Files.setLastModifiedTime(older, FileTime.fromMillis(1000L)); + Files.setLastModifiedTime(newer, FileTime.fromMillis(2000L)); + + // act + final boolean result = subject.isNewer(older, newer); + + // assert + assertTrue(result); + } + + @Test + void testIsNewer_older_false() throws Exception { + // prepare + final Path older = Files.createFile(folder.resolve("testIsNewer_older_false_one")); + final Path newer = Files.createFile(folder.resolve("testIsNewer_older_false_two")); + Files.setLastModifiedTime(older, FileTime.fromMillis(1000L)); + Files.setLastModifiedTime(newer, FileTime.fromMillis(2000L)); + + // act + final boolean result = subject.isNewer(newer, older); + + // assert + assertFalse(result); + } + + @Test + void testIsNewer_equals_false() throws Exception { + // prepare + final Path older = Files.createFile(folder.resolve("testIsNewer_equals_false_one")); + Files.setLastModifiedTime(older, FileTime.fromMillis(1000L)); + + // act + final boolean result = subject.isNewer(older, older); + + // assert + assertFalse(result); + } + + @Test + void testFindMostRecentReport_notMatchingPattern() throws Exception { + // prepare + folder.resolve("someFile.txt"); + + // act + final Path report = subject.findMostRecentReport(folder, "*.xml"); + + // assert + assertNull(report); + } + + @Test + void testFindMostRecentReport_matchingPatternOnce() throws Exception { + // prepare + final Path newFile = Files.createFile(folder.resolve("someFile.xml")); + + // act + final Path report = subject.findMostRecentReport(folder, "*.xml"); + + // assert + assertNotNull(report); + assertEquals(newFile, report); + } + + @Test + void testFindMostRecentReport_matchingPatternNewerFile() throws Exception { + // prepare + final Path newFile1 = Files.createFile(folder.resolve("someFile1.xml")); + final Path newFile2 = Files.createFile(folder.resolve("someFile2.xml")); + Files.setLastModifiedTime(newFile1, FileTime.fromMillis(1000L)); + Files.setLastModifiedTime(newFile2, FileTime.fromMillis(2000L)); + + // act + final Path report = subject.findMostRecentReport(folder, "*.xml"); + + // assert + assertNotNull(report); + assertEquals(newFile2, report); + } + + @Test + void testFindMostRecentReport_matchingPatternNewerFile_reverseOrder() throws Exception { + // prepare + final Path newFile1 = Files.createFile(folder.resolve("someFile2.xml")); + final Path newFile2 = Files.createFile(folder.resolve("someFile1.xml")); + Files.setLastModifiedTime(newFile1, FileTime.fromMillis(1000L)); + Files.setLastModifiedTime(newFile2, FileTime.fromMillis(2000L)); + + // act + final Path report = subject.findMostRecentReport(folder, "*.xml"); + + // assert + assertNotNull(report); + assertEquals(newFile2, report); + } + + @Test + void testFindMostRecentReport_inSubDirectories() throws IOException { + // prepare + final File olderReport = TestUtils.tempFileFromResource(folder, "target/pitest-reports/subDirectory1/mutations.xml", + getClass(), "ReportFinderTest_mutations.xml"); + final File newReport = TestUtils.tempFileFromResource(folder, "target/pitest-reports/subDirectory2/mutations.xml", + getClass(), "ReportFinderTest_mutations.xml"); + Files.setLastModifiedTime(olderReport.toPath(), FileTime.fromMillis(1000L)); + Files.setLastModifiedTime(newReport.toPath(), FileTime.fromMillis(2000L)); + + // act + final Path report = subject.findMostRecentReport(folder, "*.xml"); + + // assert + assertNotNull(report); + assertEquals(newReport.toPath(), report); + } + + @Test + void nullCheck_ReportFinderVisitor() { + ReportFinderVisitor visitor = new ReportFinderVisitor("mutatations.xml"); + IllegalArgumentException thrownException = assertThrows(IllegalArgumentException.class, () -> + visitor.visitFile(null, null)); + assertEquals("file must not be null", thrownException.getMessage()); + } + + @Test + void nullPath_ReportFinderVisitor() { + final ReportFinderVisitor visitor = new ReportFinderVisitor("mutatations.xml"); + final FileVisitResult result = visitor.visitFile(Paths.get("/"), null); + assertEquals(FileVisitResult.CONTINUE, result); + assertTrue(visitor.getReports().isEmpty()); + } -import ch.devcon5.sonar.plugins.mutationanalysis.report.ReportFinder.ReportFinderVisitor; -import ch.devcon5.sonar.plugins.mutationanalysis.testharness.TestUtils; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.rules.TemporaryFolder; - -public class ReportFinderTest { - - @Rule - public TemporaryFolder folder = new TemporaryFolder(); - - @Rule - public ExpectedException expects = ExpectedException.none(); - - private ReportFinder subject; - - @Before - public void setUp() throws Exception { - subject = new ReportFinder(); - } - - @Test - public void testFindReport_existingReport() throws IOException { - - // prepare - final File reportsFile = TestUtils.tempFileFromResource(folder, "target/pitest-reports/mutations.xml", - getClass(), "ReportFinderTest_mutations.xml"); - final Path directory = reportsFile.toPath().getParent(); - - // act - final Path report = subject.findReport(directory); - - // assert - assertEquals(reportsFile.toPath(), report); - } - - @Test - public void testFindReport_noReportInDirectory_nullReportPath() throws IOException { - - final Path directory = folder.newFolder().toPath(); - - // act - final Path report = subject.findReport(directory); - - // assert - assertNull(report); - } - - @Test - public void testFindReport_noReportDirectory_nullReportPath() throws IOException { - - final Path directory = Paths.get("nonexisting"); - - // act - final Path report = subject.findReport(directory); - - // assert - assertNull(report); - } - - @Test - public void testFindReport_nullPath_nullReportPath() throws IOException { - - final Path report = subject.findReport(null); - - assertNull(report); - } - - @Test - public void testIsNewer_newer_true() throws Exception { - - // prepare - - final Path older = folder.newFile().toPath(); - final Path newer = folder.newFile().toPath(); - Files.setLastModifiedTime(older, FileTime.fromMillis(1000L)); - Files.setLastModifiedTime(newer, FileTime.fromMillis(2000L)); - // act - final boolean result = subject.isNewer(older, newer); - - // assert - - assertTrue(result); - } - - @Test - public void testIsNewer_older_false() throws Exception { - - // prepare - - final Path older = folder.newFile().toPath(); - final Path newer = folder.newFile().toPath(); - Files.setLastModifiedTime(older, FileTime.fromMillis(1000L)); - Files.setLastModifiedTime(newer, FileTime.fromMillis(2000L)); - // act - final boolean result = subject.isNewer(newer, older); - - // assert - - assertFalse(result); - } - - @Test - public void testIsNewer_equals_false() throws Exception { - - // prepare - final Path older = folder.newFile().toPath(); - Files.setLastModifiedTime(older, FileTime.fromMillis(1000L)); - - // act - final boolean result = subject.isNewer(older, older); - - // assert - - assertFalse(result); - } - - @Test - public void testFindMostRecentReport_notMatchingPattern() throws Exception { - - folder.newFile("someFile.txt").toPath(); - // act - final Path report = subject.findMostRecentReport(folder.getRoot().toPath(), "*.xml"); - - // assert - assertNull(report); - } - - @Test - public void testFindMostRecentReport_matchingPatternOnce() throws Exception { - - // prepare - final Path newFile = folder.newFile("someFile.xml").toPath(); - // act - final Path report = subject.findMostRecentReport(folder.getRoot().toPath(), "*.xml"); - - // assert - assertNotNull(report); - assertEquals(newFile, report); - } - - @Test - public void testFindMostRecentReport_matchingPatternNewerFile() throws Exception { - - // prepare - final Path newFile1 = folder.newFile("someFile1.xml").toPath(); - final Path newFile2 = folder.newFile("someFile2.xml").toPath(); - Files.setLastModifiedTime(newFile1, FileTime.fromMillis(1000L)); - Files.setLastModifiedTime(newFile2, FileTime.fromMillis(2000L)); - - // act - final Path report = subject.findMostRecentReport(folder.getRoot().toPath(), "*.xml"); - - // assert - assertNotNull(report); - assertEquals(newFile2, report); - } - - @Test - public void testFindMostRecentReport_matchingPatternNewerFile_reverseOrder() throws Exception { - - // prepare - final Path newFile1 = folder.newFile("someFile2.xml").toPath(); - final Path newFile2 = folder.newFile("someFile1.xml").toPath(); - Files.setLastModifiedTime(newFile1, FileTime.fromMillis(1000L)); - Files.setLastModifiedTime(newFile2, FileTime.fromMillis(2000L)); - - // act - final Path report = subject.findMostRecentReport(folder.getRoot().toPath(), "*.xml"); - - // assert - assertNotNull(report); - assertEquals(newFile2, report); - } - - - @Test - public void testFindMostRecentReport_inSubDirectories() throws IOException { - - // prepare - final File olderReport = TestUtils.tempFileFromResource(folder, "target/pitest-reports/subDirectory1/mutations.xml", - getClass(), "ReportFinderTest_mutations.xml"); - final File newReport = TestUtils.tempFileFromResource(folder, "target/pitest-reports/subDirectory2/mutations.xml", - getClass(), "ReportFinderTest_mutations.xml"); - Files.setLastModifiedTime(olderReport.toPath(), FileTime.fromMillis(1000L)); - Files.setLastModifiedTime(newReport.toPath(), FileTime.fromMillis(2000L)); - - // act - final Path report = subject.findMostRecentReport(folder.getRoot().toPath(), "*.xml"); - - // assert - assertNotNull(report); - assertEquals(newReport.toPath(), report); - } - - @Test - public void nullCheck_ReportFinderVisitor() throws Exception { - - expects.expect(IllegalArgumentException.class); - expects.expectMessage("file must not be null"); - - ReportFinderVisitor visitor = new ReportFinderVisitor("mutatations.xml"); - visitor.visitFile(null, null); - } - - @Test - public void nullPath_ReportFinderVisitor() throws Exception { - - final ReportFinderVisitor visitor = new ReportFinderVisitor("mutatations.xml"); - - final FileVisitResult result = visitor.visitFile(Paths.get("/"), null); - - assertEquals(FileVisitResult.CONTINUE, result); - assertTrue(visitor.getReports().isEmpty()); - } } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/report/ReportsTest.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/report/ReportsTest.java index 7a53c94..539b988 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/report/ReportsTest.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/report/ReportsTest.java @@ -20,74 +20,66 @@ package ch.devcon5.sonar.plugins.mutationanalysis.report; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant; import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Collection; - -import ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant; import org.apache.commons.io.IOUtils; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; - -public class ReportsTest { - - @Rule - public TemporaryFolder folder = new TemporaryFolder(); - - @Test - public void testReadMutants_fromDirectory_noReport() throws Exception { - - // prepare - - // act - final Collection mutants = Reports.readMutants(folder.getRoot().toPath()); - - // assert - assertNotNull(mutants); - assertTrue(mutants.isEmpty()); - } - - @Test - public void testReadMutants_fromDirectory_withReport() throws Exception { - - // prepare - fileFromResource("ReportsTest_mutations.xml", "mutations.xml"); - - // act - final Collection mutants = Reports.readMutants(folder.getRoot().toPath()); - - // assert - assertNotNull(mutants); - assertEquals(3, mutants.size()); - } - - @Test - public void testReadMutants_fromFile() throws Exception { - - // prepare - final File file = fileFromResource("ReportsTest_mutations.xml", "mutations.xml"); - - // act - final Collection mutants = Reports.readMutants(file.toPath()); - - // assert - assertNotNull(mutants); - assertEquals(3, mutants.size()); - } - - private File fileFromResource(final String resourcePath, final String fileName) throws IOException, - FileNotFoundException { - - final File newFile = folder.newFile(fileName); - IOUtils.copy(getClass().getResourceAsStream(resourcePath), new FileOutputStream(newFile)); - return newFile; - } +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class ReportsTest { + + @TempDir + public Path folder; + + @Test + void testReadMutants_fromDirectory_noReport() throws Exception { + // act + final Collection mutants = Reports.readMutants(folder); + + // assert + assertNotNull(mutants); + assertTrue(mutants.isEmpty()); + } + + @Test + void testReadMutants_fromDirectory_withReport() throws Exception { + // prepare + fileFromResource("ReportsTest_mutations.xml", "mutations.xml"); + + // act + final Collection mutants = Reports.readMutants(folder); + + // assert + assertNotNull(mutants); + assertEquals(3, mutants.size()); + } + + @Test + void testReadMutants_fromFile() throws Exception { + // prepare + final File file = fileFromResource("ReportsTest_mutations.xml", "mutations.xml"); + + // act + final Collection mutants = Reports.readMutants(file.toPath()); + + // assert + assertNotNull(mutants); + assertEquals(3, mutants.size()); + } + + private File fileFromResource(final String resourcePath, final String fileName) throws IOException { + final File newFile = Files.createFile(folder.resolve(fileName)).toFile(); + IOUtils.copy(getClass().getResourceAsStream(resourcePath), Files.newOutputStream(newFile.toPath())); + return newFile; + } } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/JavaProfileDefinitionTest.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/JavaProfileDefinitionTest.java index 4e8fc97..f2c4105 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/JavaProfileDefinitionTest.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/JavaProfileDefinitionTest.java @@ -21,26 +21,25 @@ package ch.devcon5.sonar.plugins.mutationanalysis.rules; import static ch.devcon5.sonar.plugins.mutationanalysis.rules.MutationAnalysisRulesDefinition.REPOSITORY_KEY; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition; import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition.BuiltInQualityProfile; -public class JavaProfileDefinitionTest { +class JavaProfileDefinitionTest { - private BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); + private final BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); - @Test - public void define_java() { + @Test + void define_java() { + MutationAnalysisProfileDefinition def = new JavaProfileDefinition(); - MutationAnalysisProfileDefinition def = new JavaProfileDefinition(); + def.define(context); - def.define(context); + BuiltInQualityProfile javaProfile = context.profile("java", "Mutation Analysis"); - BuiltInQualityProfile javaProfile = context.profile("java", "Mutation Analysis"); - - assertEquals(23, javaProfile.rules().stream().filter(r -> (REPOSITORY_KEY + ".java").equals(r.repoKey())).count()); - } + assertEquals(23, javaProfile.rules().stream().filter(r -> (REPOSITORY_KEY + ".java").equals(r.repoKey())).count()); + } } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/JavaRulesDefinitionTest.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/JavaRulesDefinitionTest.java index bc91c07..5b34d04 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/JavaRulesDefinitionTest.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/JavaRulesDefinitionTest.java @@ -20,15 +20,14 @@ package ch.devcon5.sonar.plugins.mutationanalysis.rules; import static ch.devcon5.sonar.plugins.mutationanalysis.rules.MutationAnalysisRulesDefinition.MUTANT_RULES_PREFIX; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.util.List; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import ch.devcon5.sonar.plugins.mutationanalysis.testharness.TestConfiguration; -import org.junit.Before; -import org.junit.Test; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.sonar.api.config.Configuration; import org.sonar.api.rule.RuleStatus; import org.sonar.api.rules.RuleType; @@ -36,21 +35,19 @@ import org.sonar.api.server.rule.RulesDefinition.Context; import org.sonar.api.server.rule.RulesDefinitionXmlLoader; -public class JavaRulesDefinitionTest { +class JavaRulesDefinitionTest { - private Configuration configuration = new TestConfiguration(); + private final Configuration configuration = new TestConfiguration(); private JavaRulesDefinition subject; - @Before + @BeforeEach public void setUp() throws Exception { - subject = new JavaRulesDefinition(configuration, new RulesDefinitionXmlLoader()); } @Test - public void testDefine() throws Exception { - + void testDefine() { // prepare Context context = new RulesDefinition.Context(); @@ -58,7 +55,8 @@ public void testDefine() throws Exception { subject.define(context); // assert - RulesDefinition.Repository repository = context.repository(MutationAnalysisRulesDefinition.REPOSITORY_KEY + ".java"); + RulesDefinition.Repository repository = context.repository( + MutationAnalysisRulesDefinition.REPOSITORY_KEY + ".java"); assertNotNull(repository); assertEquals("java", repository.language()); @@ -67,9 +65,7 @@ public void testDefine() throws Exception { } private void assertRules(final List rules) { - assertEquals(50, rules.size()); - for (RulesDefinition.Rule rule : rules) { assertNotNull(rule.debtRemediationFunction()); assertNotNull(rule.gapDescription()); @@ -79,39 +75,39 @@ private void assertRules(final List rules) { //@ denotes a toString'ed object reference //"null " would be exactly 5 characters assertTrue(rules.stream() - .filter(r -> r.key().startsWith(MUTANT_RULES_PREFIX) && r.key().endsWith("CODE_SMELL")) - .allMatch(r -> r.name().matches("[^@]{6,}\\(Code Smell\\)"))); + .filter(r -> r.key().startsWith(MUTANT_RULES_PREFIX) && r.key().endsWith("CODE_SMELL")) + .allMatch(r -> r.name().matches("[^@]{6,}\\(Code Smell\\)"))); assertTrue(rules.stream() - .filter(r -> r.key().startsWith(MUTANT_RULES_PREFIX) && !r.key().endsWith("CODE_SMELL")) - .noneMatch(r -> r.name().matches("[^@]{6,}\\(Code Smell\\)"))); + .filter(r -> r.key().startsWith(MUTANT_RULES_PREFIX) && !r.key().endsWith("CODE_SMELL")) + .noneMatch(r -> r.name().matches("[^@]{6,}\\(Code Smell\\)"))); //all mutator rules assertEquals(26, - rules.stream() - .filter(rule -> rule.key().startsWith(MUTANT_RULES_PREFIX)) - .filter(rule -> RuleType.BUG.equals(rule.type())) - .count()); + rules.stream() + .filter(rule -> rule.key().startsWith(MUTANT_RULES_PREFIX)) + .filter(rule -> RuleType.BUG.equals(rule.type())) + .count()); assertEquals(24, - rules.stream() - .filter(rule -> rule.key().startsWith(MUTANT_RULES_PREFIX)) - .filter(rule -> RuleType.CODE_SMELL.equals(rule.type())) - .count()); + rules.stream() + .filter(rule -> rule.key().startsWith(MUTANT_RULES_PREFIX)) + .filter(rule -> RuleType.CODE_SMELL.equals(rule.type())) + .count()); assertEquals(34, - rules.stream() - .filter(rule -> rule.status() == RuleStatus.READY) - .filter(RulesDefinition.Rule::activatedByDefault) - .count()); + rules.stream() + .filter(rule -> rule.status() == RuleStatus.READY) + .filter(RulesDefinition.Rule::activatedByDefault) + .count()); assertEquals(12, - rules.stream() - .filter(rule -> rule.status() == RuleStatus.BETA) - .filter(rule -> !rule.activatedByDefault()) - .count()); + rules.stream() + .filter(rule -> rule.status() == RuleStatus.BETA) + .filter(rule -> !rule.activatedByDefault()) + .count()); assertEquals(3, - rules.stream() - .filter(rule -> rule.status() == RuleStatus.DEPRECATED) - .filter(rule -> !rule.activatedByDefault()) - .count()); + rules.stream() + .filter(rule -> rule.status() == RuleStatus.DEPRECATED) + .filter(rule -> !rule.activatedByDefault()) + .count()); } } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/KotlinProfileDefinitionTest.kt b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/KotlinProfileDefinitionTest.kt index 7ce7f99..ad6dd0f 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/KotlinProfileDefinitionTest.kt +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/KotlinProfileDefinitionTest.kt @@ -21,17 +21,17 @@ package ch.devcon5.sonar.plugins.mutationanalysis.rules import ch.devcon5.sonar.plugins.mutationanalysis.rules.MutationAnalysisRulesDefinition.REPOSITORY_KEY +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test as test import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition -import kotlin.test.assertEquals -import org.junit.Test as test class KotlinProfileDefinitionTest { val context = BuiltInQualityProfilesDefinition.Context() - @test fun define() { - + @test + fun define() { val def = KotlinProfileDefinition() def.define(context) @@ -40,4 +40,5 @@ class KotlinProfileDefinitionTest { assertEquals(23, kotlinProfile.rules().stream().filter { r -> "$REPOSITORY_KEY.kotlin" == r.repoKey() }.count()) } + } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/KotlinRulesDefinitionTest.kt b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/KotlinRulesDefinitionTest.kt index ad38adc..03c9261 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/KotlinRulesDefinitionTest.kt +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/KotlinRulesDefinitionTest.kt @@ -22,15 +22,15 @@ package ch.devcon5.sonar.plugins.mutationanalysis.rules import ch.devcon5.sonar.plugins.mutationanalysis.rules.MutationAnalysisRulesDefinition.MUTANT_RULES_PREFIX import ch.devcon5.sonar.plugins.mutationanalysis.testharness.TestConfiguration +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotNull import org.sonar.api.config.Configuration import org.sonar.api.rule.RuleStatus import org.sonar.api.rules.RuleType import org.sonar.api.server.rule.RulesDefinition import org.sonar.api.server.rule.RulesDefinitionXmlLoader -import kotlin.test.assertEquals -import kotlin.test.assertNotNull -import org.junit.Before as BeforeTest -import org.junit.Test as test +import org.junit.jupiter.api.BeforeEach as BeforeTest +import org.junit.jupiter.api.Test as test class KotlinRulesDefinitionTest { @@ -43,14 +43,12 @@ class KotlinRulesDefinitionTest { @BeforeTest @Throws(Exception::class) fun setUp() { - subject = KotlinRulesDefinition(configuration, RulesDefinitionXmlLoader()) } @test @Throws(Exception::class) fun testDefine() { - // prepare val context = RulesDefinition.Context() @@ -66,9 +64,7 @@ class KotlinRulesDefinitionTest { } private fun assertRules(rules: List) { - assertEquals(50, rules.size.toLong()) - for (rule in rules) { assertNotNull(rule.debtRemediationFunction()) assertNotNull(rule.gapDescription()) diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/PitestSensorTest.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/PitestSensorTest.java index e082283..e38ba0e 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/PitestSensorTest.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/PitestSensorTest.java @@ -31,472 +31,505 @@ import static ch.devcon5.sonar.plugins.mutationanalysis.rules.MutationAnalysisRulesDefinition.RULE_SURVIVED_MUTANT; import static ch.devcon5.sonar.plugins.mutationanalysis.testharness.TestUtils.assertContains; import static ch.devcon5.sonar.plugins.mutationanalysis.testharness.TestUtils.assertNotContains; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import ch.devcon5.sonar.plugins.mutationanalysis.MutationAnalysisPlugin; +import ch.devcon5.sonar.plugins.mutationanalysis.testharness.SensorTestHarness; +import ch.devcon5.sonar.plugins.mutationanalysis.testharness.SystemLocaleExtension; +import ch.devcon5.sonar.plugins.mutationanalysis.testharness.TestSensorContext; import java.io.IOException; +import java.nio.file.Path; import java.util.Iterator; import java.util.List; import java.util.Locale; - -import ch.devcon5.sonar.plugins.mutationanalysis.MutationAnalysisPlugin; -import ch.devcon5.sonar.plugins.mutationanalysis.testharness.SensorTestHarness; -import ch.devcon5.sonar.plugins.mutationanalysis.testharness.SystemLocale; -import ch.devcon5.sonar.plugins.mutationanalysis.testharness.TestSensorContext; -import org.junit.*; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.api.io.TempDir; import org.sonar.api.batch.fs.TextRange; import org.sonar.api.batch.rule.ActiveRules; import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor; import org.sonar.api.batch.sensor.issue.Issue; import org.sonar.api.batch.sensor.measure.Measure; -//kills 68 mutants, 11 alive +/** + * Pitest Sensor Tests kills 68 mutants, 11 alive + */ public class PitestSensorTest { - public static final int EXPECTED_QUANTITATIVE_METRICS = 12; - @Rule - public TemporaryFolder folder = new TemporaryFolder(); - - private SensorTestHarness harness; - - @ClassRule - public static SystemLocale systemLocale = SystemLocale.overrideDefault(Locale.ENGLISH); - - @Before - public void setUp() throws Exception { - this.harness = SensorTestHarness.builder().withTempFolder(folder).withResourceResolver(this.getClass()).build(); - } - @Test - public void describe() { - final PitestSensor sensor = new PitestSensor(harness.createConfiguration(), harness.createEmptyActiveRules(), - harness.createSensorContext().fileSystem()); - - final DefaultSensorDescriptor desc = new DefaultSensorDescriptor(); - - sensor.describe(desc); - - assertEquals("Mutation Analysis", desc.name()); - - assertTrue(desc.languages().contains("java")); - assertTrue(desc.ruleRepositories().contains(REPOSITORY_KEY + ".java")); - - assertTrue(desc.languages().contains("kotlin")); - assertTrue(desc.ruleRepositories().contains(REPOSITORY_KEY + ".kotlin")); - - //the following two assertion block target at killing mutants introduced - //by the javac compiler for vargs. - //these provide no extra value as the order is actually unimportant - //and any reordering on the arguments done by pit is irrelevant - Iterator langIt = desc.languages().iterator(); - assertEquals("java", langIt.next()); - assertEquals("kotlin", langIt.next()); - - Iterator repoIt = desc.ruleRepositories().iterator(); - assertEquals(REPOSITORY_KEY + ".java", repoIt.next()); - assertEquals(REPOSITORY_KEY + ".kotlin", repoIt.next()); - } - - @Test - public void testToString() throws Exception { - - final PitestSensor sensor = new PitestSensor(harness.createConfiguration(), harness.createEmptyActiveRules(), harness - .createSensorContext().fileSystem()); - - assertEquals("PitestSensor", sensor.toString()); - } - - @Test - public void execute_AllSensorsDisabled_noIssuesOrMeasuresCreated() throws IOException { - - //arrange - final TestSensorContext context = disableBothSensors(harness.createSensorContext()); - createReportFile("PitestSensorTest_KotlinJava_mutations.xml"); - final ActiveRules profile = harness.createActiveRules( - harness.createRule("java",RULE_SURVIVED_MUTANT), - harness.createRule("kotlin",RULE_SURVIVED_MUTANT) - ); - context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/JavaExample.java", md -> md.lines = 200); - context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/KotlinExample.kt", md -> md.lines = 200); - - //act - final PitestSensor sensor = new PitestSensor(context.getConfiguration(), profile, context.fileSystem()); - sensor.execute(context); - - //assert - assertTrue(context.getStorage().getIssues().isEmpty()); - assertTrue(context.getStorage().getMeasures().isEmpty()); - } - - @Test - public void execute_JavaSensorsDisabled_noJavaIssuesOrMeasuresCreated() throws IOException { - - //arrange - final TestSensorContext context = disableJavaSensor(harness.createSensorContext()); - createReportFile("PitestSensorTest_KotlinJava_mutations.xml"); - final ActiveRules profile = harness.createActiveRules( - harness.createRule("java","mutant.survived"), - harness.createRule("kotlin","mutant.survived") - ); - - context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/JavaExample.java", md -> md.lines = 200); - context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/KotlinExample.kt", md -> md.lines = 200); - - //act - final PitestSensor sensor = new PitestSensor(context.getConfiguration(), profile, context.fileSystem()); - sensor.execute(context); - - //assert - assertFalse(context.getStorage().getMeasures().isEmpty()); - final List issues = context.getStorage().getIssues(); - assertContains(issues, i -> { - assertEquals("mutant.survived", i.ruleKey().rule()); - assertEquals("test-module:src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/KotlinExample.kt", i.primaryLocation().inputComponent().key()); - assertEquals(1.0, i.gap(), 0.01); - assertTextrangeOnLine(i.primaryLocation().textRange(), 28, 79); - assertEquals("Alive Mutant: A conditional expression has been negated without being detected by a test. Mutation: negated conditional", i.primaryLocation().message()); - }); - } - - @Test - public void execute_KotlinSensorDisabled_noJavaIssuesOrMeasuresCreated() throws IOException { - - //arrange - final TestSensorContext context = disableKotlingSensor(harness.createSensorContext()); - createReportFile("PitestSensorTest_KotlinJava_mutations.xml"); - final ActiveRules profile = harness.createActiveRules( - harness.createRule("java",RULE_SURVIVED_MUTANT), - harness.createRule("kotlin",RULE_SURVIVED_MUTANT) - ); - context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/JavaExample.java", md -> md.lines = 200); - context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/KotlinExample.kt", md -> md.lines = 200); - - //act - final PitestSensor sensor = new PitestSensor(context.getConfiguration(), profile, context.fileSystem()); - sensor.execute(context); - - //assert - assertFalse(context.getStorage().getMeasures().isEmpty()); - final List issues = context.getStorage().getIssues(); - assertContains(issues, i -> { - assertEquals("mutant.survived", i.ruleKey().rule()); - assertEquals("test-module:src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/JavaExample.java", i.primaryLocation().inputComponent().key()); - assertEquals(1.0, i.gap(), 0.01); - assertTextrangeOnLine(i.primaryLocation().textRange(), 162, 79); - assertEquals("Alive Mutant: A conditional expression has been negated without being detected by a test.", i.primaryLocation().message()); - }); - } - - - @Test - public void execute_noTestFile_noIssuesAndMeasuresCreated() throws Exception { - - createReportFile("PitestSensorTest_Java_mutations.xml"); - final ActiveRules profile = harness.createActiveRules(RULE_SURVIVED_MUTANT); - final TestSensorContext context = harness.createSensorContext(); - - final PitestSensor sensor = new PitestSensor(context.getConfiguration(), profile, context.fileSystem()); - - sensor.execute(context); - - assertTrue(context.getStorage().getIssues().isEmpty()); - assertTrue(context.getStorage().getMeasures().isEmpty()); - - } - - @Test - public void execute_noReport_noIssuesAndMeasuresCreated() throws Exception { - - final ActiveRules profile = harness.createActiveRules(RULE_SURVIVED_MUTANT); - final TestSensorContext context = harness.createSensorContext(); - context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", md -> md.lines = 200); - - final PitestSensor sensor = new PitestSensor(context.getConfiguration(), profile, context.fileSystem()); - - sensor.execute(context); - - assertTrue(context.getStorage().getIssues().isEmpty()); - assertTrue(context.getStorage().getMeasures().isEmpty()); - - } - - - @Test - public void execute_noRuleActivated_noIssuesCreated_but_MetricsCreated() throws Exception { - - createReportFile("PitestSensorTest_Java_mutations.xml"); - final TestSensorContext context = harness.createSensorContext().scanFiles(); - context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", md -> md.lines = 200); - final ActiveRules profile = harness.createEmptyActiveRules(); - - //sensor is configured by default - final PitestSensor sensor = new PitestSensor(context.getConfiguration(), profile, context.fileSystem()); - - sensor.execute(context); - - assertTrue(context.getStorage().getIssues().isEmpty()); - assertEquals(12, context.getStorage().getMeasures().size()); - } - - - @Test - public void execute_mutatorSpecificRuleActive_issueCreated() throws Exception { - - createReportFile("PitestSensorTest_Java_mutations.xml"); - - final TestSensorContext context = harness.createSensorContext().scanFiles(); - context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", md -> md.lines = 200); - - final ActiveRules profile = harness.createActiveRules("mutant.NEGATE_CONDITIONALS"); - - //sensor is configured by default - final PitestSensor sensor = new PitestSensor(context.getConfiguration(), profile, context.fileSystem()); - - sensor.execute(context); - - final List issues = context.getStorage().getIssues(); - assertContains(issues, i -> { - assertEquals("mutant.NEGATE_CONDITIONALS", i.ruleKey().rule()); - assertEquals("test-module:src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", i.primaryLocation().inputComponent().key()); - assertEquals(1.0, i.gap(), 0.01); - assertTextrangeOnLine(i.primaryLocation().textRange(), 172, 79); - assertEquals("Alive Mutant: A conditional expression has been negated without being detected by a test.", i.primaryLocation().message()); - }); - assertContains(issues, i -> { - assertEquals("mutant.NEGATE_CONDITIONALS", i.ruleKey().rule()); - assertEquals("test-module:src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", i.primaryLocation().inputComponent().key()); - assertEquals(1.0, i.gap(), 0.01); - assertTextrangeOnLine(i.primaryLocation().textRange(), 175, 79); - assertEquals("Alive Mutant: A conditional expression has been negated without being detected by a test. (WITH_SUFFIX)", i.primaryLocation().message()); - }); - assertEquals(12, context.getStorage().getMeasures().size()); - } - - @Test - public void execute_survivedMutantRuleActive_issueCreated() throws Exception { - - createReportFile("PitestSensorTest_Java_mutations.xml"); - final TestSensorContext context = harness.createSensorContext().scanFiles(); - context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", md -> md.lines = 200); - - final ActiveRules profile = harness.createActiveRules("mutant.uncovered"); - final PitestSensor sensor = new PitestSensor(context.getConfiguration(), profile, context.fileSystem()); - - sensor.execute(context); - - final List issues = context.getStorage().getIssues(); - assertEquals(1, issues.size()); - assertContains(issues, i -> { - assertEquals("mutant.uncovered", i.ruleKey().rule()); - assertEquals("test-module:src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", i.primaryLocation().inputComponent().key()); - assertEquals(1.0, i.gap(), 0.01); - assertTextrangeOnLine(i.primaryLocation().textRange(), 175, 79); - assertEquals("Alive Mutant: A conditional expression has been negated without being detected by a test. (WITH_SUFFIX)", i.primaryLocation().message()); - }); - assertEquals(12, context.getStorage().getMeasures().size()); - - } - - @Test - public void execute_unknownMutantStatusRuleActive_issueCreated() throws Exception { - - createReportFile("PitestSensorTest_Java_mutations.xml"); - final TestSensorContext context = harness.createSensorContext().scanFiles(); - context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", md -> md.lines = 200); - final ActiveRules profile = harness.createActiveRules("mutant.unknownStatus"); - final PitestSensor sensor = new PitestSensor(context.getConfiguration(), profile, context.fileSystem()); - - sensor.execute(context); - - final List issues = context.getStorage().getIssues(); - assertEquals(1, issues.size()); - assertContains(issues, i -> { - assertEquals("mutant.unknownStatus", i.ruleKey().rule()); - assertEquals("test-module:src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", i.primaryLocation().inputComponent().key()); - assertEquals(1.0, i.gap(), 0.01); - assertTextrangeOnLine(i.primaryLocation().textRange(), 175, 79); - assertEquals("Alive Mutant: A return value has been replaced by a method argument without being detected by a test.", i.primaryLocation().message()); - }); - assertEquals(EXPECTED_QUANTITATIVE_METRICS, context.getStorage().getMeasures().size()); - - } - - @Test - public void execute_coverageThresholdRuleActive_belowThreshold_twoMutantMissing() throws Exception { - - createReportFile("PitestSensorTest_Java_mutations.xml"); - final TestSensorContext context = harness.createSensorContext().scanFiles(); - context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", md -> md.lines = 200); - context.setConfiguration(EFFORT_FACTOR_MISSING_COVERAGE, 2.0); - - final ActiveRules profile = harness.createActiveRules(harness.createRule("mutant.coverage", PARAM_MUTANT_COVERAGE_THRESHOLD, "66.6")); - final PitestSensor sensor = new PitestSensor(context.getConfiguration(), profile, context.fileSystem()); - - sensor.execute(context); - - final List issues = context.getStorage().getIssues(); - assertEquals(1, issues.size()); - assertContains(issues, i -> { - assertEquals("mutant.coverage", i.ruleKey().rule()); - assertEquals("test-module:src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", i.primaryLocation().inputComponent().key()); - assertEquals(2.0, i.gap(), 0.01); - assertEquals("1 more mutants need to be killed to get the mutation coverage from 50.0% to 66.6%", i.primaryLocation().message()); - }); - - assertCoverage(50.0, context.getStorage().getMeasures()); - } - - @Test - public void execute_coverageThresholdRuleActive_belowThreshold_moreMutantsMissing() throws Exception { - - createReportFile("PitestSensorTest_Java_mutations.xml"); - final TestSensorContext context = harness.createSensorContext().scanFiles(); - context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", md -> md.lines = 200); - context.setConfiguration(EFFORT_FACTOR_MISSING_COVERAGE, 2.0); - final ActiveRules profile = harness.createActiveRules(harness.createRule("mutant.coverage", PARAM_MUTANT_COVERAGE_THRESHOLD, "80.0")); - final PitestSensor sensor = new PitestSensor(context.getConfiguration(), profile, context.fileSystem()); - - sensor.execute(context); - - final List issues = context.getStorage().getIssues(); - assertEquals(1, issues.size()); - assertContains(issues, i -> { - assertEquals("mutant.coverage", i.ruleKey().rule()); - assertEquals("test-module:src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", i.primaryLocation().inputComponent().key()); - assertEquals(4.0, i.gap(), 0.01); - assertEquals("2 more mutants need to be killed to get the mutation coverage from 50.0% to 80.0%", i.primaryLocation().message()); - }); - - assertCoverage(50.0, context.getStorage().getMeasures()); - } - - @Test - public void execute_coverageThresholdRuleActive_aboveThreshold_noIssue() throws Exception { - - createReportFile("PitestSensorTest_Java_mutations.xml"); - final TestSensorContext context = harness.createSensorContext().scanFiles(); - context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", md -> md.lines = 200); - final ActiveRules profile = harness.createActiveRules(harness.createRule("mutant.coverage", PARAM_MUTANT_COVERAGE_THRESHOLD, "40.0")); - final PitestSensor sensor = new PitestSensor(context.getConfiguration(), profile, context.fileSystem()); - - sensor.execute(context); - - assertTrue(context.getStorage().getIssues().isEmpty()); - - } - - @Test - public void execute_coverageThresholdRuleActive_onThreshold_noIssue() throws Exception { - - createReportFile("PitestSensorTest_Java_mutations.xml"); - final TestSensorContext context = harness.createSensorContext().scanFiles(); - context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", md -> md.lines = 200); - context.setConfiguration(EFFORT_FACTOR_MISSING_COVERAGE, 2.0); - final ActiveRules profile = harness.createActiveRules(harness.createRule("mutant.coverage", PARAM_MUTANT_COVERAGE_THRESHOLD, "50.0")); - final PitestSensor sensor = new PitestSensor(context.getConfiguration(), profile, context.fileSystem()); - - sensor.execute(context); - - assertTrue(context.getStorage().getIssues().isEmpty()); - assertCoverage(50.0, context.getStorage().getMeasures()); - } - - @Test - public void execute_withExperimentalFeaturesEnable_TestMetricsCreated() throws Exception { - - createReportFile("PitestSensorTest_Java_mutations.xml"); - - final TestSensorContext context = harness.createSensorContext().scanFiles(); - context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", md -> md.lines = 200); - context.addTestFile("src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/MutantTest.java"); - context.setConfiguration(EXPERIMENTAL_FEATURE_ENABLED, "true"); - - final PitestSensor sensor = new PitestSensor(context.getConfiguration(), harness.createEmptyActiveRules(), context - .fileSystem()); - - sensor.execute(context); - - final List measures = context.getStorage().getMeasures(); - assertEquals(EXPECTED_QUANTITATIVE_METRICS +2, measures.size()); - assertEquals(3, assertContains(measures, m -> assertEquals(TEST_KILLS_KEY, m.metric().key())).value()); - assertEquals(6, assertContains(measures, m -> assertEquals(UTILITY_GLOBAL_MUTATIONS_KEY, m.metric().key())).value()); - } - - @Test - public void execute_withoutExperimentalFeaturesEnable_noTestMetricsCreated() throws Exception { - - createReportFile("PitestSensorTest_Java_mutations.xml"); - - final TestSensorContext context = harness.createSensorContext().scanFiles(); - context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", md -> md.lines = 200); - context.addTestFile("src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/MutantTest.java"); - context.setConfiguration(EXPERIMENTAL_FEATURE_ENABLED, "false"); - - //sensor is configured by default - final PitestSensor sensor = new PitestSensor(context.getConfiguration(), harness.createEmptyActiveRules(), context.fileSystem()); - - sensor.execute(context); - - final List measures = context.getStorage().getMeasures(); - assertEquals(12, measures.size()); - assertNotContains(measures, m -> assertEquals(TEST_KILLS_KEY, m.metric().key())); - assertNotContains(measures, m -> assertEquals(UTILITY_GLOBAL_MUTATIONS_KEY, m.metric().key())); - } - - @Test - public void execute_onlyKotlinSensor_issueCreated() throws Exception { - - this.harness = harness.changeLanguage("kotlin"); - createReportFile("PitestSensorTest_Kotlin_mutations.xml"); - final TestSensorContext context = disableJavaSensor(harness.createSensorContext().scanFiles()); - context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/KotlinExample.kt", md -> md.lines = 200); - - final ActiveRules profile = harness.createActiveRules("mutant.uncovered"); - final PitestSensor sensor = new PitestSensor(context.getConfiguration(), profile, context.fileSystem()); - - sensor.execute(context); - - final List issues = context.getStorage().getIssues(); - assertFalse(issues.isEmpty()); - assertContains(issues, i -> { - assertEquals("mutant.uncovered", i.ruleKey().rule()); - assertEquals("test-module:src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/KotlinExample.kt", i.primaryLocation().inputComponent().key()); - assertEquals(1.0, i.gap(), 0.01); - assertTextrangeOnLine(i.primaryLocation().textRange(), 28, 79); - assertEquals("Alive Mutant: A conditional expression has been negated without being detected by a test. Mutation: negated conditional", i.primaryLocation().message()); - }); - assertEquals(12, context.getStorage().getMeasures().size()); - - } - - private void createReportFile(String reportFile) throws IOException { - - harness.resourceToFile("target/pit-reports/mutations.xml", reportFile); - } - - private void assertCoverage(final double percent, final List measures) { - final int total = (int) assertContains(measures, m -> assertEquals(MUTATIONS_TOTAL_KEY, m.metric().key())).value(); - final int killed = (int) assertContains(measures, m -> assertEquals(MUTATIONS_KILLED_KEY, m.metric().key())).value(); - assertEquals(percent, (double)killed * 100./(double)total, 0.01); - } - - private void assertTextrangeOnLine(final TextRange textRange, final int expectedLineNumber, final int expectedLineLength) { - assertEquals(expectedLineNumber, textRange.start().line()); - assertEquals(0, textRange.start().lineOffset()); - assertEquals(expectedLineNumber, textRange.end().line()); - assertEquals(expectedLineLength, textRange.end().lineOffset()); - } - + public static final int EXPECTED_QUANTITATIVE_METRICS = 12; + + @RegisterExtension + public final SystemLocaleExtension extension = SystemLocaleExtension.overrideDefault(Locale.ENGLISH); + + @TempDir + public Path folder; + + private SensorTestHarness harness; + + @BeforeEach + public void setUp() throws Exception { + this.harness = SensorTestHarness.builder() + .withTempFolder(folder) + .withResourceResolver(this.getClass()) + .build(); + } + + @Test + void describe() { + final PitestSensor sensor = new PitestSensor(harness.createConfiguration(), harness.createEmptyActiveRules(), + harness.createSensorContext().fileSystem()); + final DefaultSensorDescriptor desc = new DefaultSensorDescriptor(); + + sensor.describe(desc); + + assertEquals("Mutation Analysis", desc.name()); + + assertTrue(desc.languages().contains("java")); + assertTrue(desc.ruleRepositories().contains(REPOSITORY_KEY + ".java")); + + assertTrue(desc.languages().contains("kotlin")); + assertTrue(desc.ruleRepositories().contains(REPOSITORY_KEY + ".kotlin")); + + //the following two assertion block target at killing mutants introduced + //by the javac compiler for vargs. + //these provide no extra value as the order is actually unimportant + //and any reordering on the arguments done by pit is irrelevant + Iterator langIt = desc.languages().iterator(); + assertEquals("java", langIt.next()); + assertEquals("kotlin", langIt.next()); + + Iterator repoIt = desc.ruleRepositories().iterator(); + assertEquals(REPOSITORY_KEY + ".java", repoIt.next()); + assertEquals(REPOSITORY_KEY + ".kotlin", repoIt.next()); + } + + @Test + void testToString() { + final PitestSensor sensor = new PitestSensor(harness.createConfiguration(), harness.createEmptyActiveRules(), + harness.createSensorContext().fileSystem()); + + assertEquals("PitestSensor", sensor.toString()); + } + + @Test + void execute_AllSensorsDisabled_noIssuesOrMeasuresCreated() throws IOException { + //arrange + final TestSensorContext context = disableBothSensors(harness.createSensorContext()); + createReportFile("PitestSensorTest_KotlinJava_mutations.xml"); + final ActiveRules profile = harness.createActiveRules( + harness.createRule("java", RULE_SURVIVED_MUTANT), + harness.createRule("kotlin", RULE_SURVIVED_MUTANT) + ); + context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/JavaExample.java", + md -> md.lines = 200); + context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/KotlinExample.kt", + md -> md.lines = 200); + + //act + final PitestSensor sensor = new PitestSensor(context.getConfiguration(), profile, context.fileSystem()); + sensor.execute(context); + + //assert + assertTrue(context.getStorage().getIssues().isEmpty()); + assertTrue(context.getStorage().getMeasures().isEmpty()); + } + + @Test + void execute_JavaSensorsDisabled_noJavaIssuesOrMeasuresCreated() throws IOException { + //arrange + final TestSensorContext context = disableJavaSensor(harness.createSensorContext()); + createReportFile("PitestSensorTest_KotlinJava_mutations.xml"); + final ActiveRules profile = harness.createActiveRules( + harness.createRule("java", "mutant.survived"), + harness.createRule("kotlin", "mutant.survived") + ); + + context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/JavaExample.java", + md -> md.lines = 200); + context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/KotlinExample.kt", + md -> md.lines = 200); + + //act + final PitestSensor sensor = new PitestSensor(context.getConfiguration(), profile, context.fileSystem()); + sensor.execute(context); + + //assert + assertFalse(context.getStorage().getMeasures().isEmpty()); + final List issues = context.getStorage().getIssues(); + assertContains(issues, i -> { + assertEquals("mutant.survived", i.ruleKey().rule()); + assertEquals("test-module:src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/KotlinExample.kt", + i.primaryLocation().inputComponent().key()); + assertEquals(1.0, i.gap(), 0.01); + assertTextRangeOnLine(i.primaryLocation().textRange(), 28, 79); + assertEquals( + "Alive Mutant: A conditional expression has been negated without being detected by a test. Mutation: negated conditional", + i.primaryLocation().message()); + }); + } + + @Test + void execute_KotlinSensorDisabled_noJavaIssuesOrMeasuresCreated() throws IOException { + //arrange + final TestSensorContext context = disableKotlingSensor(harness.createSensorContext()); + createReportFile("PitestSensorTest_KotlinJava_mutations.xml"); + final ActiveRules profile = harness.createActiveRules( + harness.createRule("java", RULE_SURVIVED_MUTANT), + harness.createRule("kotlin", RULE_SURVIVED_MUTANT) + ); + context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/JavaExample.java", + md -> md.lines = 200); + context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/KotlinExample.kt", + md -> md.lines = 200); + + //act + final PitestSensor sensor = new PitestSensor(context.getConfiguration(), profile, context.fileSystem()); + sensor.execute(context); + + //assert + assertFalse(context.getStorage().getMeasures().isEmpty()); + final List issues = context.getStorage().getIssues(); + assertContains(issues, i -> { + assertEquals("mutant.survived", i.ruleKey().rule()); + assertEquals("test-module:src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/JavaExample.java", + i.primaryLocation().inputComponent().key()); + assertEquals(1.0, i.gap(), 0.01); + assertTextRangeOnLine(i.primaryLocation().textRange(), 162, 79); + assertEquals("Alive Mutant: A conditional expression has been negated without being detected by a test.", + i.primaryLocation().message()); + }); + } + + @Test + void execute_noTestFile_noIssuesAndMeasuresCreated() throws Exception { + createReportFile("PitestSensorTest_Java_mutations.xml"); + final ActiveRules profile = harness.createActiveRules(RULE_SURVIVED_MUTANT); + final TestSensorContext context = harness.createSensorContext(); + + final PitestSensor sensor = new PitestSensor(context.getConfiguration(), profile, context.fileSystem()); + + sensor.execute(context); + + assertTrue(context.getStorage().getIssues().isEmpty()); + assertTrue(context.getStorage().getMeasures().isEmpty()); + } + + @Test + void execute_noReport_noIssuesAndMeasuresCreated() throws Exception { + final ActiveRules profile = harness.createActiveRules(RULE_SURVIVED_MUTANT); + final TestSensorContext context = harness.createSensorContext(); + context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", + md -> md.lines = 200); + + final PitestSensor sensor = new PitestSensor(context.getConfiguration(), profile, context.fileSystem()); + + sensor.execute(context); + + assertTrue(context.getStorage().getIssues().isEmpty()); + assertTrue(context.getStorage().getMeasures().isEmpty()); + } + + @Test + void execute_noRuleActivated_noIssuesCreated_but_MetricsCreated() throws Exception { + createReportFile("PitestSensorTest_Java_mutations.xml"); + final TestSensorContext context = harness.createSensorContext().scanFiles(); + context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", + md -> md.lines = 200); + final ActiveRules profile = harness.createEmptyActiveRules(); + + //sensor is configured by default + final PitestSensor sensor = new PitestSensor(context.getConfiguration(), profile, context.fileSystem()); + + sensor.execute(context); + + assertTrue(context.getStorage().getIssues().isEmpty()); + assertEquals(12, context.getStorage().getMeasures().size()); + } + + + @Test + void execute_mutatorSpecificRuleActive_issueCreated() throws Exception { + createReportFile("PitestSensorTest_Java_mutations.xml"); + + final TestSensorContext context = harness.createSensorContext().scanFiles(); + context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", + md -> md.lines = 200); + + final ActiveRules profile = harness.createActiveRules("mutant.NEGATE_CONDITIONALS"); + + //sensor is configured by default + final PitestSensor sensor = new PitestSensor(context.getConfiguration(), profile, context.fileSystem()); + + sensor.execute(context); + + final List issues = context.getStorage().getIssues(); + assertContains(issues, i -> { + assertEquals("mutant.NEGATE_CONDITIONALS", i.ruleKey().rule()); + assertEquals("test-module:src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", + i.primaryLocation().inputComponent().key()); + assertEquals(1.0, i.gap(), 0.01); + assertTextRangeOnLine(i.primaryLocation().textRange(), 172, 79); + assertEquals("Alive Mutant: A conditional expression has been negated without being detected by a test.", + i.primaryLocation().message()); + }); + assertContains(issues, i -> { + assertEquals("mutant.NEGATE_CONDITIONALS", i.ruleKey().rule()); + assertEquals("test-module:src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", + i.primaryLocation().inputComponent().key()); + assertEquals(1.0, i.gap(), 0.01); + assertTextRangeOnLine(i.primaryLocation().textRange(), 175, 79); + assertEquals( + "Alive Mutant: A conditional expression has been negated without being detected by a test. (WITH_SUFFIX)", + i.primaryLocation().message()); + }); + assertEquals(12, context.getStorage().getMeasures().size()); + } + + @Test + void execute_survivedMutantRuleActive_issueCreated() throws Exception { + createReportFile("PitestSensorTest_Java_mutations.xml"); + final TestSensorContext context = harness.createSensorContext().scanFiles(); + context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", + md -> md.lines = 200); + + final ActiveRules profile = harness.createActiveRules("mutant.uncovered"); + final PitestSensor sensor = new PitestSensor(context.getConfiguration(), profile, context.fileSystem()); + + sensor.execute(context); + + final List issues = context.getStorage().getIssues(); + assertEquals(1, issues.size()); + assertContains(issues, i -> { + assertEquals("mutant.uncovered", i.ruleKey().rule()); + assertEquals("test-module:src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", + i.primaryLocation().inputComponent().key()); + assertEquals(1.0, i.gap(), 0.01); + assertTextRangeOnLine(i.primaryLocation().textRange(), 175, 79); + assertEquals( + "Alive Mutant: A conditional expression has been negated without being detected by a test. (WITH_SUFFIX)", + i.primaryLocation().message()); + }); + assertEquals(12, context.getStorage().getMeasures().size()); + } + + @Test + void execute_unknownMutantStatusRuleActive_issueCreated() throws Exception { + createReportFile("PitestSensorTest_Java_mutations.xml"); + final TestSensorContext context = harness.createSensorContext().scanFiles(); + context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", + md -> md.lines = 200); + final ActiveRules profile = harness.createActiveRules("mutant.unknownStatus"); + final PitestSensor sensor = new PitestSensor(context.getConfiguration(), profile, context.fileSystem()); + + sensor.execute(context); + + final List issues = context.getStorage().getIssues(); + assertEquals(1, issues.size()); + assertContains(issues, i -> { + assertEquals("mutant.unknownStatus", i.ruleKey().rule()); + assertEquals("test-module:src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", + i.primaryLocation().inputComponent().key()); + assertEquals(1.0, i.gap(), 0.01); + assertTextRangeOnLine(i.primaryLocation().textRange(), 175, 79); + assertEquals( + "Alive Mutant: A return value has been replaced by a method argument without being detected by a test.", + i.primaryLocation().message()); + }); + assertEquals(EXPECTED_QUANTITATIVE_METRICS, context.getStorage().getMeasures().size()); + } + + @Test + void execute_coverageThresholdRuleActive_belowThreshold_twoMutantMissing() throws Exception { + createReportFile("PitestSensorTest_Java_mutations.xml"); + final TestSensorContext context = harness.createSensorContext().scanFiles(); + context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", + md -> md.lines = 200); + context.setConfiguration(EFFORT_FACTOR_MISSING_COVERAGE, 2.0); + + final ActiveRules profile = harness.createActiveRules( + harness.createRule("mutant.coverage", PARAM_MUTANT_COVERAGE_THRESHOLD, "66.6")); + final PitestSensor sensor = new PitestSensor(context.getConfiguration(), profile, context.fileSystem()); + + sensor.execute(context); + + final List issues = context.getStorage().getIssues(); + assertEquals(1, issues.size()); + assertContains(issues, i -> { + assertEquals("mutant.coverage", i.ruleKey().rule()); + assertEquals("test-module:src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", + i.primaryLocation().inputComponent().key()); + assertEquals(2.0, i.gap(), 0.01); + assertEquals("1 more mutants need to be killed to get the mutation coverage from 50.0% to 66.6%", + i.primaryLocation().message()); + }); + + assertCoverage(50.0, context.getStorage().getMeasures()); + } + + @Test + void execute_coverageThresholdRuleActive_belowThreshold_moreMutantsMissing() throws Exception { + createReportFile("PitestSensorTest_Java_mutations.xml"); + final TestSensorContext context = harness.createSensorContext().scanFiles(); + context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", + md -> md.lines = 200); + context.setConfiguration(EFFORT_FACTOR_MISSING_COVERAGE, 2.0); + final ActiveRules profile = harness.createActiveRules( + harness.createRule("mutant.coverage", PARAM_MUTANT_COVERAGE_THRESHOLD, "80.0")); + final PitestSensor sensor = new PitestSensor(context.getConfiguration(), profile, context.fileSystem()); + + sensor.execute(context); + + final List issues = context.getStorage().getIssues(); + assertEquals(1, issues.size()); + assertContains(issues, i -> { + assertEquals("mutant.coverage", i.ruleKey().rule()); + assertEquals("test-module:src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", + i.primaryLocation().inputComponent().key()); + assertEquals(4.0, i.gap(), 0.01); + assertEquals("2 more mutants need to be killed to get the mutation coverage from 50.0% to 80.0%", + i.primaryLocation().message()); + }); + + assertCoverage(50.0, context.getStorage().getMeasures()); + } + + @Test + void execute_coverageThresholdRuleActive_aboveThreshold_noIssue() throws Exception { + createReportFile("PitestSensorTest_Java_mutations.xml"); + final TestSensorContext context = harness.createSensorContext().scanFiles(); + context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", + md -> md.lines = 200); + final ActiveRules profile = harness.createActiveRules( + harness.createRule("mutant.coverage", PARAM_MUTANT_COVERAGE_THRESHOLD, "40.0")); + final PitestSensor sensor = new PitestSensor(context.getConfiguration(), profile, context.fileSystem()); + + sensor.execute(context); + + assertTrue(context.getStorage().getIssues().isEmpty()); + } + + @Test + void execute_coverageThresholdRuleActive_onThreshold_noIssue() throws Exception { + createReportFile("PitestSensorTest_Java_mutations.xml"); + final TestSensorContext context = harness.createSensorContext().scanFiles(); + context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", + md -> md.lines = 200); + context.setConfiguration(EFFORT_FACTOR_MISSING_COVERAGE, 2.0); + final ActiveRules profile = harness.createActiveRules( + harness.createRule("mutant.coverage", PARAM_MUTANT_COVERAGE_THRESHOLD, "50.0")); + final PitestSensor sensor = new PitestSensor(context.getConfiguration(), profile, context.fileSystem()); + + sensor.execute(context); + + assertTrue(context.getStorage().getIssues().isEmpty()); + assertCoverage(50.0, context.getStorage().getMeasures()); + } + + @Test + void execute_withExperimentalFeaturesEnable_TestMetricsCreated() throws Exception { + createReportFile("PitestSensorTest_Java_mutations.xml"); + + final TestSensorContext context = harness.createSensorContext().scanFiles(); + context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", + md -> md.lines = 200); + context.addTestFile("src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/MutantTest.java"); + context.setConfiguration(EXPERIMENTAL_FEATURE_ENABLED, "true"); + + final PitestSensor sensor = new PitestSensor(context.getConfiguration(), harness.createEmptyActiveRules(), + context.fileSystem()); + + sensor.execute(context); + + final List measures = context.getStorage().getMeasures(); + assertEquals(EXPECTED_QUANTITATIVE_METRICS + 2, measures.size()); + assertEquals(3, assertContains(measures, m -> assertEquals(TEST_KILLS_KEY, m.metric().key())).value()); + assertEquals(6, + assertContains(measures, m -> assertEquals(UTILITY_GLOBAL_MUTATIONS_KEY, m.metric().key())).value()); + } + + @Test + void execute_withoutExperimentalFeaturesEnable_noTestMetricsCreated() throws Exception { + createReportFile("PitestSensorTest_Java_mutations.xml"); + + final TestSensorContext context = harness.createSensorContext().scanFiles(); + context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/Mutant.java", + md -> md.lines = 200); + context.addTestFile("src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/MutantTest.java"); + context.setConfiguration(EXPERIMENTAL_FEATURE_ENABLED, "false"); + + //sensor is configured by default + final PitestSensor sensor = new PitestSensor(context.getConfiguration(), harness.createEmptyActiveRules(), + context.fileSystem()); + + sensor.execute(context); + + final List measures = context.getStorage().getMeasures(); + assertEquals(12, measures.size()); + assertNotContains(measures, m -> assertEquals(TEST_KILLS_KEY, m.metric().key())); + assertNotContains(measures, m -> assertEquals(UTILITY_GLOBAL_MUTATIONS_KEY, m.metric().key())); + } + + @Test + void execute_onlyKotlinSensor_issueCreated() throws Exception { + this.harness = harness.changeLanguage("kotlin"); + createReportFile("PitestSensorTest_Kotlin_mutations.xml"); + final TestSensorContext context = disableJavaSensor(harness.createSensorContext().scanFiles()); + context.addTestFile("src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/KotlinExample.kt", + md -> md.lines = 200); + + final ActiveRules profile = harness.createActiveRules("mutant.uncovered"); + final PitestSensor sensor = new PitestSensor(context.getConfiguration(), profile, context.fileSystem()); + + sensor.execute(context); + + final List issues = context.getStorage().getIssues(); + assertFalse(issues.isEmpty()); + assertContains(issues, i -> { + assertEquals("mutant.uncovered", i.ruleKey().rule()); + assertEquals("test-module:src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/KotlinExample.kt", + i.primaryLocation().inputComponent().key()); + assertEquals(1.0, i.gap(), 0.01); + assertTextRangeOnLine(i.primaryLocation().textRange(), 28, 79); + assertEquals( + "Alive Mutant: A conditional expression has been negated without being detected by a test. Mutation: negated conditional", + i.primaryLocation().message()); + }); + assertEquals(12, context.getStorage().getMeasures().size()); + } + + private void createReportFile(String reportFile) throws IOException { + harness.resourceToFile("target/pit-reports/mutations.xml", reportFile); + } + + private void assertCoverage(final double percent, final List measures) { + final int total = (int) assertContains(measures, m -> assertEquals(MUTATIONS_TOTAL_KEY, m.metric().key())).value(); + final int killed = (int) assertContains(measures, + m -> assertEquals(MUTATIONS_KILLED_KEY, m.metric().key())).value(); + assertEquals(percent, (double) killed * 100. / (double) total, 0.01); + } + + private void assertTextRangeOnLine(final TextRange textRange, final int expectedLineNumber, + final int expectedLineLength) { + assertEquals(expectedLineNumber, textRange.start().line()); + assertEquals(0, textRange.start().lineOffset()); + assertEquals(expectedLineNumber, textRange.end().line()); + assertEquals(expectedLineLength, textRange.end().lineOffset()); + } + + private TestSensorContext disableBothSensors(final TestSensorContext context) { + return context.setConfiguration(MutationAnalysisPlugin.PITEST_JAVA_SENSOR_ENABLED, false) + .setConfiguration(MutationAnalysisPlugin.PITEST_KOTLIN_SENSOR_ENABLED, false); + } + + private TestSensorContext disableJavaSensor(final TestSensorContext context) { + return context.setConfiguration(MutationAnalysisPlugin.PITEST_JAVA_SENSOR_ENABLED, false); + } + + private TestSensorContext disableKotlingSensor(final TestSensorContext context) { + return context.setConfiguration(MutationAnalysisPlugin.PITEST_KOTLIN_SENSOR_ENABLED, false); + } - private TestSensorContext disableBothSensors(final TestSensorContext context) { - return context.setConfiguration(MutationAnalysisPlugin.PITEST_JAVA_SENSOR_ENABLED, false) - .setConfiguration(MutationAnalysisPlugin.PITEST_KOTLIN_SENSOR_ENABLED, false); - } - private TestSensorContext disableJavaSensor(final TestSensorContext context) { - return context.setConfiguration(MutationAnalysisPlugin.PITEST_JAVA_SENSOR_ENABLED, false); - } - private TestSensorContext disableKotlingSensor(final TestSensorContext context) { - return context.setConfiguration(MutationAnalysisPlugin.PITEST_KOTLIN_SENSOR_ENABLED, false); - } } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/ReportCollectorTest.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/ReportCollectorTest.java index 19deb28..8d96800 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/ReportCollectorTest.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/ReportCollectorTest.java @@ -22,58 +22,56 @@ import static ch.devcon5.sonar.plugins.mutationanalysis.MutationAnalysisPlugin.EXPERIMENTAL_FEATURE_ENABLED; import static ch.devcon5.sonar.plugins.mutationanalysis.MutationAnalysisPlugin.REPORT_DIRECTORY_KEY; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant; +import ch.devcon5.sonar.plugins.mutationanalysis.testharness.SensorTestHarness; +import ch.devcon5.sonar.plugins.mutationanalysis.testharness.TestConfiguration; +import ch.devcon5.sonar.plugins.mutationanalysis.testharness.TestSensorContext; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.charset.StandardCharsets; import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.OpenOption; import java.nio.file.Path; import java.nio.file.spi.FileSystemProvider; import java.util.Collection; - -import ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant; -import ch.devcon5.sonar.plugins.mutationanalysis.testharness.SensorTestHarness; -import ch.devcon5.sonar.plugins.mutationanalysis.testharness.TestConfiguration; -import ch.devcon5.sonar.plugins.mutationanalysis.testharness.TestSensorContext; import org.apache.commons.io.IOUtils; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; /** - * + * Report Collector Tests */ public class ReportCollectorTest { public static final String DEFAULT_PIT_REPORTS_DIR = "target/pit-reports"; - @Rule - public TemporaryFolder folder = new TemporaryFolder(); + + @TempDir + public Path folder; private TestConfiguration configuration; private SensorTestHarness harness; - @Before + @BeforeEach public void setUp() throws Exception { - this.harness = SensorTestHarness.builder().withTempFolder(folder).build(); this.configuration = harness.createConfiguration(); } @Test - public void findProjectRoot_noMavenOrGradleProject_noModules() throws IOException { - - final Path moduleRoot = folder.newFolder("test-module").toPath(); + void findProjectRoot_noMavenOrGradleProject_noModules() throws IOException { + final Path moduleRoot = Files.createDirectories(folder.resolve("test-module")); final Path childModuleRoot1 = Files.createDirectories(moduleRoot.resolve("child-module-1")); final TestSensorContext context = harness.changeBasePath(moduleRoot).createSensorContext(); @@ -87,9 +85,8 @@ public void findProjectRoot_noMavenOrGradleProject_noModules() throws IOExceptio } @Test - public void findProjectRoot_MavenAndGradleProject_findRootFromAllChildren() throws IOException { - - final Path moduleRoot = folder.newFolder("test-module").toPath(); + void findProjectRoot_MavenAndGradleProject_findRootFromAllChildren() throws IOException { + final Path moduleRoot = Files.createDirectories(folder.resolve("test-module")); final Path mvnChildModuleRoot = Files.createDirectories(moduleRoot.resolve("child-module-mvn")); final Path gradleChildModuleRoot = Files.createDirectories(moduleRoot.resolve("child-module-grdl")); @@ -106,9 +103,8 @@ public void findProjectRoot_MavenAndGradleProject_findRootFromAllChildren() thro } @Test - public void findProjectRoot_singleNonMavenNonGradleProject() throws IOException { - - final Path moduleRoot = folder.newFolder("test-module").toPath(); + void findProjectRoot_singleNonMavenNonGradleProject() throws IOException { + final Path moduleRoot = Files.createFile(folder.resolve("test-module")); final TestSensorContext context = harness.changeBasePath(moduleRoot).createSensorContext(); final ReportCollector collector = new ReportCollector(configuration, context.fileSystem()); @@ -119,9 +115,8 @@ public void findProjectRoot_singleNonMavenNonGradleProject() throws IOException } @Test - public void findProjectRoot_singleModuleMavenProject() throws IOException { - - final Path moduleRoot = folder.newFolder("test-module").toPath(); + void findProjectRoot_singleModuleMavenProject() throws IOException { + final Path moduleRoot = Files.createDirectories(folder.resolve("test-module")).toAbsolutePath(); createPom(moduleRoot); final TestSensorContext context = harness.changeBasePath(moduleRoot).createSensorContext(); @@ -133,9 +128,8 @@ public void findProjectRoot_singleModuleMavenProject() throws IOException { } @Test - public void findProjectRoot_multiModuleMavenProject() throws IOException { - - final Path moduleRoot = Files.createDirectories(folder.getRoot().toPath().resolve("root-module")); + void findProjectRoot_multiModuleMavenProject() throws IOException { + final Path moduleRoot = Files.createDirectories(folder.resolve("root-module")); final Path childModuleRoot = Files.createDirectories(moduleRoot.resolve("child-module")); createPom(moduleRoot, "child-module"); @@ -150,9 +144,8 @@ public void findProjectRoot_multiModuleMavenProject() throws IOException { } @Test - public void findProjectRoot_multiModuleMavenProjectWithMalformedPom() throws IOException { - - final Path moduleRoot = Files.createDirectories(folder.getRoot().toPath().resolve("root-module")); + void findProjectRoot_multiModuleMavenProjectWithMalformedPom() throws IOException { + final Path moduleRoot = Files.createDirectories(folder.resolve("root-module")); final Path childModuleRoot = Files.createDirectories(moduleRoot.resolve("child-module")); createMalFormedPom(moduleRoot, "child-module"); @@ -167,9 +160,8 @@ public void findProjectRoot_multiModuleMavenProjectWithMalformedPom() throws IOE } @Test - public void findProjectRoot_multiModuleMavenProjectWithSiblingParent_withoutSharedReactorPom() throws IOException { - - final Path root = Files.createDirectories(folder.getRoot().toPath().resolve("root")); + void findProjectRoot_multiModuleMavenProjectWithSiblingParent_withoutSharedReactorPom() throws IOException { + final Path root = Files.createDirectories(folder.resolve("root")); final Path parentModuleRoot = Files.createDirectories(root.resolve("parent-module")); final Path childModuleRoot = Files.createDirectories(root.resolve("child-module")); @@ -186,9 +178,8 @@ public void findProjectRoot_multiModuleMavenProjectWithSiblingParent_withoutShar } @Test - public void findProjectRoot_multiModuleMavenProjectWithSiblingParent_withSharedReactorPom() throws IOException { - - final Path moduleRoot = Files.createDirectories(folder.getRoot().toPath().resolve("root-module")); + void findProjectRoot_multiModuleMavenProjectWithSiblingParent_withSharedReactorPom() throws IOException { + final Path moduleRoot = Files.createDirectories(folder.resolve("root-module")); final Path parentModuleRoot = Files.createDirectories(moduleRoot.resolve("parent-module")); final Path childModuleRoot = Files.createDirectories(moduleRoot.resolve("child-module")); @@ -207,9 +198,8 @@ public void findProjectRoot_multiModuleMavenProjectWithSiblingParent_withSharedR @Test - public void findProjectRoot_singleModuleGradleProject() throws IOException { - - final Path moduleRoot = folder.newFolder("test-module").toPath(); + void findProjectRoot_singleModuleGradleProject() throws IOException { + final Path moduleRoot = Files.createDirectories(folder.resolve("test-module")); createSettings(moduleRoot); final TestSensorContext context = harness.changeBasePath(moduleRoot).createSensorContext(); @@ -221,9 +211,8 @@ public void findProjectRoot_singleModuleGradleProject() throws IOException { } @Test - public void findProjectRoot_multiModuleGradleProject() throws IOException { - - final Path moduleRoot = Files.createDirectories(folder.getRoot().toPath().resolve("root-module")); + void findProjectRoot_multiModuleGradleProject() throws IOException { + final Path moduleRoot = Files.createDirectories(folder.resolve("root-module")); final Path childModuleRoot = Files.createDirectories(moduleRoot.resolve("child-module")); createSettings(moduleRoot, "child-module"); @@ -238,9 +227,8 @@ public void findProjectRoot_multiModuleGradleProject() throws IOException { } @Test - public void findProjectRoot_multiModuleProjectWithoutConfigurationFiles() throws IOException { - - final Path moduleRoot = Files.createDirectories(folder.getRoot().toPath().resolve("root-module")); + void findProjectRoot_multiModuleProjectWithoutConfigurationFiles() throws IOException { + final Path moduleRoot = Files.createDirectories(folder.resolve("root-module")); final Path childModuleRoot = Files.createDirectories(moduleRoot.resolve("child-module")); final TestSensorContext context = harness.changeBasePath(moduleRoot).createSensorContext(); @@ -252,9 +240,8 @@ public void findProjectRoot_multiModuleProjectWithoutConfigurationFiles() throws } @Test - public void findProjectRoot_multiModuleGradleProjectWithSettingsFileAndPomFileMalformed() throws IOException { - - final Path moduleRoot = Files.createDirectories(folder.getRoot().toPath().resolve("root-module")); + void findProjectRoot_multiModuleGradleProjectWithSettingsFileAndPomFileMalformed() throws IOException { + final Path moduleRoot = Files.createDirectories(folder.resolve("root-module")); final Path childModuleRoot = Files.createDirectories(moduleRoot.resolve("child-module")); createMalFormedSettings(moduleRoot, "child-module"); @@ -271,9 +258,8 @@ public void findProjectRoot_multiModuleGradleProjectWithSettingsFileAndPomFileMa } @Test - public void findProjectRoot_multiGradleProjectWithSettingsFileMalformedAndAValidPomFile() throws IOException { - - final Path moduleRoot = Files.createDirectories(folder.getRoot().toPath().resolve("root-module")); + void findProjectRoot_multiGradleProjectWithSettingsFileMalformedAndAValidPomFile() throws IOException { + final Path moduleRoot = Files.createDirectories(folder.resolve("root-module")); final Path childModuleRoot = Files.createDirectories(moduleRoot.resolve("child-module")); createMalFormedSettings(moduleRoot, "child-module"); @@ -290,9 +276,8 @@ public void findProjectRoot_multiGradleProjectWithSettingsFileMalformedAndAValid } @Test - public void findProjectRoot_multiModuleGradleProjectWithMalformedSettings() throws IOException { - - final Path moduleRoot = Files.createDirectories(folder.getRoot().toPath().resolve("root-module")); + void findProjectRoot_multiModuleGradleProjectWithMalformedSettings() throws IOException { + final Path moduleRoot = Files.createDirectories(folder.resolve("root-module")); final Path childModuleRoot = Files.createDirectories(moduleRoot.resolve("child-module")); createMalFormedSettings(moduleRoot, "child-module"); @@ -307,9 +292,8 @@ public void findProjectRoot_multiModuleGradleProjectWithMalformedSettings() thro } @Test - public void collectLocalMutants_defaultReportDirectory() throws IOException { - - final Path moduleRoot = folder.newFolder("test-module").toPath(); + void collectLocalMutants_defaultReportDirectory() throws IOException { + final Path moduleRoot = Files.createDirectories(folder.resolve("test-module")); createPom(moduleRoot); createMutationReportsFile(moduleRoot, "target/pit-reports", "ReportCollectorTest_mutations.xml"); @@ -323,12 +307,11 @@ public void collectLocalMutants_defaultReportDirectory() throws IOException { } @Test - public void collectLocalMutants_customReportDirectory() throws IOException { - + void collectLocalMutants_customReportDirectory() throws IOException { final String reportsDirectory = "target/reports"; configuration.set(REPORT_DIRECTORY_KEY, reportsDirectory); - final Path moduleRoot = folder.newFolder("test-module").toPath(); + final Path moduleRoot = Files.createDirectories(folder.resolve("test-module")); createPom(moduleRoot); createMutationReportsFile(moduleRoot, reportsDirectory, "ReportCollectorTest_mutations.xml"); @@ -341,9 +324,8 @@ public void collectLocalMutants_customReportDirectory() throws IOException { } @Test - public void collectGlobalMutants_experimentalFeaturesDisable_noMutants() throws IOException { - - final Path moduleRoot = folder.newFolder("test-module").toPath(); + void collectGlobalMutants_experimentalFeaturesDisable_noMutants() throws IOException { + final Path moduleRoot = Files.createDirectories(folder.resolve("test-module")); createPom(moduleRoot); createMutationReportsFile(moduleRoot, DEFAULT_PIT_REPORTS_DIR, "ReportCollectorTest_mutations.xml"); @@ -356,11 +338,10 @@ public void collectGlobalMutants_experimentalFeaturesDisable_noMutants() throws } @Test - public void collectGlobalMutants_singleModule_defaultReportDirectory() throws IOException { - + void collectGlobalMutants_singleModule_defaultReportDirectory() throws IOException { configuration.set(EXPERIMENTAL_FEATURE_ENABLED, true); - final Path moduleRoot = folder.newFolder("test-module").toPath(); + final Path moduleRoot = Files.createDirectories(folder.resolve("test-module")); createPom(moduleRoot); createMutationReportsFile(moduleRoot, DEFAULT_PIT_REPORTS_DIR, "ReportCollectorTest_mutations.xml"); @@ -373,13 +354,12 @@ public void collectGlobalMutants_singleModule_defaultReportDirectory() throws IO } @Test - public void collectGlobalMutants_singleModule_customReportDirectory() throws IOException { - + void collectGlobalMutants_singleModule_customReportDirectory() throws IOException { final String reportsDirectory = "target/reports"; configuration.set(REPORT_DIRECTORY_KEY, reportsDirectory); configuration.set(EXPERIMENTAL_FEATURE_ENABLED, true); - final Path moduleRoot = folder.newFolder("test-module").toPath(); + final Path moduleRoot = Files.createDirectories(folder.resolve("test-module")); createPom(moduleRoot); createMutationReportsFile(moduleRoot, reportsDirectory, "ReportCollectorTest_mutations.xml"); @@ -392,11 +372,10 @@ public void collectGlobalMutants_singleModule_customReportDirectory() throws IOE } @Test - public void collectGlobalMutants_multiModule_defaultReportDirectory() throws IOException { - + void collectGlobalMutants_multiModule_defaultReportDirectory() throws IOException { configuration.set(EXPERIMENTAL_FEATURE_ENABLED, true); - final Path moduleRoot = Files.createDirectories(folder.getRoot().toPath().resolve("root-module")); + final Path moduleRoot = Files.createDirectories(folder.resolve("root-module")); final Path childModule1Root = Files.createDirectories(moduleRoot.resolve("child-module1")); final Path childModule2Root = Files.createDirectories(moduleRoot.resolve("child-module2")); @@ -416,11 +395,10 @@ public void collectGlobalMutants_multiModule_defaultReportDirectory() throws IOE } @Test - public void collectGlobalMutants_multiModule_rootFolderFromSettings() throws IOException { - + void collectGlobalMutants_multiModule_rootFolderFromSettings() throws IOException { configuration.set(EXPERIMENTAL_FEATURE_ENABLED, true); - final Path parentRoot = Files.createDirectories(folder.getRoot().toPath().resolve("root-module")); + final Path parentRoot = Files.createDirectories(folder.resolve("root-module")); final Path moduleRoot = Files.createDirectories(parentRoot.resolve("parent")); final Path childModule1Root = Files.createDirectories(moduleRoot.resolve("child-module1")); final Path childModule2Root = Files.createDirectories(parentRoot.resolve("child-module2")); @@ -444,11 +422,10 @@ public void collectGlobalMutants_multiModule_rootFolderFromSettings() throws IOE } @Test - public void collectGlobalMutants_multiModule_rootFolderFromRelativePathInPom() throws IOException { - + void collectGlobalMutants_multiModule_rootFolderFromRelativePathInPom() throws IOException { configuration.set(EXPERIMENTAL_FEATURE_ENABLED, true); - final Path parentRoot = Files.createDirectories(folder.getRoot().toPath().resolve("root-module")); + final Path parentRoot = Files.createDirectories(folder.resolve("root-module")); final Path intermediate = Files.createDirectories(parentRoot.resolve("intermediate")); final Path childModule1Root = Files.createDirectories(intermediate.resolve("child-module1")); final Path childModule2Root = Files.createDirectories(intermediate.resolve("child-module2")); @@ -469,13 +446,12 @@ public void collectGlobalMutants_multiModule_rootFolderFromRelativePathInPom() t } @Test - public void collectGlobalMutants_multiModule_customReportDirectory() throws IOException { - + void collectGlobalMutants_multiModule_customReportDirectory() throws IOException { final String reportDirectory = "target/custom"; configuration.set(REPORT_DIRECTORY_KEY, reportDirectory); configuration.set(EXPERIMENTAL_FEATURE_ENABLED, true); - final Path moduleRoot = Files.createDirectories(folder.getRoot().toPath().resolve("root-module")); + final Path moduleRoot = Files.createDirectories(folder.resolve("root-module")); final Path childModule1Root = Files.createDirectories(moduleRoot.resolve("child-module1")); final Path childModule2Root = Files.createDirectories(moduleRoot.resolve("child-module2")); @@ -495,8 +471,7 @@ public void collectGlobalMutants_multiModule_customReportDirectory() throws IOEx } @Test - public void exceptionHandling_of_isSamePath_fsMockCausesIOException_false() throws Exception { - + void exceptionHandling_of_isSamePath_fsMockCausesIOException_false() throws Exception { //I found no other efficient way than using mockito to induce IOException on working with Path FileSystemProvider fsProv = mock(FileSystemProvider.class); FileSystem fs = mock(FileSystem.class); @@ -512,8 +487,7 @@ public void exceptionHandling_of_isSamePath_fsMockCausesIOException_false() thro } @Test - public void exceptionHandling_of_readMutantsFromReport_fsMockCausesIOException_emptyResult() throws Exception { - + void exceptionHandling_of_readMutantsFromReport_fsMockCausesIOException_emptyResult() throws Exception { //I found no other efficient way than using mockito to induce IOException on working with Path FileSystemProvider fsProv = mock(FileSystemProvider.class); FileSystem fs = mock(FileSystem.class); @@ -532,7 +506,6 @@ public void exceptionHandling_of_readMutantsFromReport_fsMockCausesIOException_e } private void createPomWithRelativeParent(final Path moduleRoot, String relParent) throws IOException { - StringBuilder b = new StringBuilder(128); b.append(""); b.append(""); @@ -546,11 +519,10 @@ private void createPomWithRelativeParent(final Path moduleRoot, String relParent b.append(""); b.append(""); - Files.write(moduleRoot.resolve("pom.xml"), b.toString().getBytes("UTF-8")); + Files.write(moduleRoot.resolve("pom.xml"), b.toString().getBytes(StandardCharsets.UTF_8)); } private void createPom(final Path moduleRoot, String... moduleNames) throws IOException { - StringBuilder b = new StringBuilder(128); b.append(""); if (moduleNames.length > 0) { @@ -562,11 +534,10 @@ private void createPom(final Path moduleRoot, String... moduleNames) throws IOEx } b.append(""); - Files.write(moduleRoot.resolve("pom.xml"), b.toString().getBytes("UTF-8")); + Files.write(moduleRoot.resolve("pom.xml"), b.toString().getBytes(StandardCharsets.UTF_8)); } private void createMalFormedPom(final Path moduleRoot, String... moduleNames) throws IOException { - StringBuilder b = new StringBuilder(128); b.append(""); if (moduleNames.length > 0) { @@ -578,41 +549,34 @@ private void createMalFormedPom(final Path moduleRoot, String... moduleNames) th } b.append(""); - Files.write(moduleRoot.resolve("pom.xml"), b.toString().getBytes("UTF-8")); + Files.write(moduleRoot.resolve("pom.xml"), b.toString().getBytes(StandardCharsets.UTF_8)); } private void createSettings(Path moduleRoot, String... moduleNames) throws IOException { - StringBuilder b = new StringBuilder(128); b.append("rootProject.name = \'test-root-project\'\n\n"); - if (moduleNames.length > 0) { - for (String module : moduleNames) { - b.append("include \'").append(module).append("\'\n)"); - } + for (String module : moduleNames) { + b.append("include \'").append(module).append("\'\n)"); } - Files.write(moduleRoot.resolve("settings.gradle"), b.toString().getBytes("UTF-8")); + Files.write(moduleRoot.resolve("settings.gradle"), b.toString().getBytes(StandardCharsets.UTF_8)); } private void createMalFormedSettings(Path moduleRoot, String... moduleNames) throws IOException { - StringBuilder b = new StringBuilder(128); b.append("rootProject.name = \'test-root-project\'\n\n"); - if (moduleNames.length > 0) { - for (String module : moduleNames) { - b.append("malformed \'").append(module).append("\'\n)"); - } + for (String module : moduleNames) { + b.append("malformed \'").append(module).append("\'\n)"); } - Files.write(moduleRoot.resolve("settings.gradle"), b.toString().getBytes("UTF-8")); + Files.write(moduleRoot.resolve("settings.gradle"), b.toString().getBytes(StandardCharsets.UTF_8)); } private Path createMutationReportsFile(final Path moduleRoot, final String reportsDirectory, final String resourceName) throws IOException { - final Path reportsDir = Files.createDirectories(moduleRoot.resolve(reportsDirectory)); final Path reportFile = reportsDir.resolve("mutations.xml"); - try (InputStream is = ReportCollectorTest.class.getResourceAsStream(resourceName); - OutputStream os = Files.newOutputStream(reportFile)) { + try (InputStream is = ReportCollectorTest.class.getResourceAsStream(resourceName); OutputStream os = Files.newOutputStream(reportFile)) { IOUtils.copy(is, os); } return reportFile; } + } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/ResourceResolverTest.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/ResourceResolverTest.java index e0608c4..15b615e 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/ResourceResolverTest.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/ResourceResolverTest.java @@ -20,69 +20,79 @@ package ch.devcon5.sonar.plugins.mutationanalysis.sensors; -import static org.junit.Assert.assertEquals; - -import java.io.IOException; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import ch.devcon5.sonar.plugins.mutationanalysis.testharness.SensorTestHarness; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import java.nio.file.Path; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import org.sonar.api.batch.fs.FileSystem; public class ResourceResolverTest { - @ClassRule - public static TemporaryFolder folder = new TemporaryFolder(); - - private static SensorTestHarness HARNESS; - private static FileSystem FILESYSTEM; - - private ResourceResolver resolver; - - @BeforeClass - public static void setUpFilesystem() throws Exception { - SensorTestHarness harness = SensorTestHarness.builder().withTempFolder(folder).build(); - - harness.createSourceFile("src/main/java/ch/example/java/", "Example.java"); - harness.createSourceFile("src/main/java/ch/example/java/", "Example$Nested.java"); - harness.createSourceFile("src/main/java/ch/example/java/", "Example$Nested$Nested.java"); - harness.createSourceFile("src/main/kotlin/ch/example/kotlin/", "Example.kt"); - harness.createSourceFile("src/main/kotlin/ch/example/kotlin/", "Example$Nested.kt"); - harness.createSourceFile("src/main/kotlin/ch/example/kotlin/", "Example$Nested$Nested.kt"); - - FILESYSTEM = harness.createSensorContext().scanFiles().fileSystem(); - } - - @Before - public void setUp() throws Exception { - resolver = new ResourceResolver(FILESYSTEM); - } - - @Test - public void resolve_javaFile() throws IOException { - assertEquals("Example.java", resolver.resolve("ch.example.java.Example").get().filename()); - } - @Test - public void resolve_nestedJavaClass() throws IOException { - assertEquals("Example.java", resolver.resolve("ch.example.java.Example$Nested").get().filename()); - } - @Test - public void resolve_deeplyNestedJavaClass() throws IOException { - assertEquals("Example.java", resolver.resolve("ch.example.java.Example$Nested$Nested").get().filename()); - } - @Test - public void resolve_kotlinClass() throws IOException { - assertEquals("Example.kt", resolver.resolve("ch.example.kotlin.Example").get().filename()); - } - @Test - public void resolve_nestedKotlinClass() throws IOException { - assertEquals("Example.kt", resolver.resolve("ch.example.kotlin.Example$Nested").get().filename()); - } - @Test - public void resolve_deeplyNestedKotlinClass() throws IOException { - assertEquals("Example.kt", resolver.resolve("ch.example.kotlin.Example$Nested$Nested").get().filename()); - } + @TempDir + public static Path folder; + + private static FileSystem FILESYSTEM; + + private ResourceResolver resolver; + + @BeforeAll + public static void setUpFilesystem() throws Exception { + SensorTestHarness harness = SensorTestHarness.builder().withTempFolder(folder).build(); + + harness.createSourceFile("src/main/java/ch/example/java/", "Example.java"); + harness.createSourceFile("src/main/java/ch/example/java/", "Example$Nested.java"); + harness.createSourceFile("src/main/java/ch/example/java/", "Example$Nested$Nested.java"); + harness.createSourceFile("src/main/kotlin/ch/example/kotlin/", "Example.kt"); + harness.createSourceFile("src/main/kotlin/ch/example/kotlin/", "Example$Nested.kt"); + harness.createSourceFile("src/main/kotlin/ch/example/kotlin/", "Example$Nested$Nested.kt"); + + FILESYSTEM = harness.createSensorContext().scanFiles().fileSystem(); + } + + @BeforeEach + public void setUp() throws Exception { + resolver = new ResourceResolver(FILESYSTEM); + } + + @Test + void resolve_javaFile() { + assertTrue(resolver.resolve("ch.example.java.Example").isPresent()); + assertEquals("Example.java", resolver.resolve("ch.example.java.Example").get().filename()); + } + + @Test + void resolve_nestedJavaClass() { + assertTrue(resolver.resolve("ch.example.java.Example$Nested").isPresent()); + assertEquals("Example.java", resolver.resolve("ch.example.java.Example$Nested").get().filename()); + } + + @Test + void resolve_deeplyNestedJavaClass() { + assertTrue(resolver.resolve("ch.example.java.Example$Nested$Nested").isPresent()); + assertEquals("Example.java", resolver.resolve("ch.example.java.Example$Nested$Nested").get().filename()); + } + + @Test + void resolve_kotlinClass() { + assertTrue(resolver.resolve("ch.example.kotlin.Example").isPresent()); + assertEquals("Example.kt", resolver.resolve("ch.example.kotlin.Example").get().filename()); + } + + @Test + void resolve_nestedKotlinClass() { + assertTrue(resolver.resolve("ch.example.kotlin.Example$Nested").isPresent()); + assertEquals("Example.kt", resolver.resolve("ch.example.kotlin.Example$Nested").get().filename()); + } + + @Test + void resolve_deeplyNestedKotlinClass() { + assertTrue(resolver.resolve("ch.example.kotlin.Example$Nested$Nested").isPresent()); + assertEquals("Example.kt", resolver.resolve("ch.example.kotlin.Example$Nested$Nested").get().filename()); + } + } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/RulesProcessorTest.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/RulesProcessorTest.java index 9b025e7..293f21e 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/RulesProcessorTest.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/RulesProcessorTest.java @@ -22,29 +22,38 @@ import static ch.devcon5.sonar.plugins.mutationanalysis.MutationAnalysisPlugin.EFFORT_FACTOR_MISSING_COVERAGE; import static ch.devcon5.sonar.plugins.mutationanalysis.MutationAnalysisPlugin.EFFORT_FACTOR_SURVIVED_MUTANT; -import static ch.devcon5.sonar.plugins.mutationanalysis.rules.MutationAnalysisRulesDefinition.*; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Locale; +import static ch.devcon5.sonar.plugins.mutationanalysis.rules.MutationAnalysisRulesDefinition.PARAM_MUTANT_COVERAGE_THRESHOLD; +import static ch.devcon5.sonar.plugins.mutationanalysis.rules.MutationAnalysisRulesDefinition.RULE_MUTANT_COVERAGE; +import static ch.devcon5.sonar.plugins.mutationanalysis.rules.MutationAnalysisRulesDefinition.RULE_SURVIVED_MUTANT; +import static ch.devcon5.sonar.plugins.mutationanalysis.rules.MutationAnalysisRulesDefinition.RULE_UNCOVERED_MUTANT; +import static ch.devcon5.sonar.plugins.mutationanalysis.rules.MutationAnalysisRulesDefinition.RULE_UNKNOWN_MUTANT_STATUS; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import ch.devcon5.sonar.plugins.mutationanalysis.metrics.ResourceMutationMetrics; import ch.devcon5.sonar.plugins.mutationanalysis.model.MutationOperator; import ch.devcon5.sonar.plugins.mutationanalysis.model.MutationOperators; import ch.devcon5.sonar.plugins.mutationanalysis.rules.JavaRulesDefinition; import ch.devcon5.sonar.plugins.mutationanalysis.rules.MutationAnalysisRulesDefinition; -import ch.devcon5.sonar.plugins.mutationanalysis.testharness.*; +import ch.devcon5.sonar.plugins.mutationanalysis.testharness.LogRecordingAppender; +import ch.devcon5.sonar.plugins.mutationanalysis.testharness.SensorTestHarness; +import ch.devcon5.sonar.plugins.mutationanalysis.testharness.SystemLocaleExtension; +import ch.devcon5.sonar.plugins.mutationanalysis.testharness.TestConfiguration; +import ch.devcon5.sonar.plugins.mutationanalysis.testharness.TestSensorContext; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Locale; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.LogEvent; -import org.junit.After; -import org.junit.Before; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.api.io.TempDir; import org.sonar.api.batch.rule.ActiveRules; import org.sonar.api.batch.sensor.issue.Issue; import org.sonar.api.rules.Rule; @@ -52,439 +61,439 @@ import org.sonar.api.server.rule.RulesDefinitionXmlLoader; /** - * + * Rules Processor Tests */ -public class RulesProcessorTest { +class RulesProcessorTest { + + @RegisterExtension + public final SystemLocaleExtension extension = SystemLocaleExtension.overrideDefault(Locale.ENGLISH); - private static final MutationOperator[] MUTATION_OPERATORS = MutationOperators.allMutationOperators().toArray(new MutationOperator[0]); - - @org.junit.Rule - public TemporaryFolder folder = new TemporaryFolder(); + private static final MutationOperator[] MUTATION_OPERATORS = MutationOperators.allMutationOperators() + .toArray(new MutationOperator[0]); - private TestConfiguration configuration; - private MutationAnalysisRulesDefinition def; - private RulesDefinition.Context rulesContext; + @TempDir + public Path folder; - private SensorTestHarness harness; - private LogRecordingAppender appender; - - - @ClassRule - public static SystemLocale systemLocale = SystemLocale.overrideDefault(Locale.ENGLISH); - - @Before - public void setUp() throws Exception { - - this.harness = SensorTestHarness.builder().withTempFolder(folder).build(); - this.configuration = this.harness.createConfiguration(); - - RulesDefinitionXmlLoader rulesLoader = new RulesDefinitionXmlLoader(); - this.def = new JavaRulesDefinition(configuration, rulesLoader); - this.rulesContext = new RulesDefinition.Context(); - this.def.define(rulesContext); - - appender = new LogRecordingAppender(); - } - - @After - public void tearDown() throws Exception { - appender.close(); - } - - @Test - public void processRules_noRules_withMetrics_noIssues() throws Exception { - - //arrange - final TestSensorContext context = harness.createSensorContext(); - final ActiveRules profile = harness.createEmptyActiveRules(); - final Collection metrics = Arrays.asList(context.newResourceMutationMetrics("Test.java", md -> { - md.lines = 100; - md.mutants.survived = 3; - md.mutants.killed = 9; - })); - - //act - final RulesProcessor processor = new RulesProcessor(configuration, profile); - processor.processRules(metrics, context, "java"); - - //assert - final List issues = context.getStorage().getIssues(); - assertTrue(issues.isEmpty()); - - final List events = appender.getEvents(); - assertTrue(events.stream() - .filter(e -> e.getLevel() == Level.WARN) - .map(e -> e.getMessage().getFormattedMessage()) - .anyMatch( - "/!\\ At least one Mutation Analysis rule needs to be activated for the current profile and language: java."::equals)); - - } - - @Test - public void processRules_survivorRuleActive_defaultEffortFactor_survivedMutant_issueCreated() { - - //arrange - final TestSensorContext context = harness.createSensorContext(); - final ActiveRules profile = harness.createActiveRules(RULE_SURVIVED_MUTANT); - - final Collection metrics = Arrays.asList(context.newResourceMutationMetrics("Test.java", md -> { - md.lines = 100; - md.mutants.survived = 3; - md.mutants.killed = 9; - })); - - //act - final RulesProcessor processor = new RulesProcessor(configuration, profile); - processor.processRules(metrics, context, "java"); - - //assert - final List issues = context.getStorage().getIssues(); - assertIssueAtLine(issues.get(0), RULE_SURVIVED_MUTANT, "test-module:Test.java", 3, 1.0); - assertIssueAtLine(issues.get(1), RULE_SURVIVED_MUTANT, "test-module:Test.java", 4, 1.0); - assertIssueAtLine(issues.get(2), RULE_SURVIVED_MUTANT, "test-module:Test.java", 5, 1.0); - assertEquals(3, issues.size()); - - assertTrue(appender.getEvents().isEmpty()); - } - - @Test - public void processRules_survivorRuleActive_defaultEffortFactor_survivedMutant_withDescription_issueCreated() { - - //arrange - final TestSensorContext context = harness.createSensorContext(); - final ActiveRules profile = harness.createActiveRules(RULE_SURVIVED_MUTANT); - final Collection metrics = Arrays.asList(context.newResourceMutationMetrics("Test.java", md -> { - md.lines = 100; - md.mutants.survived = 3; - md.mutants.killed = 9; - md.mutants.description = "substituted foo with bar"; - })); - - //act - final RulesProcessor processor = new RulesProcessor(configuration, profile); - processor.processRules(metrics, context, "java"); - - //assert - final List issues = context.getStorage().getIssues(); - assertIssueAtLine(issues.get(0), RULE_SURVIVED_MUTANT, "test-module:Test.java", 3, 1.0, " Mutation: substituted foo with bar"); - assertIssueAtLine(issues.get(1), RULE_SURVIVED_MUTANT, "test-module:Test.java", 4, 1.0, " Mutation: substituted foo with bar"); - assertIssueAtLine(issues.get(2), RULE_SURVIVED_MUTANT, "test-module:Test.java", 5, 1.0, " Mutation: substituted foo with bar"); - assertEquals(3, issues.size()); - assertTrue(appender.getEvents().isEmpty()); - } - - @Test - public void processRules_survivorRuleActive_customEffortFactor_survivedMutant_issueCreated() { - - //arrange - configuration.set(EFFORT_FACTOR_SURVIVED_MUTANT, 10.0); - final TestSensorContext context = harness.createSensorContext(); - final ActiveRules profile = harness.createActiveRules(RULE_SURVIVED_MUTANT); - final Collection metrics = Arrays.asList(context.newResourceMutationMetrics("Test.java", md -> { - md.lines = 100; - md.mutants.survived = 3; - md.mutants.killed = 9; - })); - - //act - final RulesProcessor processor = new RulesProcessor(configuration, profile); - processor.processRules(metrics, context, "java"); - - //assert - final List issues = context.getStorage().getIssues(); - assertEquals(3, issues.size()); - assertIssueAtLine(issues.get(0), RULE_SURVIVED_MUTANT, "test-module:Test.java", 3, 10.0); - assertIssueAtLine(issues.get(1), RULE_SURVIVED_MUTANT, "test-module:Test.java", 4, 10.0); - assertIssueAtLine(issues.get(2), RULE_SURVIVED_MUTANT, "test-module:Test.java", 5, 10.0); - assertTrue(appender.getEvents().isEmpty()); - } - - @Test - public void processRules_uncoveredRuleActive_defaultEffortFactor_uncoveredMutant_issueCreated() { - - //arrange - final TestSensorContext context = harness.createSensorContext(); - final ActiveRules profile = harness.createActiveRules(RULE_UNCOVERED_MUTANT); - final Collection metrics = Arrays.asList(context.newResourceMutationMetrics("Test.java", md -> { - md.lines = 100; - md.mutants.noCoverage = 2; - md.mutants.killed = 9; - })); - - //act - final RulesProcessor processor = new RulesProcessor(configuration, profile); - processor.processRules(metrics, context, "java"); - - //assert - final List issues = context.getStorage().getIssues(); - assertIssueAtLine(issues.get(0), RULE_UNCOVERED_MUTANT, "test-module:Test.java", 2, 1.0); - assertIssueAtLine(issues.get(1), RULE_UNCOVERED_MUTANT, "test-module:Test.java", 3, 1.0); - assertEquals(2, issues.size()); - assertTrue(appender.getEvents().isEmpty()); - } - - @Test - public void processRules_uncoveredRuleActive_customEffortFactor_uncoveredMutant_issueCreated() { - - //arrange - configuration.set(EFFORT_FACTOR_SURVIVED_MUTANT, 10.0); - final TestSensorContext context = harness.createSensorContext(); - final ActiveRules profile = harness.createActiveRules(RULE_UNCOVERED_MUTANT); - final Collection metrics = Arrays.asList(context.newResourceMutationMetrics("Test.java", md -> { - md.lines = 100; - md.mutants.noCoverage = 2; - md.mutants.killed = 9; - })); - - //act - final RulesProcessor processor = new RulesProcessor(configuration, profile); - processor.processRules(metrics, context, "java"); - - //assert - final List issues = context.getStorage().getIssues(); - assertIssueAtLine(issues.get(0), RULE_UNCOVERED_MUTANT, "test-module:Test.java", 2, 10.0); - assertIssueAtLine(issues.get(1), RULE_UNCOVERED_MUTANT, "test-module:Test.java", 3, 10.0); - assertEquals(2, issues.size()); - assertTrue(appender.getEvents().isEmpty()); - } - - @Test - public void processRules_unknownMutationStatusRuleActive_defaultEffortFactor_unknownMutationState_issueCreated() { - - //arrange - final TestSensorContext context = harness.createSensorContext(); - final ActiveRules profile = harness.createActiveRules(RULE_UNKNOWN_MUTANT_STATUS); - final Collection metrics = Arrays.asList(context.newResourceMutationMetrics("Test.java", md -> { - md.lines = 100; - md.mutants.unknown = 2; - md.mutants.killed = 9; - })); - - //act - final RulesProcessor processor = new RulesProcessor(configuration, profile); - processor.processRules(metrics, context, "java"); - - //assert - final List issues = context.getStorage().getIssues(); - assertIssueAtLine(issues.get(0), RULE_UNKNOWN_MUTANT_STATUS, "test-module:Test.java", 2, 1.0); - assertIssueAtLine(issues.get(1), RULE_UNKNOWN_MUTANT_STATUS, "test-module:Test.java", 3, 1.0); - assertEquals(2, issues.size()); - assertTrue(appender.getEvents().isEmpty()); - } - - @Test - public void processRules_unknownMutationStatusRuleActive_customEffortFactor_unknownMutationState_issueCreated() { - - //arrange - configuration.set(EFFORT_FACTOR_SURVIVED_MUTANT, 10.0); - final TestSensorContext context = harness.createSensorContext(); - final ActiveRules profile = harness.createActiveRules(RULE_UNKNOWN_MUTANT_STATUS); - final Collection metrics = Arrays.asList(context.newResourceMutationMetrics("Test.java", md -> { - md.lines = 100; - md.mutants.unknown = 2; - md.mutants.killed = 9; - })); - - //act - final RulesProcessor processor = new RulesProcessor(configuration, profile); - processor.processRules(metrics, context, "java"); - - //assert - final List issues = context.getStorage().getIssues(); - assertIssueAtLine(issues.get(0), RULE_UNKNOWN_MUTANT_STATUS, "test-module:Test.java", 2, 10.0); - assertIssueAtLine(issues.get(1), RULE_UNKNOWN_MUTANT_STATUS, "test-module:Test.java", 3, 10.0); - assertEquals(2, issues.size()); - assertTrue(appender.getEvents().isEmpty()); - } - - @Test - public void processRules_mutatorRuleActive_defaultEffortFactor_mutantSurvived_issueCreated() { - - //arrange - final TestSensorContext context = harness.createSensorContext(); - final ActiveRules profile = harness.createActiveRules(createMutationOperatorRules()); - final Collection metrics = Arrays.asList(context.newResourceMutationMetrics("Test.java", md -> { - md.lines = 100; - md.mutants.survived = MUTATION_OPERATORS.length; - md.mutants.killed = 5; - })); - - //act - final RulesProcessor processor = new RulesProcessor(configuration, profile); - processor.processRules(metrics, context, "java"); - - //assert - final List issues = context.getStorage().getIssues(); - assertEquals(23, issues.size()); - for (int i = 0, len = MUTATION_OPERATORS.length; i < len; i++) { - assertIssueAtLine(issues.get(i), "mutant." + MUTATION_OPERATORS[i].getId(), "test-module:Test.java", MUTATION_OPERATORS.length + i, 1.0); - } - assertTrue(appender.getEvents().isEmpty()); - } - - @Test - public void processRules_coverageThresholdRuleActive_defaultEffortFactor_coverageTooLow_issueCreated() { - - //arrange - final TestSensorContext context = harness.createSensorContext(); - final ActiveRules profile = harness.createActiveRules(harness.createRule(RULE_MUTANT_COVERAGE, - PARAM_MUTANT_COVERAGE_THRESHOLD, "80")); - final Collection metrics = Arrays.asList(context.newResourceMutationMetrics("Test.java", md -> { - md.mutants.survived = 3; - md.mutants.killed = 9; - })); - - //act - final RulesProcessor processor = new RulesProcessor(configuration, profile); - processor.processRules(metrics, context, "java"); - - //assert - final Issue issue = context.getStorage().getIssues().get(0); - assertIssueAtLine(issue, RULE_MUTANT_COVERAGE, "test-module:Test.java", 1.0, "1 more mutants need to be killed to get the mutation coverage from 75.0% to 80.0%"); - assertTrue(appender.getEvents().isEmpty()); - } - - @Test - public void processRules_coverageThresholdRuleActive_customEffortFactor_coverageTooLow_issueCreated() { - - //arrange - configuration.set(EFFORT_FACTOR_MISSING_COVERAGE, 6.0); - final TestSensorContext context = harness.createSensorContext(); - final ActiveRules profile = harness.createActiveRules(harness.createRule(RULE_MUTANT_COVERAGE, - PARAM_MUTANT_COVERAGE_THRESHOLD, "80")); - final Collection metrics = Arrays.asList(context.newResourceMutationMetrics("Test.java", md -> { - md.mutants.survived = 3; - md.mutants.killed = 9; - })); - - //act - final RulesProcessor processor = new RulesProcessor(configuration, profile); - processor.processRules(metrics, context, "java"); - - //assert - final Issue issue = context.getStorage().getIssues().get(0); - assertIssueAtLine(issue, RULE_MUTANT_COVERAGE, "test-module:Test.java", 6.0, "1 more mutants need to be killed to get the mutation coverage from 75.0% to 80.0%"); - assertTrue(appender.getEvents().isEmpty()); - } + private TestConfiguration configuration; + private SensorTestHarness harness; + private LogRecordingAppender appender; + + @BeforeEach + public void setUp() throws Exception { + this.harness = SensorTestHarness.builder().withTempFolder(folder).build(); + this.configuration = this.harness.createConfiguration(); + + RulesDefinitionXmlLoader rulesLoader = new RulesDefinitionXmlLoader(); + MutationAnalysisRulesDefinition definition = new JavaRulesDefinition(configuration, rulesLoader); + RulesDefinition.Context rulesContext = new RulesDefinition.Context(); + definition.define(rulesContext); + + appender = new LogRecordingAppender(); + } + + @AfterEach + public void tearDown() throws Exception { + appender.close(); + } @Test - public void processRules_coverageThresholdRuleInactive_defaultEffortFactor_coverageThresholdMissed_noIssueCreated() { + void processRules_noRules_withMetrics_noIssues() throws Exception { + //arrange + final TestSensorContext context = harness.createSensorContext(); + final ActiveRules profile = harness.createEmptyActiveRules(); + final Collection metrics = Collections.singletonList( + context.newResourceMutationMetrics("Test.java", md -> { + md.lines = 100; + md.mutants.survived = 3; + md.mutants.killed = 9; + })); + //act + final RulesProcessor processor = new RulesProcessor(configuration, profile); + processor.processRules(metrics, context, "java"); + + //assert + final List issues = context.getStorage().getIssues(); + assertTrue(issues.isEmpty()); + + final List events = appender.getEvents(); + assertTrue(events.stream() + .filter(e -> e.getLevel() == Level.WARN) + .map(e -> e.getMessage().getFormattedMessage()) + .anyMatch( + "/!\\ At least one Mutation Analysis rule needs to be activated for the current profile and language: java."::equals)); + } + + @Test + void processRules_survivorRuleActive_defaultEffortFactor_survivedMutant_issueCreated() { //arrange final TestSensorContext context = harness.createSensorContext(); - final ActiveRules profile = harness.createActiveRules("some.rule"); - final Collection metrics = Arrays.asList(context.newResourceMutationMetrics("Test.java", md -> { - md.mutants.survived = 2; - md.mutants.killed = 8; - })); + final ActiveRules profile = harness.createActiveRules(RULE_SURVIVED_MUTANT); + + final Collection metrics = Collections.singletonList( + context.newResourceMutationMetrics("Test.java", md -> { + md.lines = 100; + md.mutants.survived = 3; + md.mutants.killed = 9; + })); //act final RulesProcessor processor = new RulesProcessor(configuration, profile); processor.processRules(metrics, context, "java"); //assert - assertTrue(context.getStorage().getIssues().isEmpty()); + final List issues = context.getStorage().getIssues(); + assertIssueAtLine(issues.get(0), RULE_SURVIVED_MUTANT, "test-module:Test.java", 3, 1.0); + assertIssueAtLine(issues.get(1), RULE_SURVIVED_MUTANT, "test-module:Test.java", 4, 1.0); + assertIssueAtLine(issues.get(2), RULE_SURVIVED_MUTANT, "test-module:Test.java", 5, 1.0); + assertEquals(3, issues.size()); assertTrue(appender.getEvents().isEmpty()); } - @Test - public void processRules_coverageThresholdRuleActive_defaultEffortFactor_coverageThresholdHit_noIssueCreated() { + @Test + void processRules_survivorRuleActive_defaultEffortFactor_survivedMutant_withDescription_issueCreated() { + //arrange + final TestSensorContext context = harness.createSensorContext(); + final ActiveRules profile = harness.createActiveRules(RULE_SURVIVED_MUTANT); + final Collection metrics = Collections.singletonList( + context.newResourceMutationMetrics("Test.java", md -> { + md.lines = 100; + md.mutants.survived = 3; + md.mutants.killed = 9; + md.mutants.description = "substituted foo with bar"; + })); - //arrange - final TestSensorContext context = harness.createSensorContext(); - final ActiveRules profile = harness.createActiveRules(harness.createRule(RULE_MUTANT_COVERAGE, - PARAM_MUTANT_COVERAGE_THRESHOLD, "80")); - final Collection metrics = Arrays.asList(context.newResourceMutationMetrics("Test.java", md -> { - md.mutants.survived = 2; - md.mutants.killed = 8; - })); + //act + final RulesProcessor processor = new RulesProcessor(configuration, profile); + processor.processRules(metrics, context, "java"); + + //assert + final List issues = context.getStorage().getIssues(); + assertIssueAtLine(issues.get(0), RULE_SURVIVED_MUTANT, "test-module:Test.java", 3, 1.0, + " Mutation: substituted foo with bar"); + assertIssueAtLine(issues.get(1), RULE_SURVIVED_MUTANT, "test-module:Test.java", 4, 1.0, + " Mutation: substituted foo with bar"); + assertIssueAtLine(issues.get(2), RULE_SURVIVED_MUTANT, "test-module:Test.java", 5, 1.0, + " Mutation: substituted foo with bar"); + assertEquals(3, issues.size()); + assertTrue(appender.getEvents().isEmpty()); + } + + @Test + void processRules_survivorRuleActive_customEffortFactor_survivedMutant_issueCreated() { + //arrange + configuration.set(EFFORT_FACTOR_SURVIVED_MUTANT, 10.0); + final TestSensorContext context = harness.createSensorContext(); + final ActiveRules profile = harness.createActiveRules(RULE_SURVIVED_MUTANT); + final Collection metrics = Collections.singletonList( + context.newResourceMutationMetrics("Test.java", md -> { + md.lines = 100; + md.mutants.survived = 3; + md.mutants.killed = 9; + })); - //act - final RulesProcessor processor = new RulesProcessor(configuration, profile); - processor.processRules(metrics, context, "java"); + //act + final RulesProcessor processor = new RulesProcessor(configuration, profile); + processor.processRules(metrics, context, "java"); - //assert - assertTrue(context.getStorage().getIssues().isEmpty()); - assertTrue(appender.getEvents().isEmpty()); - } + //assert + final List issues = context.getStorage().getIssues(); + assertEquals(3, issues.size()); + assertIssueAtLine(issues.get(0), RULE_SURVIVED_MUTANT, "test-module:Test.java", 3, 10.0); + assertIssueAtLine(issues.get(1), RULE_SURVIVED_MUTANT, "test-module:Test.java", 4, 10.0); + assertIssueAtLine(issues.get(2), RULE_SURVIVED_MUTANT, "test-module:Test.java", 5, 10.0); + assertTrue(appender.getEvents().isEmpty()); + } @Test - public void processRules_coverageThresholdRuleActive_defaultThreshold_noIssueCreated() { + void processRules_uncoveredRuleActive_defaultEffortFactor_uncoveredMutant_issueCreated() { + //arrange + final TestSensorContext context = harness.createSensorContext(); + final ActiveRules profile = harness.createActiveRules(RULE_UNCOVERED_MUTANT); + final Collection metrics = Collections.singletonList( + context.newResourceMutationMetrics("Test.java", md -> { + md.lines = 100; + md.mutants.noCoverage = 2; + md.mutants.killed = 9; + })); + + //act + final RulesProcessor processor = new RulesProcessor(configuration, profile); + processor.processRules(metrics, context, "java"); + + //assert + final List issues = context.getStorage().getIssues(); + assertIssueAtLine(issues.get(0), RULE_UNCOVERED_MUTANT, "test-module:Test.java", 2, 1.0); + assertIssueAtLine(issues.get(1), RULE_UNCOVERED_MUTANT, "test-module:Test.java", 3, 1.0); + assertEquals(2, issues.size()); + assertTrue(appender.getEvents().isEmpty()); + } + @Test + void processRules_uncoveredRuleActive_customEffortFactor_uncoveredMutant_issueCreated() { //arrange + configuration.set(EFFORT_FACTOR_SURVIVED_MUTANT, 10.0); final TestSensorContext context = harness.createSensorContext(); - final ActiveRules profile = harness.createActiveRules(harness.createRule(RULE_MUTANT_COVERAGE)); - final Collection metrics = Arrays.asList(context.newResourceMutationMetrics("Test.java", md -> { - md.mutants.survived = 2; - md.mutants.killed = 8; - })); + final ActiveRules profile = harness.createActiveRules(RULE_UNCOVERED_MUTANT); + final Collection metrics = Collections.singletonList( + context.newResourceMutationMetrics("Test.java", md -> { + md.lines = 100; + md.mutants.noCoverage = 2; + md.mutants.killed = 9; + })); //act final RulesProcessor processor = new RulesProcessor(configuration, profile); processor.processRules(metrics, context, "java"); //assert - assertTrue(context.getStorage().getIssues().isEmpty()); + final List issues = context.getStorage().getIssues(); + assertIssueAtLine(issues.get(0), RULE_UNCOVERED_MUTANT, "test-module:Test.java", 2, 10.0); + assertIssueAtLine(issues.get(1), RULE_UNCOVERED_MUTANT, "test-module:Test.java", 3, 10.0); + assertEquals(2, issues.size()); assertTrue(appender.getEvents().isEmpty()); } - @Test - public void processRules_coverageThresholdRuleActive_gapIsSamllerThan05_RoundedUp_issueCreated() { + @Test + void processRules_unknownMutationStatusRuleActive_defaultEffortFactor_unknownMutationState_issueCreated() { + //arrange + final TestSensorContext context = harness.createSensorContext(); + final ActiveRules profile = harness.createActiveRules(RULE_UNKNOWN_MUTANT_STATUS); + final Collection metrics = Collections.singletonList( + context.newResourceMutationMetrics("Test.java", md -> { + md.lines = 100; + md.mutants.unknown = 2; + md.mutants.killed = 9; + })); - //arrange - final TestSensorContext context = harness.createSensorContext(); - final ActiveRules profile = harness.createActiveRules(harness.createRule(RULE_MUTANT_COVERAGE, - PARAM_MUTANT_COVERAGE_THRESHOLD, "80")); - final Collection metrics = Arrays.asList(context.newResourceMutationMetrics("Test.java", md -> { - md.mutants.survived = 2; - md.mutants.killed = 7; - })); + //act + final RulesProcessor processor = new RulesProcessor(configuration, profile); + processor.processRules(metrics, context, "java"); - //act - final RulesProcessor processor = new RulesProcessor(configuration, profile); - processor.processRules(metrics, context, "java"); + //assert + final List issues = context.getStorage().getIssues(); + assertIssueAtLine(issues.get(0), RULE_UNKNOWN_MUTANT_STATUS, "test-module:Test.java", 2, 1.0); + assertIssueAtLine(issues.get(1), RULE_UNKNOWN_MUTANT_STATUS, "test-module:Test.java", 3, 1.0); + assertEquals(2, issues.size()); + assertTrue(appender.getEvents().isEmpty()); + } - //assert - final Issue issue = context.getStorage().getIssues().get(0); - assertIssueAtLine(issue, RULE_MUTANT_COVERAGE, "test-module:Test.java", 1.0, "1 more mutants need to be killed to get the mutation coverage from 77.8% to 80.0%"); - assertTrue(appender.getEvents().isEmpty()); - } + @Test + void processRules_unknownMutationStatusRuleActive_customEffortFactor_unknownMutationState_issueCreated() { + //arrange + configuration.set(EFFORT_FACTOR_SURVIVED_MUTANT, 10.0); + final TestSensorContext context = harness.createSensorContext(); + final ActiveRules profile = harness.createActiveRules(RULE_UNKNOWN_MUTANT_STATUS); + final Collection metrics = Collections.singletonList( + context.newResourceMutationMetrics("Test.java", md -> { + md.lines = 100; + md.mutants.unknown = 2; + md.mutants.killed = 9; + })); - private Rule[] createMutationOperatorRules() { + //act + final RulesProcessor processor = new RulesProcessor(configuration, profile); + processor.processRules(metrics, context, "java"); - return Arrays.stream(MUTATION_OPERATORS).map(o -> harness.createRule("mutant." + o.getId())).toArray(Rule[]::new); - } + //assert + final List issues = context.getStorage().getIssues(); + assertIssueAtLine(issues.get(0), RULE_UNKNOWN_MUTANT_STATUS, "test-module:Test.java", 2, 10.0); + assertIssueAtLine(issues.get(1), RULE_UNKNOWN_MUTANT_STATUS, "test-module:Test.java", 3, 10.0); + assertEquals(2, issues.size()); + assertTrue(appender.getEvents().isEmpty()); + } - private void assertIssueAtLine(final Issue issue, String ruleKey, String componentName, final double gap, String message) { + @Test + void processRules_mutatorRuleActive_defaultEffortFactor_mutantSurvived_issueCreated() { + //arrange + final TestSensorContext context = harness.createSensorContext(); + final ActiveRules profile = harness.createActiveRules(createMutationOperatorRules()); + final Collection metrics = Collections.singletonList( + context.newResourceMutationMetrics("Test.java", md -> { + md.lines = 100; + md.mutants.survived = MUTATION_OPERATORS.length; + md.mutants.killed = 5; + })); - assertIssueAtLine(issue, ruleKey, componentName, -1, gap, message); - } + //act + final RulesProcessor processor = new RulesProcessor(configuration, profile); + processor.processRules(metrics, context, "java"); - private void assertIssueAtLine(final Issue issue, String ruleKey, String componentName, int line, final double gap) { - assertIssueAtLine(issue, ruleKey, componentName, line, gap, null); - } + //assert + final List issues = context.getStorage().getIssues(); + assertEquals(23, issues.size()); + for (int i = 0, len = MUTATION_OPERATORS.length; i < len; i++) { + assertIssueAtLine(issues.get(i), "mutant." + MUTATION_OPERATORS[i].getId(), "test-module:Test.java", + MUTATION_OPERATORS.length + i, 1.0); + } + assertTrue(appender.getEvents().isEmpty()); + } - private void assertIssueAtLine(final Issue issue, String ruleKey, String componentName, int line, final double gap, String description) { + @Test + void processRules_coverageThresholdRuleActive_defaultEffortFactor_coverageTooLow_issueCreated() { + //arrange + final TestSensorContext context = harness.createSensorContext(); + final ActiveRules profile = harness.createActiveRules(harness.createRule(RULE_MUTANT_COVERAGE, + PARAM_MUTANT_COVERAGE_THRESHOLD, "80")); + final Collection metrics = Collections.singletonList( + context.newResourceMutationMetrics("Test.java", md -> { + md.mutants.survived = 3; + md.mutants.killed = 9; + })); - assertEquals(ruleKey, issue.ruleKey().rule()); - assertEquals(componentName, issue.primaryLocation().inputComponent().key()); - assertEquals(gap, issue.gap(), 0.05); + //act + final RulesProcessor processor = new RulesProcessor(configuration, profile); + processor.processRules(metrics, context, "java"); - final String message; + //assert + final Issue issue = context.getStorage().getIssues().get(0); + assertIssueAtLine(issue, RULE_MUTANT_COVERAGE, "test-module:Test.java", 1.0, + "1 more mutants need to be killed to get the mutation coverage from 75.0% to 80.0%"); + assertTrue(appender.getEvents().isEmpty()); + } - if (line != -1) { - assertEquals(line, issue.primaryLocation().textRange().start().line()); - message = TestSensorContext.getMutationOperatorForLine(line).getViolationDescription() + optionalDescription(description); - } else { - assertNull("issue is not expected to have a textrange", issue.primaryLocation().textRange()); - message = optionalDescription(description); - } + @Test + void processRules_coverageThresholdRuleActive_customEffortFactor_coverageTooLow_issueCreated() { + //arrange + configuration.set(EFFORT_FACTOR_MISSING_COVERAGE, 6.0); + final TestSensorContext context = harness.createSensorContext(); + final ActiveRules profile = harness.createActiveRules(harness.createRule(RULE_MUTANT_COVERAGE, + PARAM_MUTANT_COVERAGE_THRESHOLD, "80")); + final Collection metrics = Collections.singletonList( + context.newResourceMutationMetrics("Test.java", md -> { + md.mutants.survived = 3; + md.mutants.killed = 9; + })); - assertEquals(message, issue.primaryLocation().message()); - } + //act + final RulesProcessor processor = new RulesProcessor(configuration, profile); + processor.processRules(metrics, context, "java"); - private String optionalDescription(final String description) { - return description == null ? "" : description; - } + //assert + final Issue issue = context.getStorage().getIssues().get(0); + assertIssueAtLine(issue, RULE_MUTANT_COVERAGE, "test-module:Test.java", 6.0, + "1 more mutants need to be killed to get the mutation coverage from 75.0% to 80.0%"); + assertTrue(appender.getEvents().isEmpty()); + } + + @Test + void processRules_coverageThresholdRuleInactive_defaultEffortFactor_coverageThresholdMissed_noIssueCreated() { + //arrange + final TestSensorContext context = harness.createSensorContext(); + final ActiveRules profile = harness.createActiveRules("some.rule"); + final Collection metrics = Collections.singletonList( + context.newResourceMutationMetrics("Test.java", md -> { + md.mutants.survived = 2; + md.mutants.killed = 8; + })); + + //act + final RulesProcessor processor = new RulesProcessor(configuration, profile); + processor.processRules(metrics, context, "java"); + + //assert + assertTrue(context.getStorage().getIssues().isEmpty()); + assertTrue(appender.getEvents().isEmpty()); + } + + @Test + void processRules_coverageThresholdRuleActive_defaultEffortFactor_coverageThresholdHit_noIssueCreated() { + //arrange + final TestSensorContext context = harness.createSensorContext(); + final ActiveRules profile = harness.createActiveRules(harness.createRule(RULE_MUTANT_COVERAGE, + PARAM_MUTANT_COVERAGE_THRESHOLD, "80")); + final Collection metrics = Collections.singletonList( + context.newResourceMutationMetrics("Test.java", md -> { + md.mutants.survived = 2; + md.mutants.killed = 8; + })); + + //act + final RulesProcessor processor = new RulesProcessor(configuration, profile); + processor.processRules(metrics, context, "java"); + + //assert + assertTrue(context.getStorage().getIssues().isEmpty()); + assertTrue(appender.getEvents().isEmpty()); + } + + @Test + void processRules_coverageThresholdRuleActive_defaultThreshold_noIssueCreated() { + //arrange + final TestSensorContext context = harness.createSensorContext(); + final ActiveRules profile = harness.createActiveRules(harness.createRule(RULE_MUTANT_COVERAGE)); + final Collection metrics = Collections.singletonList( + context.newResourceMutationMetrics("Test.java", md -> { + md.mutants.survived = 2; + md.mutants.killed = 8; + })); + + //act + final RulesProcessor processor = new RulesProcessor(configuration, profile); + processor.processRules(metrics, context, "java"); + + //assert + assertTrue(context.getStorage().getIssues().isEmpty()); + assertTrue(appender.getEvents().isEmpty()); + } + + @Test + void processRules_coverageThresholdRuleActive_gapIsSamllerThan05_RoundedUp_issueCreated() { + //arrange + final TestSensorContext context = harness.createSensorContext(); + final ActiveRules profile = harness.createActiveRules(harness.createRule(RULE_MUTANT_COVERAGE, + PARAM_MUTANT_COVERAGE_THRESHOLD, "80")); + final Collection metrics = Collections.singletonList( + context.newResourceMutationMetrics("Test.java", md -> { + md.mutants.survived = 2; + md.mutants.killed = 7; + })); + + //act + final RulesProcessor processor = new RulesProcessor(configuration, profile); + processor.processRules(metrics, context, "java"); + + //assert + final Issue issue = context.getStorage().getIssues().get(0); + assertIssueAtLine(issue, RULE_MUTANT_COVERAGE, "test-module:Test.java", 1.0, + "1 more mutants need to be killed to get the mutation coverage from 77.8% to 80.0%"); + assertTrue(appender.getEvents().isEmpty()); + } + + private Rule[] createMutationOperatorRules() { + return Arrays.stream(MUTATION_OPERATORS).map(o -> harness.createRule("mutant." + o.getId())).toArray(Rule[]::new); + } + + private void assertIssueAtLine(final Issue issue, String ruleKey, String componentName, final double gap, + String message) { + assertIssueAtLine(issue, ruleKey, componentName, -1, gap, message); + } + + private void assertIssueAtLine(final Issue issue, String ruleKey, String componentName, int line, final double gap) { + assertIssueAtLine(issue, ruleKey, componentName, line, gap, null); + } + + private void assertIssueAtLine(final Issue issue, String ruleKey, String componentName, int line, final double gap, + String description) { + assertEquals(ruleKey, issue.ruleKey().rule()); + assertEquals(componentName, issue.primaryLocation().inputComponent().key()); + assertEquals(gap, issue.gap(), 0.05); + + final String message; + if (line != -1) { + assertEquals(line, issue.primaryLocation().textRange().start().line()); + message = TestSensorContext.getMutationOperatorForLine(line).getViolationDescription() + optionalDescription( + description); + } else { + assertNull(issue.primaryLocation().textRange(), "issue is not expected to have a textrange"); + message = optionalDescription(description); + } + + assertEquals(message, issue.primaryLocation().message()); + } + + private String optionalDescription(final String description) { + return description == null ? "" : description; + } } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/SourceMetricsWriterTest.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/SourceMetricsWriterTest.java index 525f026..60c59f1 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/SourceMetricsWriterTest.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/SourceMetricsWriterTest.java @@ -20,11 +20,17 @@ package ch.devcon5.sonar.plugins.mutationanalysis.sensors; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics; +import ch.devcon5.sonar.plugins.mutationanalysis.metrics.ResourceMutationMetrics; +import ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant; +import ch.devcon5.sonar.plugins.mutationanalysis.testharness.SensorTestHarness; +import ch.devcon5.sonar.plugins.mutationanalysis.testharness.TestSensorContext; import java.io.Serializable; +import java.nio.file.Path; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -33,39 +39,32 @@ import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.IntStream; - -import ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics; -import ch.devcon5.sonar.plugins.mutationanalysis.metrics.ResourceMutationMetrics; -import ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant; -import ch.devcon5.sonar.plugins.mutationanalysis.testharness.SensorTestHarness; -import ch.devcon5.sonar.plugins.mutationanalysis.testharness.TestSensorContext; import org.jetbrains.annotations.NotNull; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage; import org.sonar.api.batch.sensor.measure.Measure; /** - * + * Source Metrics Tests */ public class SourceMetricsWriterTest { public static final int EXPECTED_QUANTITATIVE_METRICS = 12; - @Rule - public TemporaryFolder folder = new TemporaryFolder(); + + @TempDir + public Path folder; private SensorTestHarness harness; - @Before + @BeforeEach public void setUp() throws Exception { this.harness = SensorTestHarness.builder().withTempFolder(folder).build(); } @Test - public void writeMetrics_noMutants_noMetric_nothingWritten() { - + void writeMetrics_noMutants_noMetric_nothingWritten() { SourceMetricsWriter smw = new SourceMetricsWriter(); final TestSensorContext context = harness.createSensorContext(); @@ -78,8 +77,7 @@ public void writeMetrics_noMutants_noMetric_nothingWritten() { } @Test - public void writeMetrics_singleResourceMetrics_withGlobalMutants_metricsWritten() throws Exception { - + void writeMetrics_singleResourceMetrics_withGlobalMutants_metricsWritten() { SourceMetricsWriter smw = new SourceMetricsWriter(); final TestSensorContext context = harness.createSensorContext(); @@ -109,8 +107,7 @@ public void writeMetrics_singleResourceMetrics_withGlobalMutants_metricsWritten( } @Test - public void writeMetrics_singleResourceMetrics_metricsWritten() throws Exception { - + void writeMetrics_singleResourceMetrics_metricsWritten() { SourceMetricsWriter smw = new SourceMetricsWriter(); final TestSensorContext context = harness.createSensorContext(); @@ -140,13 +137,11 @@ public void writeMetrics_singleResourceMetrics_metricsWritten() throws Exception } @Test - public void writeMetrics_singleTestResourceMetrics_noMetricsWritten() throws Exception { - - + void writeMetrics_singleTestResourceMetrics_noMetricsWritten() { final TestSensorContext context = harness.createSensorContext(); final Collection globalMutants = Collections.emptyList(); - final Collection metrics = Arrays.asList( - context.newResourceMutationMetrics("Test.java",md -> md.isTestResource = true)); + final Collection metrics = Collections.singletonList( + context.newResourceMutationMetrics("Test.java", md -> md.isTestResource = true)); final SourceMetricsWriter smw = new SourceMetricsWriter(); smw.writeMetrics(metrics, context, globalMutants); @@ -160,21 +155,21 @@ public void writeMetrics_singleTestResourceMetrics_noMetricsWritten() throws Exc } @Test - public void writeMetrics_singleResourceMetrics_noMutantKilled_noCoverageWritten() throws Exception { - + void writeMetrics_singleResourceMetrics_noMutantKilled_noCoverageWritten() { SourceMetricsWriter smw = new SourceMetricsWriter(); final TestSensorContext context = harness.createSensorContext(); final Collection globalMutants = Collections.emptyList(); - final Collection metrics = Arrays.asList(context.newResourceMutationMetrics("Test.java", md -> { - md.lines = 100; - md.mutants.unknown = 0; - md.mutants.noCoverage = 5; - md.mutants.survived = 10; - md.mutants.memoryError = 0; - md.mutants.timedOut = 0; - md.mutants.killed = 0; - })); + final Collection metrics = Collections.singletonList( + context.newResourceMutationMetrics("Test.java", md -> { + md.lines = 100; + md.mutants.unknown = 0; + md.mutants.noCoverage = 5; + md.mutants.survived = 10; + md.mutants.memoryError = 0; + md.mutants.timedOut = 0; + md.mutants.killed = 0; + })); smw.writeMetrics(metrics, context, globalMutants); @@ -183,32 +178,31 @@ public void writeMetrics_singleResourceMetrics_noMutantKilled_noCoverageWritten( @Test - public void writeMetrics_multiResourceMetrics_metricsWritten() throws Exception { - + void writeMetrics_multiResourceMetrics_metricsWritten() { SourceMetricsWriter smw = new SourceMetricsWriter(); final TestSensorContext context = harness.createSensorContext(); - final Collection globalMutants = Collections.emptyList(); - final Collection metrics = Arrays.asList(context.newResourceMutationMetrics("Test1.java", md -> { - md.lines = 200; - md.mutants.unknown = 0; - md.mutants.noCoverage = 1; - md.mutants.survived = 2; - md.mutants.memoryError = 3; - md.mutants.timedOut = 4; - md.mutants.killed = 5; - md.mutants.numTestRun = 2; - }), context.newResourceMutationMetrics("Test2.java", md -> { - md.lines = 100; - md.mutants.unknown = 1; - md.mutants.noCoverage = 2; - md.mutants.survived = 2; - md.mutants.memoryError = 0; - md.mutants.timedOut = 1; - md.mutants.killed = 3; - md.mutants.numTestRun = 3; - })); + final Collection metrics = Arrays.asList( + context.newResourceMutationMetrics("Test1.java", md -> { + md.lines = 200; + md.mutants.unknown = 0; + md.mutants.noCoverage = 1; + md.mutants.survived = 2; + md.mutants.memoryError = 3; + md.mutants.timedOut = 4; + md.mutants.killed = 5; + md.mutants.numTestRun = 2; + }), context.newResourceMutationMetrics("Test2.java", md -> { + md.lines = 100; + md.mutants.unknown = 1; + md.mutants.noCoverage = 2; + md.mutants.survived = 2; + md.mutants.memoryError = 0; + md.mutants.timedOut = 1; + md.mutants.killed = 3; + md.mutants.numTestRun = 3; + })); smw.writeMetrics(metrics, context, globalMutants); @@ -226,6 +220,7 @@ public void writeMetrics_multiResourceMetrics_metricsWritten() throws Exception assertEquals(12, values1.get(MutationMetrics.MUTATIONS_DETECTED.key())); assertEquals(24, values1.get(MutationMetrics.UTILITY_GLOBAL_MUTATIONS.key())); assertEquals(8, values1.get(MutationMetrics.UTILITY_GLOBAL_ALIVE.key())); + final Map values2 = getMeasuresByKey("test-module:Test2.java", context); assertEquals(EXPECTED_QUANTITATIVE_METRICS, values2.size()); assertEquals(9, values2.get(MutationMetrics.MUTATIONS_TOTAL.key())); @@ -249,33 +244,29 @@ public void writeMetrics_multiResourceMetrics_metricsWritten() throws Exception } private Map getMeasuresByKey(String expectedComponentKey, final TestSensorContext context) { - return context.getStorage() - .getMeasures() - .stream() - .filter(m -> expectedComponentKey.equals(m.inputComponent().key())) - .collect(Collectors.toMap(m -> m.metric().key(), Measure::value)); + .getMeasures() + .stream() + .filter(m -> expectedComponentKey.equals(m.inputComponent().key())) + .collect(Collectors.toMap(m -> m.metric().key(), Measure::value)); } private DefaultCoverage getCoveragesByKey(final String expectedInputFile, final TestSensorContext context) { - return getOptionalCoveragesByKey(expectedInputFile, context) - .orElseThrow(() -> new AssertionError("no input file found with filename=" + expectedInputFile)); + .orElseThrow(() -> new AssertionError("no input file found with filename=" + expectedInputFile)); } private Optional getOptionalCoveragesByKey(final String expectedInputFile, final TestSensorContext context) { - return context.getStorage() - .getCoverages() - .stream() - .filter(c -> expectedInputFile.equals(c.inputFile().filename())) - .findFirst(); + .getCoverages() + .stream() + .filter(c -> expectedInputFile.equals(c.inputFile().filename())) + .findFirst(); } + @NotNull private List generateMutantMetrics(final TestSensorContext context) { - - return Arrays.asList( - context.newResourceMutationMetrics("Test.java", md -> { + return Collections.singletonList(context.newResourceMutationMetrics("Test.java", md -> { md.lines = 100; md.mutants.unknown = 0; md.mutants.noCoverage = 1; @@ -288,21 +279,20 @@ private List generateMutantMetrics(final TestSensorCont @NotNull private List generateMutants(final int count) { - return IntStream.range(0, count).mapToObj(this::newMutant).collect(Collectors.toList()); } private Mutant newMutant(int i) { - return Mutant.builder() - .mutantStatus(Mutant.State.KILLED) - .inSourceFile("Example.java") - .inClass("Example") - .inMethod("aMethod") - .withMethodParameters("()") - .usingMutator("BOOLEAN_FALSE_RETURN") - .killedBy("Test") - .inLine(i) - .build(); + .mutantStatus(Mutant.State.KILLED) + .inSourceFile("Example.java") + .inClass("Example") + .inMethod("aMethod") + .withMethodParameters("()") + .usingMutator("BOOLEAN_FALSE_RETURN") + .killedBy("Test") + .inLine(i) + .build(); } + } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/TestMetricsWriterTest.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/TestMetricsWriterTest.java index 738bf22..dfc64aa 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/TestMetricsWriterTest.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/TestMetricsWriterTest.java @@ -20,10 +20,16 @@ package ch.devcon5.sonar.plugins.mutationanalysis.sensors; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics; +import ch.devcon5.sonar.plugins.mutationanalysis.metrics.ResourceMutationMetrics; +import ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant; +import ch.devcon5.sonar.plugins.mutationanalysis.testharness.SensorTestHarness; +import ch.devcon5.sonar.plugins.mutationanalysis.testharness.TestSensorContext; import java.io.Serializable; +import java.nio.file.Path; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -31,37 +37,29 @@ import java.util.Map; import java.util.stream.Collectors; import java.util.stream.IntStream; - -import ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics; -import ch.devcon5.sonar.plugins.mutationanalysis.metrics.ResourceMutationMetrics; -import ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant; -import ch.devcon5.sonar.plugins.mutationanalysis.testharness.SensorTestHarness; -import ch.devcon5.sonar.plugins.mutationanalysis.testharness.TestSensorContext; import org.jetbrains.annotations.NotNull; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import org.sonar.api.batch.sensor.measure.Measure; /** - * + * Test Metrics Writer Tests */ -public class TestMetricsWriterTest { +class TestMetricsWriterTest { - @Rule - public TemporaryFolder folder = new TemporaryFolder(); + @TempDir + public Path folder; private SensorTestHarness harness; - @Before + @BeforeEach public void setUp() throws Exception { this.harness = SensorTestHarness.builder().withTempFolder(folder).build(); } @Test - public void writeMetrics_noMutants_noMetrics_noMeasure() { - + void writeMetrics_noMutants_noMetrics_noMeasure() { //arrange final Collection metrics = Collections.emptyList(); final TestSensorContext context = harness.createSensorContext(); @@ -76,8 +74,7 @@ public void writeMetrics_noMutants_noMetrics_noMeasure() { } @Test - public void writeMetrics_oneMutant_withKillingTest_measureCreated() throws Exception { - + void writeMetrics_oneMutant_withKillingTest_measureCreated() throws Exception { //arrange final TestSensorContext context = harness.createSensorContext(); context.addTestFile("CustomTest.java"); @@ -96,8 +93,7 @@ public void writeMetrics_oneMutant_withKillingTest_measureCreated() throws Excep } @Test - public void writeMetrics_oneMutant_withGlobalMutants_measureCreated() throws Exception { - + void writeMetrics_oneMutant_withGlobalMutants_measureCreated() throws Exception { //arrange final TestSensorContext context = harness.createSensorContext(); context.addTestFile("CustomTest.java"); @@ -117,8 +113,7 @@ public void writeMetrics_oneMutant_withGlobalMutants_measureCreated() throws Exc } @Test - public void writeMetrics_oneMutant_killingTestEmpty_noMeasureCreated() throws Exception { - + void writeMetrics_oneMutant_killingTestEmpty_noMeasureCreated() throws Exception { //arrange final TestSensorContext context = harness.createSensorContext(); context.addTestFile("CustomTest.java"); @@ -136,8 +131,7 @@ public void writeMetrics_oneMutant_killingTestEmpty_noMeasureCreated() throws Ex @NotNull private List generateMutantMetrics(final TestSensorContext context, String testname) { - - return Arrays.asList(context.newResourceMutationMetrics("Product.java", md -> { + return Collections.singletonList(context.newResourceMutationMetrics("Product.java", md -> { md.lines = 100; md.mutants.survived = 2; md.mutants.killed = 5; @@ -146,24 +140,24 @@ private List generateMutantMetrics(final TestSensorCont } @Test - public void writeMetrics_moreMutants_measureCreated() throws Exception { - + void writeMetrics_moreMutants_measureCreated() throws Exception { //arrange final TestSensorContext context = harness.createSensorContext(); context.addTestFile("CustomTest.java"); context.addTestFile("OtherTest.java"); final Collection globalMutants = Collections.emptyList(); - final Collection metrics = Arrays.asList(context.newResourceMutationMetrics("Product.java", md -> { - md.lines = 100; - md.mutants.survived = 2; - md.mutants.killed = 5; - md.test.name = "CustomTest"; - }), context.newResourceMutationMetrics("Other.java", md -> { - md.lines = 50; - md.mutants.survived = 3; - md.mutants.killed = 2; - md.test.name = "OtherTest"; - })); + final Collection metrics = Arrays.asList( + context.newResourceMutationMetrics("Product.java", md -> { + md.lines = 100; + md.mutants.survived = 2; + md.mutants.killed = 5; + md.test.name = "CustomTest"; + }), context.newResourceMutationMetrics("Other.java", md -> { + md.lines = 50; + md.mutants.survived = 3; + md.mutants.killed = 2; + md.test.name = "OtherTest"; + })); //act final TestMetricsWriter smw = new TestMetricsWriter(context.fileSystem()); @@ -180,31 +174,30 @@ public void writeMetrics_moreMutants_measureCreated() throws Exception { assertEquals(2, measures2.get(MutationMetrics.TEST_KILLS_KEY)); assertEquals(12, measures2.get(MutationMetrics.UTILITY_GLOBAL_MUTATIONS_KEY)); } - private Map getMeasuresByKey(String expectedComponentKey, final TestSensorContext context) { + private Map getMeasuresByKey(String expectedComponentKey, final TestSensorContext context) { return context.getStorage() - .getMeasures() - .stream() - .filter(m -> expectedComponentKey.equals(m.inputComponent().key())) - .collect(Collectors.toMap(m -> m.metric().key(), Measure::value)); + .getMeasures() + .stream() + .filter(m -> expectedComponentKey.equals(m.inputComponent().key())) + .collect(Collectors.toMap(m -> m.metric().key(), Measure::value)); } - private List generateMutants(final int count) { + private List generateMutants(final int count) { return IntStream.range(0, count).mapToObj(this::newMutant).collect(Collectors.toList()); } private Mutant newMutant(int i) { - return Mutant.builder() - .mutantStatus(Mutant.State.KILLED) - .inSourceFile("Example.java") - .inClass("Example") - .inMethod("aMethod") - .withMethodParameters("()") - .usingMutator("BOOLEAN_FALSE_RETURN") - .killedBy("Test") - .inLine(i) - .build(); + .mutantStatus(Mutant.State.KILLED) + .inSourceFile("Example.java") + .inClass("Example") + .inMethod("aMethod") + .withMethodParameters("()") + .usingMutator("BOOLEAN_FALSE_RETURN") + .killedBy("Test") + .inLine(i) + .build(); } } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/LogRecordingAppender.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/LogRecordingAppender.java index e2b36f3..31e2e06 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/LogRecordingAppender.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/LogRecordingAppender.java @@ -22,7 +22,6 @@ import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; - import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.LogEvent; @@ -32,31 +31,32 @@ public class LogRecordingAppender extends AbstractAppender implements AutoCloseable { - private List events = new CopyOnWriteArrayList<>(); - - public LogRecordingAppender() { - super("listAppender", null, null); - getLoggerConfig().addAppender(this, Level.ALL, null); - this.start(); - } - - @Override - public void append(final LogEvent logEvent) { - events.add(logEvent.toImmutable()); - } - - public List getEvents() { - return events; - } - - private LoggerConfig getLoggerConfig() { - final LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false); - return loggerContext.getConfiguration().getLoggerConfig(""); - } - - @Override - public void close() throws Exception { - getLoggerConfig().removeAppender(getName()); - this.stop(); - } + private final List events = new CopyOnWriteArrayList<>(); + + public LogRecordingAppender() { + super("listAppender", null, null); + getLoggerConfig().addAppender(this, Level.ALL, null); + this.start(); + } + + @Override + public void append(final LogEvent logEvent) { + events.add(logEvent.toImmutable()); + } + + public List getEvents() { + return events; + } + + private LoggerConfig getLoggerConfig() { + final LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false); + return loggerContext.getConfiguration().getLoggerConfig(""); + } + + @Override + public void close() throws Exception { + getLoggerConfig().removeAppender(getName()); + this.stop(); + } + } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/MeasureComputerTestHarness.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/MeasureComputerTestHarness.java index e8fc28f..424eb69 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/MeasureComputerTestHarness.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/MeasureComputerTestHarness.java @@ -21,10 +21,9 @@ package ch.devcon5.sonar.plugins.mutationanalysis.testharness; +import ch.devcon5.sonar.plugins.mutationanalysis.MutationAnalysisPlugin; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; - -import ch.devcon5.sonar.plugins.mutationanalysis.MutationAnalysisPlugin; import org.sonar.api.ce.measure.Component; import org.sonar.api.ce.measure.MeasureComputer; import org.sonar.api.ce.measure.test.TestComponent; @@ -42,19 +41,16 @@ public class MeasureComputerTestHarness { private final TestConfiguration config; public MeasureComputerTestHarness(final T computer) { - this(computer, new TestConfiguration()); } public MeasureComputerTestHarness(final T computer, final TestConfiguration configuration) { - this.computer = computer; this.config = configuration; enableExperimentalFeatures(true); } public static MeasureComputerTestHarness createFor(Class computer) { - try { final Constructor c = computer.getConstructor(Configuration.class); final TestConfiguration configuration = new TestConfiguration(); @@ -71,7 +67,6 @@ public static MeasureComputerTestHarness createFo } public T getComputer() { - return computer; } @@ -80,12 +75,10 @@ public void enableExperimentalFeatures(final boolean enabled) { } public TestConfiguration getConfig() { - return config; } public TestMeasureComputerContext createMeasureContextForSourceFile(String componentKey) { - return createMeasureContext(componentKey, Component.Type.FILE, "java", false); } @@ -93,39 +86,37 @@ public TestMeasureComputerContext createMeasureContextForUnitTest(String compone return createMeasureContext(componentKey, Component.Type.FILE, "java", true); } - public TestMeasureComputerContext createMeasureContextForDirectory(String componentKey) { + public TestMeasureComputerContext createMeasureContextForDirectory(String componentKey) { return createMeasureContext(componentKey, Component.Type.DIRECTORY, "java", false); } public TestMeasureComputerContext createMeasureContextForModule(String componentKey) { - return createMeasureContext(componentKey, Component.Type.MODULE, "java", false); } - public TestMeasureComputerContext createMeasureContextForProject(String componentKey) { + public TestMeasureComputerContext createMeasureContextForProject(String componentKey) { return createMeasureContext(componentKey, Component.Type.PROJECT, "java", false); } public TestMeasureComputerContext createMeasureContext(String componentKey, Component.Type type) { - return createMeasureContext(componentKey, type, "java", false); } public TestMeasureComputerContext createMeasureContext(String componentKey, Component.Type type, String language, boolean unitTest) { - final TestMeasureComputerDefinitionContext context = new TestMeasureComputerDefinitionContext(); final MeasureComputer.MeasureComputerDefinition def = computer.define(context); final TestSettings settings = new TestSettings(); - if(type == Component.Type.FILE){ - return new TestMeasureComputerContext(new TestComponent(componentKey, type, new TestComponent.FileAttributesImpl(language, unitTest)), settings, def); + if (type == Component.Type.FILE) { + return new TestMeasureComputerContext( + new TestComponent(componentKey, type, new TestComponent.FileAttributesImpl(language, unitTest)), settings, def); } return new TestMeasureComputerContext(new TestComponent(componentKey, type, null), settings, def); } public TestMeasureComputerContext createMeasureContext(String componentKey, Component.Type type, boolean unitTest) { - return createMeasureContext(componentKey, type, "java", unitTest); } + } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/SensorTestHarness.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/SensorTestHarness.java index 4277758..b9db681 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/SensorTestHarness.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/SensorTestHarness.java @@ -23,6 +23,7 @@ import static ch.devcon5.sonar.plugins.mutationanalysis.rules.MutationAnalysisRulesDefinition.REPOSITORY_KEY; import static org.sonar.api.rules.RulePriority.MAJOR; +import ch.devcon5.sonar.plugins.mutationanalysis.rules.MutationAnalysisRulesDefinition; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -32,10 +33,7 @@ import java.util.Collections; import java.util.Optional; import java.util.stream.Collectors; - -import ch.devcon5.sonar.plugins.mutationanalysis.rules.MutationAnalysisRulesDefinition; import org.jetbrains.annotations.NotNull; -import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.rule.ActiveRules; import org.sonar.api.batch.rule.internal.ActiveRulesBuilder; import org.sonar.api.batch.rule.internal.DefaultActiveRules; @@ -47,228 +45,226 @@ public class SensorTestHarness { - private final String language; - private final Optional folder; - private final Path basePath; - private final String moduleName; - private final Class resourceResolver; - - public SensorTestHarness(final Builder builder) { - this.language = builder.language; - this.basePath = builder.basePath; - this.moduleName = builder.moduleName; - this.resourceResolver = builder.resourceResolver; - this.folder = Optional.ofNullable(builder.tempFolder); - - } - - public SensorTestHarness resolveResourcesFrom(Class caller) { - return builder().from(this).withResourceResolver(caller).build(); - } - - public SensorTestHarness changeBasePath(Path newPath) { - return builder().from(this).withBasePath(newPath).build(); - } - - public SensorTestHarness changeLanguage(String newLanguage) { - return builder().from(this).withLanguage(newLanguage).build(); - } - - public SensorTestHarness changeModuleName(String newModuleName) { - return builder().from(this).withModuleName(newModuleName).build(); - } - - public Path createSourceFile(String path, String filename) throws IOException { - return this.folder.map(f -> { - try { - final File file = TestUtils.createTempFile(f, path, filename); - file.createNewFile(); - return file.toPath(); - } catch (IOException e) { - throw new RuntimeException(e); - } - }).orElseGet(() -> { - try { - return Files.createFile(this.basePath.resolve(path).resolve(filename)); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - } - - public Path resourceToFile(final String filePath, final String resource) throws IOException { - return this.folder.map(f -> { - try { - return TestUtils.tempFileFromResource(f, filePath, resourceResolver, resource).toPath(); - } catch (IOException e) { - throw new RuntimeException(e); - } - }).orElseGet(() -> { - try (InputStream is = resourceResolver.getResourceAsStream(resource)) { - if (is != null) { - Path destination = basePath.resolve(filePath); - Files.copy(is, destination); - return destination; - } else { - throw new RuntimeException("Resource " + resource + " not found using " + resourceResolver); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - - } - - public TestConfiguration createConfiguration() { - return new TestConfiguration(); - } - - public TestSensorContext createSensorContext() { - - return new TestSensorContext(basePath, moduleName); - } - - public ActiveRules createEmptyActiveRules() { - return new DefaultActiveRules(Collections.emptyList()); - } - - public RulesProfile createEmptyRulesProfile() { - return RulesProfile.create("test.profile", language); - } - - /** - * @param ruleKeys - * @return - * @deprecated use createActiveRules - */ - @Deprecated - public RulesProfile createRulesProfile(String... ruleKeys) { - return createRulesProfile(Arrays.stream(ruleKeys) - .map(this::createRule) - .toArray(Rule[]::new)); - } - public ActiveRules createActiveRules(String... ruleKeys) { - - final ActiveRulesBuilder builder = new ActiveRulesBuilder(); - - for(String ruleKey : ruleKeys){ - - builder.addRule(newActiveRule().setRuleKey(RuleKey.of(MutationAnalysisRulesDefinition.REPOSITORY_KEY + ".kotlin", ruleKey)).build()); - builder.addRule(newActiveRule().setRuleKey(RuleKey.of(MutationAnalysisRulesDefinition.REPOSITORY_KEY + ".java", ruleKey)).build()); - } - return builder.build(); - } - - @NotNull - private NewActiveRule.Builder newActiveRule() { - return new NewActiveRule.Builder(); - } - - public ActiveRules createActiveRules(Rule... rules) { - - final ActiveRulesBuilder builder = new ActiveRulesBuilder(); - for(Rule rule : rules){ - final NewActiveRule.Builder activeRule = newActiveRule().setRuleKey(RuleKey.of(rule.getRepositoryKey(), rule.getKey())); - rule.getParams().forEach(param -> activeRule.setParam(param.getKey(), param.getDefaultValue())); - builder.addRule(activeRule.build()); - } - return builder.build(); - } - - /** - * @deprecated use createActiveRules - * @param rules - * @return - */ - @Deprecated - public RulesProfile createRulesProfile(Rule... rules) { - - final RulesProfile profile = createEmptyRulesProfile(); - profile.setActiveRules(Arrays.stream(rules).map(r -> { - final ActiveRule ar = new ActiveRule(profile, r, MAJOR); - r.getParams().forEach(p -> ar.setParameter(p.getKey(), p.getDefaultValue())); - return ar; - }).collect(Collectors.toList())); - return profile; - } - - public Rule createRule(String language, final String ruleKey) { - return Rule.create(REPOSITORY_KEY + "." + language, ruleKey).setLanguage(language); - } - public Rule createRule(final String ruleKey) { - return createRule(this.language, ruleKey); - } - public Rule createRule(String ruleKey, String key, String value) { - - final Rule r = Rule.create(REPOSITORY_KEY + "." + language, ruleKey); - r.createParameter(key).setDefaultValue(value); - return r; - } - - public static Builder builder() { - return new Builder(); - } - - /** - * Builder for fluently create new immutable sensor harnesses - */ - public static class Builder { - - private String language = "java"; - private TemporaryFolder tempFolder; - private Path basePath; - private String moduleName = "test-module"; - private Class resourceResolver = SensorTestHarness.class; - - public Builder withLanguage(String language) { - this.language = language; - return this; - } - - public Builder withBasePath(Path basePath) { - this.basePath = basePath; - return this; + private final String language; + private final Optional folder; + private final Path basePath; + private final String moduleName; + private final Class resourceResolver; + + public SensorTestHarness(final Builder builder) { + this.language = builder.language; + this.basePath = builder.basePath; + this.moduleName = builder.moduleName; + this.resourceResolver = builder.resourceResolver; + this.folder = Optional.ofNullable(builder.tempFolder); + } + + public SensorTestHarness resolveResourcesFrom(Class caller) { + return builder().from(this).withResourceResolver(caller).build(); + } + + public SensorTestHarness changeBasePath(Path newPath) { + return builder().from(this).withBasePath(newPath).build(); + } + + public SensorTestHarness changeLanguage(String newLanguage) { + return builder().from(this).withLanguage(newLanguage).build(); + } + + public SensorTestHarness changeModuleName(String newModuleName) { + return builder().from(this).withModuleName(newModuleName).build(); + } + + public Path createSourceFile(String path, String filename) { + return this.folder.map(f -> { + try { + final File file = TestUtils.createTempFile(f, path, filename); + return file.toPath(); + } catch (IOException e) { + throw new RuntimeException(e); } - - public Builder withModuleName(String moduleName) { - this.moduleName = moduleName; - return this; + }).orElseGet(() -> { + try { + return Files.createFile(this.basePath.resolve(path).resolve(filename)); + } catch (IOException e) { + throw new RuntimeException(e); } - - public Builder withResourceResolver(Class resourceResolver) { - this.resourceResolver = resourceResolver; - return this; + }); + } + + public Path resourceToFile(final String filePath, final String resource) { + return this.folder.map(f -> { + try { + return TestUtils.tempFileFromResource(f, filePath, resourceResolver, resource).toPath(); + } catch (IOException e) { + throw new RuntimeException(e); } - - public Builder withTempFolder(TemporaryFolder tempFolder) { - this.tempFolder = tempFolder; - this.basePath = tempFolder.getRoot().toPath(); - return this; + }).orElseGet(() -> { + try (InputStream is = resourceResolver.getResourceAsStream(resource)) { + if (is != null) { + Path destination = basePath.resolve(filePath); + Files.copy(is, destination); + return destination; + } else { + throw new RuntimeException("Resource " + resource + " not found using " + resourceResolver); + } + } catch (IOException e) { + throw new RuntimeException(e); } - - public Builder from(SensorTestHarness template) { - this.tempFolder = template.folder.orElse(null); - this.basePath = template.basePath; - this.moduleName = template.moduleName; - this.resourceResolver = template.resourceResolver; - this.language = template.language; - return this; + }); + } + + public TestConfiguration createConfiguration() { + return new TestConfiguration(); + } + + public TestSensorContext createSensorContext() { + return new TestSensorContext(basePath, moduleName); + } + + public ActiveRules createEmptyActiveRules() { + return new DefaultActiveRules(Collections.emptyList()); + } + + public RulesProfile createEmptyRulesProfile() { + return RulesProfile.create("test.profile", language); + } + + /** + * @param ruleKeys The keys for the new rules profile + * @return The new rules profile + * @deprecated use createActiveRules + */ + @Deprecated + public RulesProfile createRulesProfile(String... ruleKeys) { + return createRulesProfile(Arrays.stream(ruleKeys) + .map(this::createRule) + .toArray(Rule[]::new)); + } + + public ActiveRules createActiveRules(String... ruleKeys) { + final ActiveRulesBuilder builder = new ActiveRulesBuilder(); + for (String ruleKey : ruleKeys) { + builder.addRule( + newActiveRule().setRuleKey(RuleKey.of(MutationAnalysisRulesDefinition.REPOSITORY_KEY + ".kotlin", ruleKey)) + .build()); + builder.addRule( + newActiveRule().setRuleKey(RuleKey.of(MutationAnalysisRulesDefinition.REPOSITORY_KEY + ".java", ruleKey)) + .build()); + } + return builder.build(); + } + + @NotNull + private NewActiveRule.Builder newActiveRule() { + return new NewActiveRule.Builder(); + } + + public ActiveRules createActiveRules(Rule... rules) { + final ActiveRulesBuilder builder = new ActiveRulesBuilder(); + for (Rule rule : rules) { + final NewActiveRule.Builder activeRule = newActiveRule().setRuleKey( + RuleKey.of(rule.getRepositoryKey(), rule.getKey())); + rule.getParams().forEach(param -> activeRule.setParam(param.getKey(), param.getDefaultValue())); + builder.addRule(activeRule.build()); + } + return builder.build(); + } + + /** + * @param rules + * @return + * @deprecated use createActiveRules + */ + @Deprecated + public RulesProfile createRulesProfile(Rule... rules) { + final RulesProfile profile = createEmptyRulesProfile(); + profile.setActiveRules(Arrays.stream(rules).map(r -> { + final ActiveRule ar = new ActiveRule(profile, r, MAJOR); + r.getParams().forEach(p -> ar.setParameter(p.getKey(), p.getDefaultValue())); + return ar; + }).collect(Collectors.toList())); + return profile; + } + + public Rule createRule(String language, final String ruleKey) { + return Rule.create(REPOSITORY_KEY + "." + language, ruleKey).setLanguage(language); + } + + public Rule createRule(final String ruleKey) { + return createRule(this.language, ruleKey); + } + + public Rule createRule(String ruleKey, String key, String value) { + final Rule r = Rule.create(REPOSITORY_KEY + "." + language, ruleKey); + r.createParameter(key).setDefaultValue(value); + return r; + } + + public static Builder builder() { + return new Builder(); + } + + /** + * Builder for fluently create new immutable sensor harnesses + */ + public static class Builder { + + private String language = "java"; + private Path tempFolder; + private Path basePath; + private String moduleName = "test-module"; + private Class resourceResolver = SensorTestHarness.class; + + public Builder withLanguage(String language) { + this.language = language; + return this; + } + + public Builder withBasePath(Path basePath) { + this.basePath = basePath; + return this; + } + + public Builder withModuleName(String moduleName) { + this.moduleName = moduleName; + return this; + } + + public Builder withResourceResolver(Class resourceResolver) { + this.resourceResolver = resourceResolver; + return this; + } + + public Builder withTempFolder(Path tempFolder) { + this.tempFolder = tempFolder; + this.basePath = tempFolder; + return this; + } + + public Builder from(SensorTestHarness template) { + this.tempFolder = template.folder.orElse(null); + this.basePath = template.basePath; + this.moduleName = template.moduleName; + this.resourceResolver = template.resourceResolver; + this.language = template.language; + return this; + } + + public SensorTestHarness build() { + requireNonNull(language, "Language"); + requireNonNull(basePath, "Base path"); + requireNonNull(moduleName, "Module Name"); + requireNonNull(resourceResolver, "Resource resolver class"); + return new SensorTestHarness(this); + } + + private static void requireNonNull(final Object arg, final String role) { + if (arg == null) { + throw new IllegalArgumentException(role + " must not be null"); } + } - public SensorTestHarness build() { - requireNonNull(language, "Language"); - requireNonNull(basePath, "Base path"); - requireNonNull(moduleName, "Module Name"); - requireNonNull(resourceResolver, "Resource resolver class"); - - return new SensorTestHarness(this); - } - - private static void requireNonNull(final Object arg, final String role) { - if (arg == null) { - throw new IllegalArgumentException(role + " must not be null"); - } - } - } + } } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/SystemLocale.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/SystemLocale.java deleted file mode 100644 index 7d23daa..0000000 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/SystemLocale.java +++ /dev/null @@ -1,38 +0,0 @@ -package ch.devcon5.sonar.plugins.mutationanalysis.testharness; - -import org.junit.rules.TestRule; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; - -import java.util.Locale; - -public class SystemLocale implements TestRule { - - - private final Locale overrideLocale; - private Locale defaultLocale = Locale.getDefault(); - - public SystemLocale(Locale locale) { - this.overrideLocale = locale; - } - - public static SystemLocale overrideDefault(Locale locale){ - return new SystemLocale(locale); - } - - @Override - public Statement apply(Statement statement, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - try { - defaultLocale = Locale.getDefault(); - Locale.setDefault(overrideLocale); - statement.evaluate(); - } finally { - Locale.setDefault(defaultLocale); - } - } - }; - } -} diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/SystemLocaleExtension.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/SystemLocaleExtension.java new file mode 100644 index 0000000..0702543 --- /dev/null +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/SystemLocaleExtension.java @@ -0,0 +1,32 @@ +package ch.devcon5.sonar.plugins.mutationanalysis.testharness; + +import java.util.Locale; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +public class SystemLocaleExtension implements AfterEachCallback, BeforeEachCallback { + + private final Locale overrideLocale; + private Locale defaultLocale = Locale.getDefault(); + + public SystemLocaleExtension(Locale locale) { + this.overrideLocale = locale; + } + + public static SystemLocaleExtension overrideDefault(Locale locale) { + return new SystemLocaleExtension(locale); + } + + @Override + public void beforeEach(ExtensionContext context) throws Exception { + defaultLocale = Locale.getDefault(); + Locale.setDefault(overrideLocale); + } + + @Override + public void afterEach(ExtensionContext context) throws Exception { + Locale.setDefault(defaultLocale); + } + +} diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/TestConfiguration.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/TestConfiguration.java index 4e4b80c..b805b69 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/TestConfiguration.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/TestConfiguration.java @@ -23,7 +23,6 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; - import org.sonar.api.config.Configuration; /** @@ -31,28 +30,28 @@ */ public class TestConfiguration implements Configuration { - private final Map settings = new ConcurrentHashMap<>(); + private final Map settings = new ConcurrentHashMap<>(); + + public TestConfiguration() {} - public TestConfiguration() { - } + public TestConfiguration set(String key, Object value) { + this.settings.put(key, String.valueOf(value)); + return this; + } - public TestConfiguration set(String key, Object value){ - this.settings.put(key, String.valueOf(value)); - return this; - } + @Override + public Optional get(final String key) { + return Optional.ofNullable(settings.get(key)); + } - @Override - public Optional get(final String key) { - return Optional.ofNullable(settings.get(key)); - } + @Override + public boolean hasKey(final String key) { + return settings.containsKey(key); + } - @Override - public boolean hasKey(final String key) { - return settings.containsKey(key); - } + @Override + public String[] getStringArray(final String key) { + return get(key).map(v -> v.split(",")).orElse(new String[0]); + } - @Override - public String[] getStringArray(final String key) { - return get(key).map(v -> v.split(",")).orElse(new String[0]); - } } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/TestSensorContext.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/TestSensorContext.java index f7a1bc2..2a2e31d 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/TestSensorContext.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/TestSensorContext.java @@ -22,6 +22,10 @@ import static org.slf4j.LoggerFactory.getLogger; +import ch.devcon5.sonar.plugins.mutationanalysis.metrics.ResourceMutationMetrics; +import ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant; +import ch.devcon5.sonar.plugins.mutationanalysis.model.MutationOperator; +import ch.devcon5.sonar.plugins.mutationanalysis.model.MutationOperators; import java.io.IOException; import java.io.Serializable; import java.nio.file.FileVisitResult; @@ -38,11 +42,6 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.stream.IntStream; - -import ch.devcon5.sonar.plugins.mutationanalysis.metrics.ResourceMutationMetrics; -import ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant; -import ch.devcon5.sonar.plugins.mutationanalysis.model.MutationOperator; -import ch.devcon5.sonar.plugins.mutationanalysis.model.MutationOperators; import org.slf4j.Logger; import org.sonar.api.SonarEdition; import org.sonar.api.SonarQubeSide; @@ -51,7 +50,14 @@ import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputModule; -import org.sonar.api.batch.fs.internal.*; +import org.sonar.api.batch.fs.internal.DefaultFileSystem; +import org.sonar.api.batch.fs.internal.DefaultIndexedFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DefaultInputModule; +import org.sonar.api.batch.fs.internal.DefaultInputProject; +import org.sonar.api.batch.fs.internal.Metadata; +import org.sonar.api.batch.fs.internal.SensorStrategy; +import org.sonar.api.batch.fs.internal.TestInputFileBuilder; import org.sonar.api.batch.rule.ActiveRules; import org.sonar.api.batch.rule.internal.DefaultActiveRules; import org.sonar.api.batch.rule.internal.NewActiveRule; @@ -94,57 +100,56 @@ public class TestSensorContext implements SensorContext { private static final Logger LOGGER = getLogger(TestSensorContext.class); - private static final List MUTATION_OPERATORS = Collections.unmodifiableList(new ArrayList<>(MutationOperators.allMutationOperators())); + private static final List MUTATION_OPERATORS = Collections.unmodifiableList( + new ArrayList<>(MutationOperators.allMutationOperators())); private final DefaultFileSystem fs; - private DefaultInputProject inputProject; + private final DefaultInputProject inputProject; private final InputModule inputModule; - private MapSettings settings = new MapSettings(); + private final MapSettings settings = new MapSettings(); private Configuration configuration = new TestConfiguration(); - private SonarRuntime runtime = SonarRuntimeImpl.forSonarQube(Version.create(7, 3), SonarQubeSide.SCANNER, SonarEdition.COMMUNITY); - private Version version = Version.create(6, 5); - private TestSensorStorage storage = new TestSensorStorage(); + private final SonarRuntime runtime = SonarRuntimeImpl.forSonarQube( + Version.create(8, 9), SonarQubeSide.SCANNER, SonarEdition.COMMUNITY); + private final Version version = Version.create(6, 5); + private final TestSensorStorage storage = new TestSensorStorage(); private ActiveRules activeRules = new DefaultActiveRules(Collections.emptyList()); TestSensorContext(final Path basePath, String moduleName) { - this.fs = new DefaultFileSystem(basePath).setWorkDir(basePath); - final ProjectDefinition pd = ProjectDefinition.create().setName(moduleName).setKey(moduleName).setBaseDir(basePath.toFile()).setWorkDir(basePath.toFile()); + final ProjectDefinition pd = ProjectDefinition.create() + .setName(moduleName) + .setKey(moduleName) + .setBaseDir(basePath.toFile()) + .setWorkDir(basePath.toFile()); this.inputProject = new DefaultInputProject(pd); this.inputModule = new DefaultInputModule(pd); } public static MutationOperator getMutationOperatorForLine(final int line) { - return MUTATION_OPERATORS.get(line % MUTATION_OPERATORS.size()); } @Override public Settings settings() { - return settings; } @Override public Configuration config() { - return configuration; } @Override public FileSystem fileSystem() { - return fs; } @Override public ActiveRules activeRules() { - return activeRules; } @Override public InputModule module() { - return inputModule; } @@ -155,31 +160,26 @@ public InputProject project() { @Override public Version getSonarQubeVersion() { - return version; } @Override public SonarRuntime runtime() { - return runtime; } @Override public boolean isCancelled() { - return false; } @Override public NewMeasure newMeasure() { - return new DefaultMeasure<>(this.storage); } @Override public NewIssue newIssue() { - return new DefaultIssue(this.inputProject, this.storage); } @@ -201,25 +201,21 @@ public NewHighlighting newHighlighting() { @Override public NewSymbolTable newSymbolTable() { - return new DefaultSymbolTable(this.storage); } @Override public NewCoverage newCoverage() { - return new DefaultCoverage(this.storage); } @Override public NewCpdTokens newCpdTokens() { - return new DefaultCpdTokens(this.storage); } @Override public NewAnalysisError newAnalysisError() { - return new DefaultAnalysisError(); } @@ -230,13 +226,11 @@ public NewSignificantCode newSignificantCode() { @Override public void addContextProperty(final String key, final String value) { - this.storage.storeProperty(key, value); } @Override public void markForPublishing(final InputFile inputFile) { - } /** @@ -245,65 +239,53 @@ public void markForPublishing(final InputFile inputFile) { * @return */ public TestSensorContext useBridgedConfiguration() { - this.configuration = settings.asConfig(); return this; } public MapSettings getSettings() { - return settings; } public Configuration getConfiguration() { - return configuration; } public SonarRuntime getRuntime() { - return runtime; } public TestSensorStorage getStorage() { - return storage; } public TestSensorContext withActiveRules(NewActiveRule... rules) { - this.activeRules = new DefaultActiveRules(Arrays.asList(rules)); return this; } public InputFile addTestFile(String filename) throws IOException { - return addTestFile(filename, md -> { }); } public InputFile addTestFile(String filename, Consumer mdGenerator) throws IOException { - final TestFileMetadata metadata = new TestFileMetadata(); mdGenerator.accept(metadata); - return addTestFile(filename, metadata); } public ResourceMutationMetrics newResourceMutationMetrics(String filename, Consumer mdGenerator) { - final TestFileMetadata metadata = new TestFileMetadata(); mdGenerator.accept(metadata); try { ResourceMutationMetrics rmm = new ResourceMutationMetrics(addTestFile(filename, metadata)); - addMutants(rmm, Mutant.State.UNKNOWN, metadata.mutants.unknown, metadata); addMutants(rmm, Mutant.State.NO_COVERAGE, metadata.mutants.noCoverage, metadata); addMutants(rmm, Mutant.State.SURVIVED, metadata.mutants.survived, metadata); addMutants(rmm, Mutant.State.TIMED_OUT, metadata.mutants.timedOut, metadata); addMutants(rmm, Mutant.State.MEMORY_ERROR, metadata.mutants.memoryError, metadata); addMutants(rmm, Mutant.State.KILLED, metadata.mutants.killed, metadata); - return rmm; } catch (IOException e) { throw new RuntimeException("Failed to create test file", e); @@ -313,39 +295,31 @@ public ResourceMutationMetrics newResourceMutationMetrics(String filename, Consu /** * Method for conveniently setting a test configuration * - * @param key - * the key of the configuration by which it can be retrieved as well + * @param key the key of the configuration by which it can be retrieved as well * @param aValue */ public TestSensorContext setConfiguration(final String key, final Object aValue) { - ((TestConfiguration) configuration).set(key, aValue); return this; } public DefaultInputFile registerFile(final String filename) { - return registerFile(filename, new TestFileMetadata()); - } public DefaultInputFile registerFile(final String filename, final TestFileMetadata metadata) { - final DefaultInputFile file = createNewInputFile(filename, metadata); this.fs.add(file); return file; } /** - * Scans the (physical) filesystem of the sensor context to register all files in the - * sensor filesystem (cached) + * Scans the (physical) filesystem of the sensor context to register all files in the sensor filesystem (cached) * * @return - * * @throws IOException */ public TestSensorContext scanFiles() throws IOException { - final AtomicInteger files = new AtomicInteger(); final Path base = this.fs.baseDirPath(); @@ -353,7 +327,6 @@ public TestSensorContext scanFiles() throws IOException { @Override public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException { - final FileVisitResult result = super.visitFile(file, attrs); registerFile(base.relativize(file).toString()); files.incrementAndGet(); @@ -366,7 +339,6 @@ public FileVisitResult visitFile(final Path file, final BasicFileAttributes attr } private InputFile addTestFile(String filename, TestFileMetadata metadata) throws IOException { - final Path basePath = this.fs.baseDirPath(); final Path filePath = basePath.resolve(filename); final Path fileDir = filePath.getParent(); @@ -374,12 +346,10 @@ private InputFile addTestFile(String filename, TestFileMetadata metadata) throws Files.createDirectories(fileDir); } Files.createFile(filePath); - return registerFile(filename, metadata); } private DefaultInputFile createNewInputFile(final String filename, TestFileMetadata md) { - final String fileExtension = filename.substring(filename.lastIndexOf('.') + 1); final String language; if ("java".equals(fileExtension)) { @@ -393,50 +363,44 @@ private DefaultInputFile createNewInputFile(final String filename, TestFileMetad DefaultIndexedFile indexedFile; if (md.isTestResource) { indexedFile = new DefaultIndexedFile(this.fs.baseDirPath().resolve(filename), - this.inputModule.key(), - PathUtils.sanitize(filename), - PathUtils.sanitize(filename), - InputFile.Type.TEST, - language, - TestInputFileBuilder.nextBatchId(), - new SensorStrategy()); + this.inputModule.key(), + PathUtils.sanitize(filename), + PathUtils.sanitize(filename), + InputFile.Type.TEST, + language, + TestInputFileBuilder.nextBatchId(), + new SensorStrategy()); } else { indexedFile = new DefaultIndexedFile(this.inputModule.key(), this.fs.baseDirPath(), filename, language); } final DefaultInputFile file = new DefaultInputFile(indexedFile, dif -> dif.setMetadata(md.toMetadata())); file.checkMetadata(); - return file; } private void addMutants(final ResourceMutationMetrics rmm, final Mutant.State state, final int count, - TestFileMetadata metadata) { - - + TestFileMetadata metadata) { String filename = rmm.getResource().filename(); - for (int i = 0; i < count; i++) { rmm.addMutant(newMutant(filename, state, count + i, metadata)); } } private Mutant newMutant(String file, Mutant.State state, final int line, TestFileMetadata metadata) { - return Mutant.builder() - .mutantStatus(state) - .inSourceFile(file) - .inClass(file.substring(0, file.lastIndexOf('.')).replaceAll("/|\\\\", ".")) - .inMethod("aMethod") - .withMethodParameters("desc") - .inLine(line) - .atIndex(0) - .numberOfTestsRun(metadata.mutants.numTestRun) - .usingMutator(getMutationOperatorForLine(line)) - .killedBy(metadata.test.name) - .withDescription(metadata.mutants.description) - .build(); - + .mutantStatus(state) + .inSourceFile(file) + .inClass(file.substring(0, file.lastIndexOf('.')).replaceAll("/|\\\\", ".")) + .inMethod("aMethod") + .withMethodParameters("desc") + .inLine(line) + .atIndex(0) + .numberOfTestsRun(metadata.mutants.numTestRun) + .usingMutator(getMutationOperatorForLine(line)) + .killedBy(metadata.test.name) + .withDescription(metadata.mutants.description) + .build(); } public static class TestFileMetadata { @@ -450,10 +414,10 @@ public static class TestFileMetadata { public boolean isTestResource; public Metadata toMetadata() { - - int[] originalLineEndOffsets = IntStream.range(0, lines).map(i -> ((i+1)*80)-1).toArray(); + int[] originalLineEndOffsets = IntStream.range(0, lines).map(i -> ((i + 1) * 80) - 1).toArray(); int[] originalLineStartOffsets = IntStream.range(0, lines).map(i -> i * 80).toArray(); - return new Metadata(lines, nonBlankLines, hash, originalLineStartOffsets, originalLineEndOffsets, lastValidOffset); + return new Metadata(lines, nonBlankLines, hash, originalLineStartOffsets, originalLineEndOffsets, + lastValidOffset); } public static class MutantMetadata implements Serializable { @@ -474,6 +438,7 @@ public static class TestMetadata implements Serializable { private static final long serialVersionUID = 7691838474280645687L; public String name = "ATest"; } + } public static class TestSensorStorage implements SensorStorage { @@ -491,13 +456,11 @@ public static class TestSensorStorage implements SensorStorage { @Override public void store(final Measure measure) { - measures.add(measure); } @Override public void store(final Issue issue) { - issues.add(issue); } @@ -533,59 +496,50 @@ public void store(NewSymbolTable symbolTable) { @Override public void store(final AnalysisError analysisError) { - analysisErrors.add(analysisError); } @Override public void storeProperty(final String key, final String value) { - properties.put(key, value); } @Override public void store(NewSignificantCode significantCode) { - } public List getMeasures() { - return measures; } public List getIssues() { - return issues; } public List getHighlightings() { - return highlightings; } public List getCoverages() { - return coverages; } public List getCpdTokens() { - return cpdTokens; } public List getSymbolTables() { - return symbolTables; } public List getAnalysisErrors() { - return analysisErrors; } public Map getProperties() { - return properties; } + } + } diff --git a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/TestUtils.java b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/TestUtils.java index a44d966..4aa162b 100644 --- a/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/TestUtils.java +++ b/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/TestUtils.java @@ -20,176 +20,140 @@ package ch.devcon5.sonar.plugins.mutationanalysis.testharness; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotNull; import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.IOException; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; import java.util.function.Consumer; - import org.apache.commons.io.IOUtils; -import org.junit.rules.TemporaryFolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class TestUtils { - /** - * SLF4J Logger for this class - */ - private static final Logger LOG = LoggerFactory.getLogger(TestUtils.class); - - /** - * Creates a temporary file with content from a classpath resource in a path relative to a {@link TemporaryFolder}. - * - * @param folder - * the {@link TemporaryFolder} rule in which the file should be created - * @param filePath - * the path to the file relative to the root of the temporary folder - * @param resource - * the absolute path to the classpath resource. - * @return the created temporary file - * @throws IOException - * @throws FileNotFoundException - */ - public static File tempFileFromResource(final TemporaryFolder folder, final String filePath, final String resource) - throws IOException, FileNotFoundException { - - return tempFileFromResource(folder, filePath, TestUtils.class, resource); - } - - /** - * Creates a temporary file with content from a classpath resource in a path relative to a {@link TemporaryFolder}. - * - * @param folder - * the {@link TemporaryFolder} rule in which the file should be created - * @param filePath - * the path to the file relative to the root of the temporary folder - * @param baseClass - * the class that is used to resolve the classpath resource. - * @param resource - * the path to the classpath resource relative to the class. The content of the resource is written to - * the temporary file - * @return the created file - * @throws IOException - * @throws FileNotFoundException - */ - public static File tempFileFromResource(final TemporaryFolder folder, final String filePath, - final Class baseClass, final String resource) throws IOException, FileNotFoundException { - - final File tempFile = newTempFile(folder, filePath); - final URL url = baseClass.getResource(resource); - assertNotNull("Resource " + resource + " not found", url); - copyResourceToFile(url, tempFile); - return tempFile; + /** + * SLF4J Logger for this class + */ + private static final Logger LOG = LoggerFactory.getLogger(TestUtils.class); + + /** + * Creates a temporary file with content from a classpath resource in a path relative to a {@link File}. + * + * @param folder the {@link File} folder in which the file should be created + * @param filePath the path to the file relative to the root of the temporary folder + * @param resource the absolute path to the classpath resource. + * @return the created temporary file + * @throws IOException if the temporary file could not be created + */ + public static File tempFileFromResource(final Path folder, final String filePath, final String resource) throws IOException { + return tempFileFromResource(folder, filePath, TestUtils.class, resource); + } + + /** + * Creates a temporary file with content from a classpath resource in a path relative to a {@link File}. + * + * @param folder the {@link File} directory in which the file should be created + * @param filePath the path to the file relative to the root of the temporary folder + * @param baseClass the class that is used to resolve the classpath resource. + * @param resource the path to the classpath resource relative to the class. The content of the resource is written to + * the temporary file + * @return the created file + * @throws IOException if the temporary file could not be created + */ + public static File tempFileFromResource(final Path folder, final String filePath, final Class baseClass, final String resource) throws IOException { + final File tempFile = newTempFile(folder, filePath); + final URL url = baseClass.getResource(resource); + assertNotNull(url, "Resource " + resource + " not found"); + copyResourceToFile(url, tempFile); + return tempFile; + } + + /** + * Copies the content from the URL to the specified file + * + * @param resource the location of the resource + * @param tempFile the file to write the data from the resource to + * @throws IOException the resource could not be copied to the file location + */ + public static void copyResourceToFile(final URL resource, final File tempFile) throws IOException { + IOUtils.copy(resource.openStream(), Files.newOutputStream(tempFile.toPath())); + LOG.info("Created temp file {}", tempFile.getAbsolutePath()); + } + + /** + * Creates a new temporary file in the {@link File} folder. The file may be specified as path relative to the root of + * the temporary folder + * + * @param folder the temporary folder in which to create the new file + * @param filePath the name of the file or a relative path to the file to be created + * @return the {@link File} reference to the newly created file + * @throws IOException the new temporary file could not be created at the specified location + */ + public static File newTempFile(final Path folder, final String filePath) throws IOException { + String path; + String filename; + final int lastPathSeparator = filePath.lastIndexOf('/'); + if (lastPathSeparator != -1) { + path = filePath.substring(0, lastPathSeparator); + filename = filePath.substring(lastPathSeparator + 1); + } else { + path = null; + filename = filePath; } - - /** - * Copies the content from the URL to the specified file - * - * @param resource - * the location of the resource - * @param tempFile - * the file to write the data from the resource to - * @throws IOException - * @throws FileNotFoundException - */ - public static void copyResourceToFile(final URL resource, final File tempFile) throws IOException, - FileNotFoundException { - - IOUtils.copy(resource.openStream(), new FileOutputStream(tempFile)); - LOG.info("Created temp file {}", tempFile.getAbsolutePath()); - } - - /** - * Creates a new temporary file in the {@link TemporaryFolder}. The file may be specified as path relative to the - * root of the temporary folder - * - * @param folder - * the temporary folder in which to create the new file - * @param filePath - * the name of the file or a relative path to the file to be created - * @return the {@link File} reference to the newly created file - * @throws IOException - */ - public static File newTempFile(final TemporaryFolder folder, final String filePath) throws IOException { - - String path; - String filename; - final int lastPathSeparator = filePath.lastIndexOf('/'); - if (lastPathSeparator != -1) { - path = filePath.substring(0, lastPathSeparator); - filename = filePath.substring(lastPathSeparator + 1); - } else { - path = null; - filename = filePath; - } - return createTempFile(folder, path, filename); + return createTempFile(folder, path, filename); + } + + /** + * Creates a temporary file in the specified path relative to the {@link File} folder + * + * @param folder the {@link File} folder that provides the root for the file + * @param path the relative to the file, without the filename itself + * @param filename the name of the file to create + * @return the {@link File} reference to the new file + * @throws IOException if the temporary file could not be created + */ + public static File createTempFile(final Path folder, final String path, final String filename) throws IOException { + File tempFile; + if (path != null) { + File newFolder; + Path newPath = folder.resolve(path); + if (!Files.exists(newPath)) { + newFolder = Files.createDirectories(newPath).toFile(); + } else { + newFolder = newPath.toFile(); + } + tempFile = Files.createFile(newFolder.toPath().resolve(filename)).toFile(); + } else { + tempFile = Files.createFile(folder.resolve(filename)).toFile(); } - - /** - * Creates a temporary file in the specified path relative to the {@link TemporaryFolder} rule - * - * @param folder - * the {@link TemporaryFolder} that provides the root for the file - * @param path - * the relative to the file, without the filename itself - * @param filename - * the name of the file to create - * @return the {@link File} reference to the new file - * @throws IOException - */ - public static File createTempFile(final TemporaryFolder folder, final String path, final String filename) - throws IOException { - - File tempFile; - if (path != null) { - final String[] pathSegments = path.split("\\/"); - File newFolder; - - Path newPath = folder.getRoot().toPath().resolve(path); - if(!Files.exists(newPath)){ - newFolder = folder.newFolder(pathSegments); - } else { - newFolder = newPath.toFile(); - } - - tempFile = new File(newFolder, filename); - } else { - tempFile = folder.newFile(filename); - } - return tempFile; - } - - public static G assertContains(List container, Consumer filter) { - - return container.stream().filter(m -> { - try { - filter.accept(m); - return true; - } catch (AssertionError e) { - return false; - } + return tempFile; + } + + public static G assertContains(List container, Consumer filter) { + return container.stream().filter(m -> { + try { + filter.accept(m); + return true; + } catch (AssertionError e) { + return false; + } }).findFirst() - .orElseThrow(() -> new AssertionError("No element found matching assertions, elements: " + container)); + .orElseThrow(() -> new AssertionError("No element found matching assertions, elements: " + container)); + } + + public static void assertNotContains(List container, Consumer filter) { + container.stream().filter(m -> { + try { + filter.accept(m); + return false; + } catch (AssertionError e) { + return true; + } + }).findFirst().orElseThrow(() -> new AssertionError("No element found matching assertions")); + } - } - - public static void assertNotContains(List container, Consumer filter) { - - container.stream().filter(m -> { - try { - filter.accept(m); - return false; - } catch (AssertionError e) { - return true; - } - }).findFirst().orElseThrow(() -> new AssertionError("No element found matching assertions")); - - } }