├── .github
└── workflows
│ └── maven.yml
├── .gitignore
├── .travis.yml
├── COPYRIGHT
├── LICENSE
├── README.md
├── pom.xml
├── sonar-project.properties
└── src
├── main
├── java
│ └── ch
│ │ └── devcon5
│ │ └── sonar
│ │ └── plugins
│ │ └── mutationanalysis
│ │ ├── MutationAnalysisPlugin.java
│ │ ├── Streams.java
│ │ ├── metrics
│ │ ├── MetricDefinitionException.java
│ │ ├── MutationAnalysisMetrics.java
│ │ ├── MutationDensityComputer.java
│ │ ├── MutationMetrics.java
│ │ ├── MutationScoreComputer.java
│ │ ├── QuantitativeMeasureComputer.java
│ │ ├── ResourceMutationMetrics.java
│ │ ├── TestKillRatioComputer.java
│ │ └── TotalMutationsComputer.java
│ │ ├── model
│ │ ├── Mutant.java
│ │ ├── MutationOperator.java
│ │ ├── MutationOperators.java
│ │ └── TestDescriptor.java
│ │ ├── report
│ │ ├── PitestReportParser.java
│ │ ├── ReportFinder.java
│ │ └── Reports.java
│ │ ├── rules
│ │ ├── JavaProfileDefinition.java
│ │ ├── JavaRulesDefinition.java
│ │ ├── KotlinProfileDefinition.kt
│ │ ├── KotlinRulesDefinition.kt
│ │ ├── MutationAnalysisProfileDefinition.java
│ │ └── MutationAnalysisRulesDefinition.java
│ │ └── sensors
│ │ ├── PitestSensor.java
│ │ ├── ReportCollector.java
│ │ ├── ResourceResolver.java
│ │ ├── RulesProcessor.java
│ │ ├── SourceMetricsWriter.java
│ │ └── TestMetricsWriter.java
└── resources
│ ├── ch
│ └── devcon5
│ │ └── sonar
│ │ └── plugins
│ │ └── mutationanalysis
│ │ ├── model
│ │ ├── ARGUMENT_PROPAGATION.html
│ │ ├── BOOLEAN_FALSE_RETURN.html
│ │ ├── BOOLEAN_TRUE_RETURN.html
│ │ ├── CONDITIONALS_BOUNDARY.html
│ │ ├── CONSTRUCTOR_CALLS.html
│ │ ├── EMPTY_RETURN_VALUES.html
│ │ ├── EXPERIMENTAL_INLINE_CONSTS.html
│ │ ├── EXPERIMENTAL_MEMBER_VARIABLE.html
│ │ ├── EXPERIMENTAL_NAKED_RECEIVER.html
│ │ ├── EXPERIMENTAL_REMOVE_INCREMENTS.html
│ │ ├── EXPERIMENTAL_REMOVE_SWITCH.html
│ │ ├── EXPERIMENTAL_RETURN_VALUES.html
│ │ ├── EXPERIMENTAL_SWITCH.html
│ │ ├── INCREMENTS.html
│ │ ├── INLINE_CONSTS.html
│ │ ├── INVERT_NEGS.html
│ │ ├── MATH.html
│ │ ├── NEGATE_CONDITIONALS.html
│ │ ├── NON_VOID_METHOD_CALLS.html
│ │ ├── NULL_RETURN_VALUES.html
│ │ ├── PRIMITIVE_RETURN_VALS.html
│ │ ├── REMOVE_CONDITIONALS.html
│ │ ├── RETURN_VALS.html
│ │ ├── VOID_METHOD_CALLS.html
│ │ ├── empty.html
│ │ └── mutagen-def.xml
│ │ └── rules.xml
│ └── log4j2.xml
└── test
├── java
└── ch
│ └── devcon5
│ └── sonar
│ └── plugins
│ └── mutationanalysis
│ ├── MutationAnalysisPluginTest.java
│ ├── StreamsTest.java
│ ├── metrics
│ ├── MutationAnalysisMetricsTest.java
│ ├── MutationDensityComputerTest.java
│ ├── MutationMetricsTest.java
│ ├── MutationScoreComputerTest.java
│ ├── QuantitativeMeasureComputerTest.java
│ ├── ResourceMutationMetricsTest.java
│ ├── TestKillRatioComputerTest.java
│ └── TotalMutationsComputerTest.java
│ ├── model
│ ├── BuilderTest.java
│ ├── MutantTest.java
│ ├── MutationOperatorTest.java
│ ├── MutationOperatorsTest.java
│ ├── StateTest.java
│ └── TestDescriptorTest.java
│ ├── report
│ ├── PitestReportParserTest.java
│ ├── ReportFinderTest.java
│ └── ReportsTest.java
│ ├── rules
│ ├── JavaProfileDefinitionTest.java
│ ├── JavaRulesDefinitionTest.java
│ ├── KotlinProfileDefinitionTest.kt
│ └── KotlinRulesDefinitionTest.kt
│ ├── sensors
│ ├── PitestSensorTest.java
│ ├── ReportCollectorTest.java
│ ├── ResourceResolverTest.java
│ ├── RulesProcessorTest.java
│ ├── SourceMetricsWriterTest.java
│ └── TestMetricsWriterTest.java
│ └── testharness
│ ├── LogRecordingAppender.java
│ ├── MeasureComputerTestHarness.java
│ ├── SensorTestHarness.java
│ ├── SystemLocaleExtension.java
│ ├── TestConfiguration.java
│ ├── TestSensorContext.java
│ └── TestUtils.java
└── resources
├── ch
└── devcon5
│ └── sonar
│ └── plugins
│ └── mutationanalysis
│ ├── report
│ ├── PitestReportParserTest_XXE.xml
│ ├── PitestReportParserTest_broken.xml
│ ├── PitestReportParserTest_mutations.xml
│ ├── PitestReportParserTest_mutationsWithDescriptions.xml
│ ├── PitestReportParserTest_mutationsWithNumTests.xml
│ ├── ReportFinderTest_mutations.xml
│ └── ReportsTest_mutations.xml
│ └── sensors
│ ├── PitestSensorTest_Java_mutations.xml
│ ├── PitestSensorTest_KotlinJava_mutations.xml
│ ├── PitestSensorTest_Kotlin_mutations.xml
│ └── ReportCollectorTest_mutations.xml
└── log4j2.xml
/.github/workflows/maven.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven
3 |
4 | # This workflow uses actions that are not certified by GitHub.
5 | # They are provided by a third-party and are governed by
6 | # separate terms of service, privacy policy, and support
7 | # documentation.
8 |
9 | name: Java CI with Maven and Sonar
10 |
11 | on:
12 | push:
13 | branches: [ "master" ]
14 | pull_request:
15 | types: [opened, synchronize, reopened]
16 |
17 | jobs:
18 | build:
19 |
20 | runs-on: ubuntu-latest
21 |
22 | steps:
23 | - uses: actions/checkout@v3
24 | - name: Set up JDK 17
25 | uses: actions/setup-java@v3
26 | with:
27 | java-version: '17'
28 | distribution: 'temurin'
29 | cache: maven
30 | - name: Cache SonarCloud packages
31 | uses: actions/cache@v3
32 | with:
33 | path: ~/.sonar/cache
34 | key: ${{ runner.os }}-sonar
35 | restore-keys: ${{ runner.os }}-sonar
36 | - name: Cache Maven packages
37 | uses: actions/cache@v2
38 | with:
39 | path: ~/.m2
40 | key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
41 | restore-keys: |
42 | ${{ runner.os }}-m2
43 | - name: Build with Maven
44 | run: mvn -B package --file pom.xml
45 | - name: Analyze with SonarCloud
46 | env:
47 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
48 | run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=devcon5io_mutation-analysis-plugin
49 | - name: Update dependency graph
50 | uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6
51 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # ---- Maven
2 | target/
3 |
4 | # ---- IntelliJ IDEA
5 | *.iws
6 | *.iml
7 | *.ipr
8 | .idea/
9 |
10 | # ---- Eclipse
11 | .classpath
12 | .project
13 | .settings
14 |
15 | # ---- MacOS
16 | .DS_Store
17 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 | sudo: false
3 | install: true
4 | addons:
5 | sonarcloud:
6 | organization: devcon5
7 | token: $SONAR_TOKEN
8 |
9 | jdk:
10 | - openjdk11
11 |
12 | script:
13 | - mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package org.jacoco:jacoco-maven-plugin:report
14 | - if [ type != pull_request ]; then mvn sonar:sonar; fi
15 |
16 | cache:
17 | directories:
18 | - $HOME/.m2/repository
19 | - $HOME/.sonar/cache
20 |
--------------------------------------------------------------------------------
/COPYRIGHT:
--------------------------------------------------------------------------------
1 | Mutation Analysis Plugin
2 | Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
3 | info@devcon5.ch
4 |
5 | This program is free software; you can redistribute it and/or
6 | modify it under the terms of the GNU Lesser General Public
7 | License as published by the Free Software Foundation; either
8 | version 3 of the License, or (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 | Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program; if not, write to the Free Software Foundation,
17 | Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Mutation Analysis Plugin for Sonarqube
2 | ======================================
3 |
4 | This plugin enables Mutation Analysis of reports generated by [Pitest](http://pitest.org/) in SonarQube
5 |
6 | Badges
7 | ------
8 |
9 | [](https://sonarcloud.io/summary/new_code?id=devcon5io_mutation-analysis-plugin)
10 |
11 | Documentation
12 | -------------
13 | Documentation for the Plugin is located in the [Mutation-Analysis-Plugin Wiki](https://github.com/devcon5io/mutation-analysis-plugin/wiki)
14 |
15 | Contribution
16 | ------------
17 | see https://github.com/devcon5io/mutation-analysis-plugin/wiki/Contributing
--------------------------------------------------------------------------------
/sonar-project.properties:
--------------------------------------------------------------------------------
1 | sonar.projectKey=ch.devcon5.sonar:mutation-analysis-plugin
2 | sonar.projectName=mutation-analysis-plugin
3 |
4 | # =====================================================
5 | # Meta-data for the project
6 | # =====================================================
7 |
8 | sonar.links.homepage=https://github.com/devcon5io/mutation-analysis-plugin
9 | sonar.links.ci=https://travis-ci.org/devcon5io/mutation-analysis-plugin
10 | sonar.links.scm=https://github.com/devcon5io/mutation-analysis-plugin
11 | sonar.links.issue=https://github.com/devcon5io/mutation-analysis-plugin/issues
12 |
13 | # =====================================================
14 | # Properties that will be shared amongst all modules
15 | # =====================================================
16 |
17 | sonar.host.url=https://sonarcloud.io
18 | sonar.organization=devcon5
19 | sonar.sources=src
20 | sonar.java.binaries=**/target/classes
21 |
--------------------------------------------------------------------------------
/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/Streams.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis;
22 |
23 | import java.util.stream.Stream;
24 | import java.util.stream.StreamSupport;
25 |
26 | public final class Streams {
27 |
28 | private Streams() {
29 | //to prevent instantiation of this class
30 | }
31 |
32 | public static Stream sequentialStream(Iterable elements) {
33 | return StreamSupport.stream(elements.spliterator(), false);
34 | }
35 |
36 | public static Stream parallelStream(Iterable elements) {
37 | return StreamSupport.stream(elements.spliterator(), true);
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MetricDefinitionException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis.metrics;
22 |
23 | /**
24 | *
25 | */
26 | public class MetricDefinitionException extends RuntimeException {
27 |
28 | public MetricDefinitionException(final String s, final Exception e) {
29 | super(s, e);
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationAnalysisMetrics.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis.metrics;
22 |
23 | import java.util.Collections;
24 | import java.util.List;
25 | import org.sonar.api.measures.Metric;
26 | import org.sonar.api.measures.Metrics;
27 |
28 | /**
29 | * PitestPro Metrics Extension for the SonarQube
30 | */
31 | public class MutationAnalysisMetrics implements Metrics {
32 |
33 | @Override
34 | public List getMetrics() {
35 | return Collections.unmodifiableList(MutationMetrics.getSensorMetrics());
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationDensityComputer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis.metrics;
22 |
23 | import static org.slf4j.LoggerFactory.getLogger;
24 |
25 | import ch.devcon5.sonar.plugins.mutationanalysis.MutationAnalysisPlugin;
26 | import org.slf4j.Logger;
27 | import org.sonar.api.ce.measure.Measure;
28 | import org.sonar.api.ce.measure.MeasureComputer;
29 | import org.sonar.api.config.Configuration;
30 | import org.sonar.api.measures.CoreMetrics;
31 |
32 | /**
33 | * Computer for calculating the mutation coverage of a component based on the detected vs total mutations.
34 | */
35 | public class MutationDensityComputer implements MeasureComputer {
36 |
37 | private static final Logger LOG = getLogger(MutationDensityComputer.class);
38 |
39 | private final Configuration config;
40 |
41 | public MutationDensityComputer(final Configuration config) {
42 | this.config = config;
43 | }
44 |
45 | @Override
46 | public MeasureComputerDefinition define(final MeasureComputerDefinitionContext defContext) {
47 | return defContext.newDefinitionBuilder()
48 | .setInputMetrics(MutationMetrics.MUTATIONS_TOTAL.key(), CoreMetrics.LINES_TO_COVER_KEY)
49 | .setOutputMetrics(MutationMetrics.MUTATIONS_DENSITY.key())
50 | .build();
51 | }
52 |
53 | @Override
54 | public void compute(final MeasureComputerContext context) {
55 | if (!MutationAnalysisPlugin.isExperimentalFeaturesEnabled(config)) {
56 | LOG.info("Experimental Features disabled");
57 | return;
58 | }
59 |
60 | LOG.info("Computing Mutation Density for {}", context.getComponent());
61 | final Measure lines = context.getMeasure(CoreMetrics.LINES_TO_COVER_KEY);
62 | final Measure mutations = context.getMeasure(MutationMetrics.MUTATIONS_TOTAL.key());
63 | if (mutations == null) {
64 | return;
65 | }
66 |
67 | final double density;
68 | if (lines == null) {
69 | density = 0d;
70 | } else {
71 | density = 100.0d * ((double) mutations.getIntValue() / (double) lines.getIntValue());
72 | }
73 |
74 | if (!Double.isNaN(density)) {
75 | LOG.info("Computed Mutation Density of {} for {}", density, context.getComponent());
76 | context.addMeasure(MutationMetrics.MUTATIONS_DENSITY.key(), density);
77 | }
78 | }
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationScoreComputer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis.metrics;
22 |
23 | import static ch.devcon5.sonar.plugins.mutationanalysis.MutationAnalysisPlugin.FORCE_MISSING_COVERAGE_TO_ZERO;
24 | import static org.slf4j.LoggerFactory.getLogger;
25 |
26 | import java.util.Optional;
27 | import org.slf4j.Logger;
28 | import org.sonar.api.batch.measure.Metric;
29 | import org.sonar.api.ce.measure.Measure;
30 | import org.sonar.api.ce.measure.MeasureComputer;
31 | import org.sonar.api.config.Configuration;
32 |
33 | /**
34 | * Computer for calculating the mutation coverage of a component based on the detected vs total mutations.
35 | */
36 | public class MutationScoreComputer implements MeasureComputer {
37 |
38 | private static final Logger LOG = getLogger(MutationScoreComputer.class);
39 |
40 | private final Configuration config;
41 |
42 | public MutationScoreComputer(final Configuration config) {
43 | this.config = config;
44 | }
45 |
46 | @Override
47 | public MeasureComputerDefinition define(final MeasureComputerDefinitionContext defContext) {
48 | return defContext.newDefinitionBuilder()
49 | .setInputMetrics(MutationMetrics.MUTATIONS_DETECTED.key(), MutationMetrics.MUTATIONS_TOTAL.key(), MutationMetrics.MUTATIONS_SURVIVED.key())
50 | .setOutputMetrics(MutationMetrics.MUTATIONS_COVERAGE.key(), MutationMetrics.MUTATIONS_TEST_STRENGTH.key())
51 | .build();
52 | }
53 |
54 | @Override
55 | public void compute(final MeasureComputerContext context) {
56 | LOG.info("Computing Mutation Coverage for {}", context.getComponent());
57 | final Measure mutationsTotalMeasure = context.getMeasure(MutationMetrics.MUTATIONS_TOTAL.key());
58 | if (mutationsTotalMeasure != null) {
59 | final int mutationsTotal = mutationsTotalMeasure.getIntValue();
60 | if (mutationsTotal > 0) {
61 | final int detectedMutants = getMetric(context, MutationMetrics.MUTATIONS_DETECTED);
62 | final int survivedMutants = getMetric(context, MutationMetrics.MUTATIONS_SURVIVED);
63 | computeMutationsCoverage(context, detectedMutants, mutationsTotal);
64 | computeTestStrength(context, survivedMutants, detectedMutants);
65 | } else {
66 | // modules with no mutants (0 total) are always 100% covered (0 of 0 is 100%, right?)
67 | context.addMeasure(MutationMetrics.MUTATIONS_COVERAGE.key(), 100.0);
68 | context.addMeasure(MutationMetrics.MUTATIONS_TEST_STRENGTH.key(), 100.0);
69 | }
70 | } else if (config.getBoolean(FORCE_MISSING_COVERAGE_TO_ZERO).orElse(Boolean.FALSE)) {
71 | context.addMeasure(MutationMetrics.MUTATIONS_COVERAGE.key(), 0.0);
72 | context.addMeasure(MutationMetrics.MUTATIONS_TEST_STRENGTH.key(), 0.0);
73 | }
74 | }
75 |
76 | private void computeMutationsCoverage(final MeasureComputerContext context, final int coveredMutants, final int totalMutants) {
77 | final double coverage = 100.0 * coveredMutants / totalMutants;
78 | LOG.info("Computed Mutation Coverage of {}% for {}", coverage, context.getComponent());
79 | context.addMeasure(MutationMetrics.MUTATIONS_COVERAGE.key(), coverage);
80 | }
81 |
82 | private void computeTestStrength(final MeasureComputerContext context, final int survivedMutants, final int detectedMutants) {
83 | final double testStrength;
84 | int allMutantsQuantity = survivedMutants + detectedMutants;
85 | if (allMutantsQuantity != 0) {
86 | testStrength = 100.0 * detectedMutants / allMutantsQuantity;
87 | } else {
88 | testStrength = 0;
89 | }
90 | LOG.info("Computed Test Strength of {}% for {}", testStrength, context.getComponent());
91 | context.addMeasure(MutationMetrics.MUTATIONS_TEST_STRENGTH.key(), testStrength);
92 | }
93 |
94 | private int getMetric(final MeasureComputerContext context, Metric> metric) {
95 | return Optional.ofNullable(context.getMeasure(metric.key())).map(Measure::getIntValue).orElse(0);
96 | }
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/QuantitativeMeasureComputer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis.metrics;
22 |
23 | import static org.slf4j.LoggerFactory.getLogger;
24 |
25 | import ch.devcon5.sonar.plugins.mutationanalysis.Streams;
26 | import org.slf4j.Logger;
27 | import org.sonar.api.ce.measure.Measure;
28 | import org.sonar.api.ce.measure.MeasureComputer;
29 | import org.sonar.api.measures.Metric;
30 |
31 | /**
32 | * Computer that processes the aggregated quantitative metric for a component from all the quantitative metrics of its
33 | * children.
34 | */
35 | public class QuantitativeMeasureComputer implements MeasureComputer {
36 |
37 | private static final Logger LOG = getLogger(QuantitativeMeasureComputer.class);
38 |
39 | @Override
40 | public MeasureComputerDefinition define(final MeasureComputerDefinitionContext defContext) {
41 | return defContext.newDefinitionBuilder()
42 | .setOutputMetrics(MutationMetrics.getQuantitativeMetrics()
43 | .stream()
44 | .map(Metric::getKey)
45 | .toArray(String[]::new))
46 | .build();
47 | }
48 |
49 | @Override
50 | public void compute(final MeasureComputerContext context) {
51 | LOG.info("Computing quantitative mutation metrics for {}", context.getComponent());
52 | MutationMetrics.getQuantitativeMetrics()
53 | .stream()
54 | .filter(m -> !m.isHidden()) //exclude hidden metrics, see below
55 | .map(Metric::getKey)
56 | .filter(metricKey -> context.getMeasure(metricKey) == null)
57 | .forEach(metricKey -> {
58 | int sum = Streams.parallelStream(context.getChildrenMeasures(metricKey))
59 | .map(Measure::getIntValue)
60 | .reduce(0, Integer::sum);
61 | if (sum > 0) {
62 | LOG.info("Computed {} {} for {}", sum, metricKey, context.getComponent());
63 | context.addMeasure(metricKey, sum);
64 | }
65 | });
66 |
67 | // hidden utility metric are globally constant for all components
68 | MutationMetrics.getQuantitativeMetrics()
69 | .stream()
70 | .filter(Metric::isHidden)
71 | .map(Metric::getKey)
72 | .filter(metricKey -> context.getMeasure(metricKey) == null)
73 | .forEach(metricKey -> {
74 | int first = Streams.parallelStream(context.getChildrenMeasures(metricKey))
75 | .map(Measure::getIntValue)
76 | .findFirst().orElse(0);
77 | if (first > 0) {
78 | LOG.info("Computed {} {} for {}", first, metricKey, context.getComponent());
79 | context.addMeasure(metricKey, first);
80 | }
81 | });
82 | }
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/TestKillRatioComputer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis.metrics;
22 |
23 | import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.TEST_KILLS_KEY;
24 | import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.TEST_KILL_RATIO;
25 | import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.TEST_KILL_RATIO_KEY;
26 | import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.UTILITY_GLOBAL_MUTATIONS_KEY;
27 | import static org.slf4j.LoggerFactory.getLogger;
28 |
29 | import ch.devcon5.sonar.plugins.mutationanalysis.MutationAnalysisPlugin;
30 | import ch.devcon5.sonar.plugins.mutationanalysis.Streams;
31 | import org.slf4j.Logger;
32 | import org.sonar.api.ce.measure.Component;
33 | import org.sonar.api.ce.measure.Measure;
34 | import org.sonar.api.ce.measure.MeasureComputer;
35 | import org.sonar.api.config.Configuration;
36 |
37 | public class TestKillRatioComputer implements MeasureComputer {
38 |
39 | private static final Logger LOG = getLogger(TestKillRatioComputer.class);
40 |
41 | private final Configuration config;
42 |
43 | public TestKillRatioComputer(final Configuration config) {
44 | this.config = config;
45 | }
46 |
47 | @Override
48 | public MeasureComputerDefinition define(final MeasureComputerDefinitionContext defContext) {
49 | return defContext.newDefinitionBuilder()
50 | .setInputMetrics(UTILITY_GLOBAL_MUTATIONS_KEY, TEST_KILLS_KEY)
51 | .setOutputMetrics(TEST_KILL_RATIO_KEY)
52 | .build();
53 | }
54 |
55 | @Override
56 | public void compute(final MeasureComputerContext context) {
57 | if (!MutationAnalysisPlugin.isExperimentalFeaturesEnabled(config)) {
58 | LOG.info("ExperimentalFeature disabled");
59 | return;
60 | }
61 |
62 | final Component comp = context.getComponent();
63 | if (isNotUnitTestOrDirectory(comp)) {
64 | LOG.info("Skipping {} because it's not a test resource", comp);
65 | return;
66 | }
67 |
68 | final double mutationsGlobal = getMutationsGlobal(context, UTILITY_GLOBAL_MUTATIONS_KEY);
69 | final double testKillsLocal = getTestKillsLocal(context);
70 | if (mutationsGlobal == 0.0) {
71 | return;
72 | }
73 | final double percentage = 100.0d * testKillsLocal / mutationsGlobal;
74 | LOG.info("Computed {} of {}% from ({} / {}) for {}", TEST_KILL_RATIO.getName(), percentage, testKillsLocal, mutationsGlobal, comp);
75 | context.addMeasure(TEST_KILL_RATIO_KEY, percentage);
76 | }
77 |
78 | boolean isNotUnitTestOrDirectory(final Component comp) {
79 | return comp.getType() == Component.Type.FILE && !comp.getFileAttributes().isUnitTest();
80 | }
81 |
82 | private double getMutationsGlobal(final MeasureComputerContext context, String key) {
83 | final double mutationsGlobal;
84 | final Measure globalMutationsMeasure = context.getMeasure(key);
85 | if (globalMutationsMeasure == null) {
86 | mutationsGlobal = Streams.parallelStream(context.getChildrenMeasures(key))
87 | .mapToInt(Measure::getIntValue)
88 | .findFirst()
89 | .orElse(0);
90 | LOG.info("Component {} has no global mutation information, using first child's: {}", context.getComponent(), mutationsGlobal);
91 | } else {
92 | mutationsGlobal = globalMutationsMeasure.getIntValue();
93 | LOG.info("Using global mutation count: {}", mutationsGlobal);
94 | }
95 | return mutationsGlobal;
96 | }
97 |
98 | private double getTestKillsLocal(final MeasureComputerContext context) {
99 | final Measure localTestKills = context.getMeasure(TEST_KILLS_KEY);
100 | if (localTestKills == null) {
101 | return Streams.parallelStream(context.getChildrenMeasures(TEST_KILLS_KEY))
102 | .mapToInt(Measure::getIntValue)
103 | .sum();
104 | } else {
105 | return localTestKills.getIntValue();
106 | }
107 | }
108 |
109 | }
110 |
--------------------------------------------------------------------------------
/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/MutationOperator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis.model;
22 |
23 | import static java.util.Objects.requireNonNull;
24 |
25 | import java.io.IOException;
26 | import java.net.URL;
27 | import java.nio.charset.StandardCharsets;
28 | import java.util.Collection;
29 | import java.util.HashSet;
30 | import java.util.Optional;
31 | import java.util.Set;
32 | import org.slf4j.Logger;
33 | import org.slf4j.LoggerFactory;
34 | import org.sonar.api.internal.apachecommons.io.IOUtils;
35 | import org.sonar.api.rules.Rule;
36 |
37 | /**
38 | * Representation of a PIT MutationOperator. The mutagens are defined in a classpath located file name
39 | * mutagen-def.xml
in the same package. To get an instance of a defined mutagen, use the find() method. As
40 | * key for finding a mutagen, the ID, classname or a name that contains the classname as prefix can be used. The mutagen
41 | * itself contains a violation description as well a description of the mutagen itself that is copied from the
42 | * documentation at pitest.org/quickstart/mutators
43 | */
44 | public final class MutationOperator {
45 |
46 | private static final Logger LOG = LoggerFactory.getLogger(MutationOperator.class);
47 |
48 | private final String id;
49 | private final URL mutagenDescLoc;
50 | private final String violationDesc;
51 | private final String name;
52 | private final Set classNames;
53 |
54 | MutationOperator(final String id, final String name, final Collection classNames, final String violationDesc, final URL mutagenDescriptionLocation) {
55 | requireNonNull(id, "Id must not be null");
56 | requireNonNull(name, "name must not be null");
57 | requireNonNull(violationDesc, "violation description must not be null");
58 | this.id = id;
59 | this.name = name;
60 | this.classNames = new HashSet<>(requireNonNull(classNames, "classNames must not be null"));
61 | this.violationDesc = violationDesc;
62 | this.mutagenDescLoc = mutagenDescriptionLocation;
63 | }
64 |
65 | /**
66 | * The ID of the mutagen. The ID is a String that uniquely defines the MutationOperator, written uppercase, like
67 | * {@code ARGUMENT_PROPAGATION}.
68 | *
69 | * @return the {@link MutationOperator} id as a String
70 | */
71 | public String getId() {
72 | return id;
73 | }
74 |
75 | /**
76 | * A URL pointing to the description of the {@link MutationOperator}. The {@link MutationOperator} descriptions are
77 | * stored in the classpath as resource, for each {@link MutationOperator} a separate description. The URLs are
78 | * specified in the {@code mutagen-def.xml} in the classpath. The resources itself are html fragments that can be
79 | * embedded in a website.
80 | *
81 | * @return URL pointing to the resource containing the description.
82 | */
83 | public Optional getMutagenDescriptionLocation() {
84 | return Optional.ofNullable(mutagenDescLoc);
85 | }
86 |
87 | /**
88 | * The violation description used for the {@link MutationOperator} specific {@link Rule} that are violated if the
89 | * mutation caused by the {@link MutationOperator} is not killed.
90 | *
91 | * @return the string description the violation.
92 | */
93 | public String getViolationDescription() {
94 | return violationDesc;
95 | }
96 |
97 | /**
98 | * The name of the MutationOperator. Unlike the Id, it is more a display name.
99 | *
100 | * @return the name as a String
101 | */
102 | public String getName() {
103 | return name;
104 | }
105 |
106 | /**
107 | * The fully qualified classnames of the {@link MutationOperator} class.
108 | *
109 | * @return the classnames
110 | */
111 | public Set getClassNames() {
112 | return classNames;
113 | }
114 |
115 | /**
116 | * The description of the {@link MutationOperator}. The method loads the content defined in the resource that is
117 | * referred to by the description URL.
118 | *
119 | * @return the description as a string
120 | */
121 | public String getMutagenDescription() {
122 | return getMutagenDescriptionLocation().map(u -> {
123 | try {
124 | return IOUtils.toString(u, StandardCharsets.UTF_8);
125 | } catch (IOException e) {
126 | LOG.warn("Cannot read mutagen description for mutagen {}", id, e);
127 | return "No description";
128 | }
129 | }).orElse("");
130 | }
131 |
132 | @Override
133 | public int hashCode() {
134 | return 31 + id.hashCode();
135 | }
136 |
137 | @Override
138 | public boolean equals(final Object obj) {
139 | if (this == obj) {
140 | return true;
141 | }
142 | if (obj == null) {
143 | return false;
144 | }
145 | if (getClass() != obj.getClass()) {
146 | return false;
147 | }
148 | final MutationOperator other = (MutationOperator) obj;
149 | return id.equals(other.id);
150 | }
151 |
152 | }
153 |
--------------------------------------------------------------------------------
/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/MutationOperators.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis.model;
22 |
23 | import static javax.xml.xpath.XPathConstants.NODESET;
24 |
25 | import java.io.IOException;
26 | import java.io.InputStream;
27 | import java.net.URL;
28 | import java.util.Collection;
29 | import java.util.Collections;
30 | import java.util.HashMap;
31 | import java.util.HashSet;
32 | import java.util.Map;
33 | import java.util.Set;
34 | import javax.xml.xpath.XPath;
35 | import javax.xml.xpath.XPathExpressionException;
36 | import javax.xml.xpath.XPathFactory;
37 | import org.w3c.dom.Node;
38 | import org.w3c.dom.NodeList;
39 | import org.xml.sax.InputSource;
40 |
41 | /**
42 | *
43 | */
44 | public final class MutationOperators {
45 |
46 | /**
47 | * Default MutationOperator definition for an unknown {@link MutationOperator}
48 | */
49 | public static final MutationOperator UNKNOWN = new MutationOperator("UNKNOWN", "Unknown mutagen",
50 | Collections.singleton("unknown.mutation.operator"), "An unknown mutagen has been applied", null);
51 |
52 | /**
53 | * URL of the mutagen definitions.
54 | */
55 | private static final URL MUTAGEN_DEF = MutationOperator.class.getResource("mutagen-def.xml");
56 |
57 | /**
58 | * Contains all instances of {@link MutationOperator}s defined in the mutagen-def.xml
59 | */
60 | private static final Map INSTANCES;
61 | static {
62 | try (InputStream stream = MUTAGEN_DEF.openStream()) {
63 | final Map mutagens = new HashMap<>();
64 | final XPathFactory xPathFactory = XPathFactory.newInstance();
65 | final XPath xp = xPathFactory.newXPath();
66 | final NodeList mutagenNodes = (NodeList) xp.evaluate("//operator", new InputSource(stream), NODESET);
67 | for (int i = 0, len = mutagenNodes.getLength(); i < len; i++) {
68 | final Node mutagenNode = mutagenNodes.item(i);
69 | final MutationOperator mutationOperator = toMutagen(xPathFactory, mutagenNode);
70 | mutagens.put(mutationOperator.getId(), mutationOperator);
71 | }
72 | INSTANCES = Collections.unmodifiableMap(mutagens);
73 | } catch (IOException | XPathExpressionException e) {
74 | throw new MutationOperatorsInitializationException("Could not load mutagen definitions", e);
75 | }
76 | }
77 |
78 | private MutationOperators() {}
79 |
80 | /**
81 | * Converts a MutationOperator from the given {@link Node}
82 | *
83 | * @param xPathFactory the factory with which to create a new xpath
84 | * @param mutagenNode the node to convert to {@link ch.devcon5.sonar.plugins.mutationanalysis.model.MutationOperator}
85 | * @return a {@link MutationOperator} for the {@link Node}
86 | * @throws XPathExpressionException if there was an exception evaluating an xpath expression
87 | */
88 | private static MutationOperator toMutagen(final XPathFactory xPathFactory, final Node mutagenNode) throws XPathExpressionException {
89 | final XPath xp = xPathFactory.newXPath();
90 | final String id = xp.evaluate("@id", mutagenNode);
91 |
92 | final Set classNames = new HashSet<>();
93 | final NodeList classNodes = (NodeList) xp.evaluate("classes/class", mutagenNode, NODESET);
94 | for (int i = 0, len = classNodes.getLength(); i < len; i++) {
95 | final Node classNode = classNodes.item(i);
96 | classNames.add(classNode.getTextContent());
97 | }
98 |
99 | final String name = xp.evaluate("name", mutagenNode);
100 | final String violationDescription = xp.evaluate("violationDescription", mutagenNode).trim();
101 | final URL mutagenDescLoc = MutationOperator.class.getResource(xp.evaluate("operatorDescription/@classpath", mutagenNode));
102 |
103 | return new MutationOperator(id, name, classNames, violationDescription, mutagenDescLoc);
104 | }
105 |
106 | /**
107 | * Finds the {@link MutationOperator} using the specified key. The key could be the ID of the MutationOperator,
108 | * its classname or an extended classname, which is the classname with a suffix.
109 | *
110 | * @param mutagenKey the key to use when searching for the mutagen
111 | * @return a matching {@link MutationOperator} or an UNKNOWN mutagen
112 | */
113 | public static MutationOperator find(final String mutagenKey) {
114 | MutationOperator result = UNKNOWN;
115 | for (final MutationOperator mutationOperator : INSTANCES.values()) {
116 | if (mutagenKey.equals(mutationOperator.getId())
117 | || mutationOperator.getClassNames().stream().anyMatch(mutagenKey::startsWith)) {
118 | result = mutationOperator;
119 | break;
120 | }
121 | }
122 | return result;
123 | }
124 |
125 | /**
126 | * Retrieves all defined {@link MutationOperator}s from the mutagen-def.xml.
127 | *
128 | * @return a collection of {@link MutationOperator}s
129 | */
130 | public static Collection allMutationOperators() {
131 | return Collections.unmodifiableCollection(INSTANCES.values());
132 | }
133 |
134 | /**
135 | * Exception that is thrown if the mutation operators can not be loaded from configuration.
136 | */
137 | public static class MutationOperatorsInitializationException extends RuntimeException {
138 |
139 | public MutationOperatorsInitializationException(final String message, final Throwable cause) {
140 | super(message, cause);
141 | }
142 |
143 | }
144 |
145 | }
146 |
--------------------------------------------------------------------------------
/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/model/TestDescriptor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis.model;
22 |
23 | import java.util.Objects;
24 |
25 | public class TestDescriptor {
26 |
27 | private final String className;
28 | private final String methodName;
29 | private final String spec;
30 |
31 | public TestDescriptor(final String spec) {
32 | this.spec = spec;
33 |
34 | final int parenthesis = spec.indexOf('(');
35 | final int methodSeparator = spec.lastIndexOf('.', parenthesis);
36 | final int nestedSeparator = spec.indexOf('$');
37 |
38 | if (nestedSeparator != -1) {
39 | this.className = spec.substring(0, nestedSeparator);
40 | if (methodSeparator != -1) {
41 | this.methodName = spec.substring(methodSeparator + 1, parenthesis);
42 | } else {
43 | this.methodName = "unknown";
44 | }
45 | } else if (methodSeparator != -1) {
46 | this.className = spec.substring(0, methodSeparator);
47 | this.methodName = spec.substring(methodSeparator + 1, parenthesis);
48 | } else {
49 | className = spec;
50 | methodName = "unknown";
51 | }
52 | }
53 |
54 | public String getClassName() {
55 | return className;
56 | }
57 |
58 | public String getMethodName() {
59 | return methodName;
60 | }
61 |
62 | public String getSpec() {
63 | return spec;
64 | }
65 |
66 | @Override
67 | public String toString() {
68 | return "TestDescriptor{class='" + className + "', method='" + methodName + "'}";
69 | }
70 |
71 | @Override
72 | public boolean equals(final Object o) {
73 | if (this == o) {
74 | return true;
75 | }
76 | if (o == null || getClass() != o.getClass()) {
77 | return false;
78 | }
79 | final TestDescriptor that = (TestDescriptor) o;
80 | return Objects.equals(className, that.className);
81 | }
82 |
83 | @Override
84 | public int hashCode() {
85 | return Objects.hash(className);
86 | }
87 |
88 | }
89 |
--------------------------------------------------------------------------------
/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/report/ReportFinder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis.report;
22 |
23 | import java.io.IOException;
24 | import java.nio.file.FileSystems;
25 | import java.nio.file.FileVisitResult;
26 | import java.nio.file.Files;
27 | import java.nio.file.Path;
28 | import java.nio.file.PathMatcher;
29 | import java.nio.file.SimpleFileVisitor;
30 | import java.nio.file.attribute.BasicFileAttributes;
31 | import java.util.ArrayList;
32 | import java.util.List;
33 | import java.util.Objects;
34 |
35 | import org.slf4j.Logger;
36 | import org.slf4j.LoggerFactory;
37 |
38 | /**
39 | * Searches the latest xml file in the reports directory.
40 | */
41 | public class ReportFinder {
42 |
43 | /**
44 | * SLF4J Logger for this class
45 | */
46 | private static final Logger LOG = LoggerFactory.getLogger(ReportFinder.class);
47 |
48 | /**
49 | * Finds the PIT report in the given report directory.
50 | *
51 | * @param reportDirectory
52 | * the report directory to search for the report. The report directory must not be null
, must
53 | * exist and must be a directory.
54 | *
55 | * @return the Path to the found PIT report or null, if no report was found or the directory is no valid directory
56 | *
57 | * @throws IOException
58 | * if the most recent report could not be determined
59 | */
60 | public Path findReport(final Path reportDirectory) throws IOException {
61 | if (reportDirectory == null || !Files.exists(reportDirectory)) {
62 | LOG.warn("ReportDirectory {} is not a valid directory", reportDirectory);
63 | return null;
64 | }
65 | return findMostRecentReport(reportDirectory, "*.xml");
66 | }
67 |
68 | /**
69 | * Locates the most recent report in the report directory by searching all xml files in the reports directory and
70 | * selecting the most recent file.
71 | *
72 | * @param reportDirectory
73 | * the path to the report directory to search the report in
74 | * @param pattern
75 | * a globbing pattern, i.e. *.java or *.xml
76 | *
77 | * @return the {@link Path} to the most recent report
78 | *
79 | * @throws java.io.IOException if the report or the directory of the report can not be accessed
80 | */
81 | protected Path findMostRecentReport(final Path reportDirectory, final String pattern) throws IOException {
82 | Path mostRecent = null;
83 | ReportFinderVisitor reportFinderVisitor = new ReportFinderVisitor(pattern);
84 | Files.walkFileTree(reportDirectory, reportFinderVisitor);
85 | for (final Path report : reportFinderVisitor.getReports()) {
86 | if (mostRecent == null || isNewer(mostRecent, report)) {
87 | mostRecent = report;
88 | }
89 | }
90 | return mostRecent;
91 | }
92 |
93 | /**
94 | * Determines if the otherPath is newer than the referencePath.
95 | *
96 | * @param referencePath
97 | * the path to compare the other path against
98 | * @param otherPath
99 | * the other path to be compared against the reference path
100 | *
101 | * @return true
if the otherPath is newer than the referencePath
102 | *
103 | * @throws IOException if the last modification time can not be determined
104 | */
105 | protected boolean isNewer(final Path referencePath, final Path otherPath) throws IOException {
106 | return Files.getLastModifiedTime(referencePath).compareTo(Files.getLastModifiedTime(otherPath)) < 0;
107 | }
108 |
109 | /**
110 | * Recursive search report xml
111 | */
112 | static class ReportFinderVisitor extends SimpleFileVisitor {
113 |
114 | private final PathMatcher matcher;
115 |
116 | private final List reports = new ArrayList<>();
117 |
118 | public List getReports() {
119 | return reports;
120 | }
121 |
122 | ReportFinderVisitor(String pattern) {
123 | matcher = FileSystems.getDefault().getPathMatcher("glob:" + pattern);
124 | }
125 |
126 | @Override
127 | public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
128 | requireNonNull(file, "file must not be null");
129 | final Path filename = file.getFileName();
130 | if (Objects.nonNull(filename) && matcher.matches(filename)) {
131 | reports.add(file);
132 | }
133 | return FileVisitResult.CONTINUE;
134 | }
135 |
136 | private static void requireNonNull(T obj, String message) {
137 | if (obj == null) {
138 | throw new IllegalArgumentException(message);
139 | }
140 | }
141 |
142 | }
143 |
144 | }
145 |
--------------------------------------------------------------------------------
/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/report/Reports.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis.report;
22 |
23 | import ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant;
24 | import java.io.IOException;
25 | import java.nio.file.Path;
26 | import java.util.Collection;
27 | import java.util.Collections;
28 | import org.slf4j.Logger;
29 | import org.slf4j.LoggerFactory;
30 |
31 | /**
32 | * Utility class to read a {@link ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant}s from a report located in a
33 | * directory.
34 | */
35 | public final class Reports {
36 |
37 | /**
38 | * SLF4J Logger for this class
39 | */
40 | private static final Logger LOG = LoggerFactory.getLogger(Reports.class);
41 |
42 | private Reports() {}
43 |
44 | /**
45 | * Reads the {@link ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant}s from the report in the reports directory.
46 | * The method searches for the most recent {@code mutations.xml} report and returns its contents as a list.
47 | *
48 | * @param reportsDirectory the {@link Path} to the directory containing the report.
49 | * @return a collection of all {@link ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant}s declared in the report
50 | * or an empty list if neither report was found nor it contained any
51 | * {@link ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant}s. The method does not return null
52 | * @throws IOException if the search for the report failed or the report could not be read.
53 | */
54 | public static Collection readMutants(final Path reportsDirectory) throws IOException {
55 | LOG.debug("Searching pit reports in {}", reportsDirectory);
56 |
57 | final Path xmlReport;
58 | if (reportsDirectory.toFile().isDirectory()) {
59 | xmlReport = new ReportFinder().findReport(reportsDirectory);
60 | } else {
61 | xmlReport = reportsDirectory;
62 | }
63 |
64 | if (xmlReport == null) {
65 | LOG.warn("No XML PIT report found in directory {} !", reportsDirectory);
66 | LOG.warn(
67 | "Checkout plugin documentation for more detailed explanations: https://github.com/devcon5io/mutation-analysis-plugin");
68 | return Collections.emptyList();
69 | }
70 |
71 | return new PitestReportParser().parseMutants(xmlReport);
72 | }
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/JavaProfileDefinition.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis.rules;
22 |
23 | public class JavaProfileDefinition extends MutationAnalysisProfileDefinition {
24 |
25 | @Override
26 | protected String getLanguageKey() {
27 | return "java";
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/JavaRulesDefinition.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis.rules;
22 |
23 | import org.sonar.api.config.Configuration;
24 | import org.sonar.api.server.rule.RulesDefinitionXmlLoader;
25 |
26 | /**
27 | * The definition of pitest rules. A new repository is created for the Pitest plugin and Java language. The rules are
28 | * defined in the rules.xml file in the classpath. The rule keys are accessible as constants.
29 | */
30 | public class JavaRulesDefinition extends MutationAnalysisRulesDefinition {
31 |
32 | /**
33 | * Constructor to create the pitest rules definitions and repository. The constructor is invoked by Sonar.
34 | *
35 | * @param settings
36 | * the settings of the Pitest-Sensor plugin
37 | * @param xmlLoader The xml loader with which to load rules
38 | */
39 | public JavaRulesDefinition(final Configuration settings, final RulesDefinitionXmlLoader xmlLoader) {
40 | super(settings, xmlLoader);
41 | }
42 |
43 | @Override
44 | protected String getLanguageKey() {
45 | return "java";
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/KotlinProfileDefinition.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis.rules
22 |
23 |
24 | class KotlinProfileDefinition : MutationAnalysisProfileDefinition() {
25 |
26 | override fun getLanguageKey(): String {
27 | return "kotlin"
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/KotlinRulesDefinition.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis.rules
22 |
23 | import org.sonar.api.config.Configuration
24 | import org.sonar.api.server.rule.RulesDefinitionXmlLoader
25 |
26 |
27 | /**
28 | * The definition of pitest rules. A new repository is created for the Pitest plugin and Java language. The rules are
29 | * defined in the rules.xml file in the classpath. The rule keys are accessible as constants.
30 | */
31 | class KotlinRulesDefinition
32 |
33 | /**
34 | * Constructor to create the pitest rules definitions and repository. The constructor is invoked by Sonar.
35 | *
36 | * @param settings
37 | * the settings of the Pitest-Sensor plugin
38 | * @param xmlLoader
39 | */
40 | (settings: Configuration, xmlLoader: RulesDefinitionXmlLoader) : MutationAnalysisRulesDefinition(settings, xmlLoader) {
41 |
42 | override fun getLanguageKey(): String {
43 | return "kotlin"
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/MutationAnalysisProfileDefinition.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis.rules;
22 |
23 | import static ch.devcon5.sonar.plugins.mutationanalysis.rules.MutationAnalysisRulesDefinition.MUTANT_RULES_PREFIX;
24 | import static ch.devcon5.sonar.plugins.mutationanalysis.rules.MutationAnalysisRulesDefinition.REPOSITORY_KEY;
25 |
26 | import ch.devcon5.sonar.plugins.mutationanalysis.model.MutationOperators;
27 | import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition;
28 |
29 | public abstract class MutationAnalysisProfileDefinition implements BuiltInQualityProfilesDefinition {
30 |
31 | @Override
32 | public void define(final Context context) {
33 | final NewBuiltInQualityProfile mutationAnalysis = context.createBuiltInQualityProfile("Mutation Analysis", getLanguageKey());
34 | MutationOperators.allMutationOperators().forEach(
35 | m -> mutationAnalysis.activateRule(REPOSITORY_KEY + "." + getLanguageKey(), MUTANT_RULES_PREFIX + m.getId()));
36 | mutationAnalysis.done();
37 | }
38 |
39 | protected abstract String getLanguageKey();
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/ResourceResolver.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis.sensors;
22 |
23 | import java.util.Optional;
24 | import org.sonar.api.batch.fs.FileSystem;
25 | import org.sonar.api.batch.fs.InputFile;
26 |
27 | /**
28 | * Resolver that resolves any given Java or Kotlin class to its source class. If the class is a nested class,
29 | * then its parent is returned
30 | */
31 | public class ResourceResolver {
32 |
33 | private final FileSystem fs;
34 |
35 | public ResourceResolver(final FileSystem fs) {
36 | this.fs = fs;
37 | }
38 |
39 | public Optional resolve(String classname) {
40 | return Optional.ofNullable(fs.inputFile(fs.predicates()
41 | .or(fs.predicates().matchesPathPattern("**/" + getPathToSourceFile(classname, "java")),
42 | fs.predicates().matchesPathPattern("**/" + getPathToSourceFile(classname, "kt")))));
43 | }
44 |
45 | private String getPathToSourceFile(String classname, String language) {
46 | final int nestedClass = classname.indexOf('$');
47 | final String mainClass;
48 | if (nestedClass != -1) {
49 | mainClass = classname.substring(0, nestedClass);
50 | } else {
51 | mainClass = classname;
52 | }
53 | return mainClass.trim().replace(".", "/") + "." + language;
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/SourceMetricsWriter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis.sensors;
22 |
23 | import static org.slf4j.LoggerFactory.getLogger;
24 |
25 | import ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics;
26 | import ch.devcon5.sonar.plugins.mutationanalysis.metrics.ResourceMutationMetrics;
27 | import ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant;
28 | import java.util.Collection;
29 | import org.slf4j.Logger;
30 | import org.sonar.api.batch.fs.InputFile;
31 | import org.sonar.api.batch.sensor.SensorContext;
32 | import org.sonar.api.batch.sensor.coverage.NewCoverage;
33 |
34 | /**
35 | *
36 | */
37 | public class SourceMetricsWriter {
38 |
39 | private static final Logger LOG = getLogger(SourceMetricsWriter.class);
40 |
41 | /**
42 | * Saves the information of the mutants the sensors context.
43 | *
44 | * @param metrics the mutant information parsed from the PIT report
45 | * @param context the current {@link org.sonar.api.batch.sensor.SensorContext}
46 | * @param globalMutants the global collection of mutants
47 | */
48 | public void writeMetrics(final Collection metrics, final SensorContext context, final Collection globalMutants) {
49 | final int total = globalMutants.isEmpty() ? sumTotal(metrics) : globalMutants.size();
50 | final int alive = total - (globalMutants.isEmpty()
51 | ? metrics.stream().mapToInt(rmm -> countDetected(rmm.getMutants())).sum()
52 | : countDetected(globalMutants));
53 | for (final ResourceMutationMetrics resourceMetrics : metrics) {
54 | saveResourceMetrics(resourceMetrics, context);
55 | context.newMeasure().forMetric(MutationMetrics.UTILITY_GLOBAL_MUTATIONS).on(resourceMetrics.getResource())
56 | .withValue(total).save();
57 | context.newMeasure().forMetric(MutationMetrics.UTILITY_GLOBAL_ALIVE).on(resourceMetrics.getResource())
58 | .withValue(alive).save();
59 | }
60 | }
61 |
62 | private int countDetected(final Collection c) {
63 | return (int) c.stream().filter(Mutant::isDetected).count();
64 | }
65 |
66 | private int sumTotal(final Collection metrics) {
67 | return (int) metrics.stream().mapToLong(ResourceMutationMetrics::getMutationsTotal).sum();
68 | }
69 |
70 | /**
71 | * Saves the {@link Mutant} metrics for the given resource in the SonarContext
72 | *
73 | * @param resourceMetrics the actual metrics for the resource to persist
74 | * @param context the context to register the metrics
75 | */
76 | private void saveResourceMetrics(final ResourceMutationMetrics resourceMetrics, final SensorContext context) {
77 | final InputFile resource = resourceMetrics.getResource();
78 | LOG.debug("Saving resource metrics for {}", resource);
79 | if (resourceMetrics.getMutationsKilled() > 0) {
80 | final NewCoverage newCov = context.newCoverage().onFile(resource);
81 | for (Mutant m : resourceMetrics.getMutants()) {
82 | if (Mutant.State.KILLED == m.getState()) {
83 | newCov.lineHits(m.getLineNumber(), 1);
84 | }
85 | }
86 | newCov.save();
87 | }
88 | if (resource.type() == InputFile.Type.MAIN) {
89 | context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_TOTAL)
90 | .withValue(resourceMetrics.getMutationsTotal()).save();
91 | context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_NO_COVERAGE)
92 | .withValue(resourceMetrics.getMutationsNoCoverage()).save();
93 | context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_KILLED)
94 | .withValue(resourceMetrics.getMutationsKilled()).save();
95 | context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_SURVIVED)
96 | .withValue(resourceMetrics.getMutationsSurvived()).save();
97 | context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_ALIVE)
98 | .withValue(resourceMetrics.getMutationsTotal() - resourceMetrics.getMutationsDetected()).save();
99 | context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_MEMORY_ERROR)
100 | .withValue(resourceMetrics.getMutationsMemoryError()).save();
101 | context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_TIMED_OUT)
102 | .withValue(resourceMetrics.getMutationsTimedOut()).save();
103 | context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_UNKNOWN)
104 | .withValue(resourceMetrics.getMutationsUnknown()).save();
105 | context.newMeasure().on(resource).forMetric(MutationMetrics.MUTATIONS_DETECTED)
106 | .withValue(resourceMetrics.getMutationsDetected()).save();
107 | context.newMeasure().on(resource).forMetric(MutationMetrics.TEST_TOTAL_EXECUTED)
108 | .withValue(resourceMetrics.getNumTestsRun()).save();
109 | }
110 | }
111 |
112 | }
113 |
--------------------------------------------------------------------------------
/src/main/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/TestMetricsWriter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis.sensors;
22 |
23 | import static org.slf4j.LoggerFactory.getLogger;
24 |
25 | import ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics;
26 | import ch.devcon5.sonar.plugins.mutationanalysis.metrics.ResourceMutationMetrics;
27 | import ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant;
28 | import ch.devcon5.sonar.plugins.mutationanalysis.model.TestDescriptor;
29 | import java.util.Collection;
30 | import java.util.List;
31 | import java.util.Map;
32 | import java.util.stream.Collectors;
33 | import org.slf4j.Logger;
34 | import org.sonar.api.batch.fs.FileSystem;
35 | import org.sonar.api.batch.sensor.SensorContext;
36 |
37 | /**
38 | *
39 | */
40 | public class TestMetricsWriter {
41 |
42 | private static final Logger LOG = getLogger(TestMetricsWriter.class);
43 | private final ResourceResolver resourceResolver;
44 |
45 | public TestMetricsWriter(final FileSystem fileSystem) {
46 | this.resourceResolver = new ResourceResolver(fileSystem);
47 | }
48 |
49 | public void writeMetrics(final Collection metrics, final SensorContext context, final Collection globalMutants) {
50 | Map> testKills = metrics.stream()
51 | .flatMap(rmm -> rmm.getMutants().stream())
52 | .collect(Collectors.groupingBy(m -> new TestDescriptor(m.getKillingTest()), Collectors.toList()));
53 |
54 | final int total = globalMutants.isEmpty() ? sumTotal(metrics) : globalMutants.size();
55 | testKills.forEach((t, m) -> {
56 | LOG.debug("Test {} kills {} mutants ", t.getClassName(), m.size());
57 | this.resourceResolver.resolve(t.getClassName()).ifPresent(f -> {
58 | context.newMeasure().forMetric(MutationMetrics.TEST_KILLS).on(f).withValue(m.size()).save();
59 | context.newMeasure().forMetric(MutationMetrics.UTILITY_GLOBAL_MUTATIONS).on(f).withValue(total).save();
60 | });
61 | });
62 | }
63 |
64 | private int sumTotal(final Collection metrics) {
65 | return (int) metrics.stream().mapToLong(ResourceMutationMetrics::getMutationsTotal).sum();
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/ARGUMENT_PROPAGATION.html:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
Argument Propagation Mutator (ARGUMENT_PROPAGATION)
23 |
24 |
25 | This operator replaces the return value of a method parameter, whose type matches the return type, with the
26 | value of the parameter. Hence, it is only applicable to non-void methods.
27 |
28 |
29 |
For example:
30 |
31 |
32 | public String aMethod() {
33 | String aValue = "example";
34 | return bMethod(aValue);
35 | }
36 |
37 | private String bMethod(String param) {
38 | return param + " not mutated";
39 | }
40 |
41 |
42 | is mutated to
43 |
44 |
45 | public String mutatedMethod() {
46 | String aValue = "example";
47 | return aValue;
48 | }
49 |
50 |
see Pitest Mutators
51 |
52 |
--------------------------------------------------------------------------------
/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/BOOLEAN_FALSE_RETURN.html:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
Boolean False Return Mutator (BOOLEAN_FALSE_RETURN)
23 |
24 |
This operator replaces a primitive or boxed boolean with false.
25 |
26 |
--------------------------------------------------------------------------------
/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/BOOLEAN_TRUE_RETURN.html:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
Boolean True Return Mutator (BOOLEAN_TRUE_RETURN)
23 |
24 |
This operator replaces a primitive or boxed boolean with true.
25 |
26 |
--------------------------------------------------------------------------------
/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/CONDITIONALS_BOUNDARY.html:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
Conditionals Boundary Mutator (CONDITIONALS_BOUNDARY)
23 |
24 |
25 | This operator replaces relational operators <, <=, >, >=
with their conditional boundary
26 | counterparts according to the following table
27 |
28 |
29 |
30 |
31 |
32 |
33 | Original
34 | |
35 |
36 | Mutation
37 | |
38 |
39 |
40 |
41 |
42 | < |
43 | <= |
44 |
45 |
46 | <= |
47 | < |
48 |
49 |
50 | > |
51 | >= |
52 |
53 |
54 | >= |
55 | > |
56 |
57 |
58 |
59 |
60 |
For example
61 |
62 |
63 | if (a <= b) {
64 | // some code
65 | }
66 |
67 |
68 |
will be mutated to
69 |
70 |
71 | if (a < b) {
72 | // someCode
73 | }
74 |
75 |
see Pitest Mutators
76 |
77 |
--------------------------------------------------------------------------------
/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/CONSTRUCTOR_CALLS.html:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
Constructor Call Mutator (CONSTRUCTOR_CALLS)
23 |
24 |
25 | This operator targets constructors, where it replaces the call to the constructor with a null
value
26 | assignment. This should induce NullPointerException
.
27 |
28 |
29 |
For Example:
30 |
31 |
32 | public Object aMethod() {
33 | Example x = new Example();
34 | return x;
35 | }
36 |
37 |
38 |
will be mutated to
39 |
40 |
41 | public Example aMethod() {
42 | Example x = null;
43 | return x;
44 | }
45 |
46 |
47 |
48 | This mutation is very likely to cause NullPointerException
s even in test suites
49 | that do not explicitly test for null
values. Nevertheless, even if the test contains no assertions
50 | at all, if the test runs this code, it is likely to kill this mutation.
51 |
52 |
53 |
54 | Every other method invocation is not affected by this operator. See Void Method Call Mutator, which mutates
56 | void methods, and
57 | Non Void Method Call Mutator
58 | mutating non-void methods.
59 |
60 |
see Pitest Mutators
61 |
62 |
--------------------------------------------------------------------------------
/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EMPTY_RETURN_VALUES.html:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
Empty Return Values Mutator (EMPTY_RETURN_VALUES)
23 |
24 |
25 | This operator mutates a return value, which is not a primitive, to return an empty value that relates to the
26 | Object's type.
27 |
28 |
29 |
For example:
30 |
31 |
32 | - 0 - Integer
33 | - Optional.empty()
34 | - "" - String
35 | - empty List
36 |
37 |
38 |
--------------------------------------------------------------------------------
/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_INLINE_CONSTS.html:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
Experimental Inline Constant Mutator (EXPERIMENTAL_INLINE_CONSTS)
23 |
24 |
This experimental operator aims to provide a more consistent behavior than the inline const operator.
25 |
26 |
27 | This operator increments the value by 1. In some cases this would lead to equivalent mutations due to JVM
28 | internals, i.e. for floating point types adding 1 would lead to equivalent mutations.
29 |
30 |
31 |
32 |
33 |
34 | Type |
35 | Mutation |
36 |
37 |
38 |
39 |
40 | boolean |
41 |
42 |
43 | - replace
true with false ,
44 | - replace
false with true
45 |
46 | |
47 |
48 | int byte short |
49 |
50 |
51 | - replace
1 with 0 ,
52 | - replace
-1 with 0 ,
53 | - all other values are incremented by 1.
54 | Byte.MAX , Short.MAX and Integer.MAX are replaced with
55 | Byte.MIN , Short.MIN and Integer.MIN
56 |
57 | |
58 |
59 |
60 | long |
61 |
62 |
63 | 1 is replaced with 0
64 | - all other values are incremented by 1
65 |
66 | |
67 |
68 |
69 | float |
70 |
71 |
72 | - replace
0.0 with 1.0 ,
73 | - replace
1.0 with 2.0 ,
74 | - replace any other value with
1.0
75 |
76 | |
77 |
78 |
79 | double |
80 |
81 |
82 | - replace
0.0 with 1.0 ,
83 | - replace
1.0 with 2.0 ,
84 | - replace any other value with
1.0
85 |
86 | |
87 |
88 |
89 |
90 |
see Pitest Mutators
91 |
92 |
--------------------------------------------------------------------------------
/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_MEMBER_VARIABLE.html:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
Experimental Member Variable Mutator (EXPERIMENTAL_MEMBER_VARIABLE)
23 |
24 |
25 | This operator mutates assignments to member variables, including final members, by removing the assignments.
26 | Member variables will be initialized with their Java default values, depending on the type of the variable.
27 |
28 |
29 |
Java Default Values for Primitives and Reference Types
30 |
31 |
32 |
33 |
34 | Type |
35 | Default Value |
36 |
37 |
38 |
39 |
40 |
41 | boolean
42 | |
43 | false |
44 |
45 |
46 |
47 | int
48 | short
49 | byte
50 | long
51 | |
52 | 0 |
53 |
54 |
55 |
56 | float
57 | double
58 | |
59 | 0.0 |
60 |
61 |
62 |
63 | char
64 | |
65 | '\u0000' |
66 |
67 |
68 |
69 | Object
70 | |
71 | null |
72 |
73 |
74 |
75 |
76 |
For Example:
77 |
78 |
79 | public class Example {
80 | private final int a = 10;
81 | private String text = "abc";
82 | //...
83 | }
84 |
85 |
86 |
will be mutated to
87 |
88 |
89 | public class Example {
90 | private final int a = 0;
91 | private String text = null;
92 | ...
93 | }
94 |
95 |
96 |
97 | In cases where the member variable is explicitly initialized with a default value this operator will create an
98 | equivalent mutation that can not be killed.
99 |
100 |
101 |
102 | public class EquivalentExample {
103 | private boolean b = false;
104 | }
105 |
106 |
see Pitest Mutators
107 |
108 |
--------------------------------------------------------------------------------
/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_NAKED_RECEIVER.html:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
Experimental Naked Receiver Mutator (EXPERIMENTAL_NAKED_RECEIVER)
23 |
24 |
25 | This operator replaces a call to a method with the receiver of the method (the instance on which the method
26 | is invoked), if the return type of the method is the same as the receiver.
27 |
28 |
29 |
For example:
30 |
31 |
32 | public String originalMethod() {
33 | String someString = "original";
34 | return someString.toUpperCase();
35 | }
36 |
37 | is mutated to
38 |
39 | public int mutatedMethod() {
40 | String someString = "original";
41 | return someString;
42 | }
43 |
44 | as the return type of
toUpperCase()
is the same as the type of
someString
45 |
46 |
--------------------------------------------------------------------------------
/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_REMOVE_INCREMENTS.html:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
Experimental Remove Increments Mutator (EXPERIMENTAL_REMOVE_INCREMENTS)
23 |
24 |
25 | This operator removes an increment operation. As the number of loop iterations are typically controlled by an
26 | incremented variable, this operator may lead to infinite loops causing timeouts.
27 |
28 |
29 |
--------------------------------------------------------------------------------
/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_REMOVE_SWITCH.html:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
Experimental Remove Switch Mutator (EXPERIMENTAL_SWITCH)
23 |
24 |
This operator removes an entire switch statement and replaces it with the default label.
25 |
see Pitest Mutators
26 |
27 |
--------------------------------------------------------------------------------
/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_RETURN_VALUES.html:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
Experimental Return Values (EXPERIMENTAL_RETURN_VALUES)
23 |
24 |
25 | This operator mutates the return values of method calls depending on the return type.
26 |
27 |
28 |
29 | Primitive types are replaces with rules similar to rules of the inline consts operator. Object reference
30 | replacements are more complex and handled by the ObjectReferenceReplacer
.
31 |
32 |
see Pitest Mutators
33 |
34 |
--------------------------------------------------------------------------------
/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/EXPERIMENTAL_SWITCH.html:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
Experimental Switch Mutator (EXPERIMENTAL_SWITCH)
23 |
24 |
25 | This operator replaces the default label with the first label that is different from the default label, and replaces
26 | all other labels with the default label.
27 |
28 |
see Pitest Mutators
29 |
30 |
--------------------------------------------------------------------------------
/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/INCREMENTS.html:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
Increments Mutator (INCREMENTS)
23 |
24 |
25 | This operator replaces increments with decrements and vice versa. It mutates increments, decrements, assignment
26 | increments and decrements of stack variables (local variables).
27 |
28 |
29 |
For example
30 |
31 |
32 | public int aMethod(int i) {
33 | i--;
34 | return i;
35 | }
36 |
37 |
38 |
will be mutated to
39 |
40 |
41 | public int aMethod(int i) {
42 | i++;
43 | return i;
44 | }
45 |
46 |
47 |
48 | Only increments and decrements of local variables are mutated. Member variables and the increments and
49 | decrements thereof will be covered by the Math Mutator.
50 |
51 |
see Pitest Mutators
52 |
53 |
--------------------------------------------------------------------------------
/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/INLINE_CONSTS.html:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
Inline Constant Mutator (INLINE_CONSTS)
23 |
24 |
25 | This operator mutates inline constants assigned to variables. An inline constant is a fixed value - a literal -
26 | that is assigned to a non-final variable.
27 |
28 |
29 |
For Example:
30 |
31 |
32 | public void aMethod() {
33 | long l = 16L;
34 | // some coding that uses l
35 | }
36 |
37 |
38 |
39 | The actual mutation depends on the type of the inline constant. Because the way Java statements get converted
40 | to byte code may vary, the rule for mutating inline constant are complex.
41 |
42 |
43 |
44 |
45 |
46 | Type |
47 | Mutation |
48 |
49 |
50 |
51 |
52 | boolean |
53 |
54 | true is replaced with false and
55 | false is replaced with true
56 | |
57 |
58 |
59 | integer byte short |
60 |
61 |
62 | 1 is replaced with 0
63 | -1 is replaced with 1
64 | 5 is replaced with -1
65 | - all other values are incremented by 1
66 |
67 | |
68 |
69 |
70 | long |
71 |
72 |
73 | 1 is replaced with 0
74 | - all other values are incremented by 1
75 |
76 | |
77 |
78 |
79 | float |
80 |
81 |
82 | 1.0 is replaced with 0.0
83 | 2.0 is replaced with 0.0
84 | - any other value is replaced with
1.0
85 |
86 | |
87 |
88 |
89 | double |
90 |
91 |
92 | 1.0 is replaced with 0.0
93 | - any other value is replaced with
1.0
94 |
95 | |
96 |
97 |
98 |
99 |
100 |
For example
101 |
102 |
103 | public int aMethod() {
104 | int i = 16;
105 | return i;
106 | }
107 |
108 |
109 |
will be changed to
110 |
111 |
112 | public int aMethod() {
113 | int i = 17;
114 | return i;
115 | }
116 |
117 |
118 |
119 | The compiler may optimize expressions with final variables - regardless of being local or member variables.
120 | In those cases the mutation is not able to mutate any variable.
121 |
122 |
Example
123 |
124 |
125 | public class Example {
126 | private static final int CONSTANT = 4;
127 |
128 | public String aMethod() {
129 | final int i = 16;
130 | return "" + CONSTANT + ":" + i;
131 | }
132 | }
133 |
134 |
135 |
might get optimized to
136 |
137 |
138 | public class Example {
139 | public String aMethod() {
140 | return "4:16";
141 | }
142 | }
143 |
144 |
see Pitest Mutators
145 |
146 |
--------------------------------------------------------------------------------
/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/INVERT_NEGS.html:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
Invert Negatives Mutator (INVERT_NEGS)
23 |
24 |
This operator mutates an integer or float number by negation.
25 |
26 |
For example
27 |
28 |
29 | public float doNegate(final int i) {
30 | return -i;
31 | }
32 |
33 |
34 |
will be mutated to
35 |
36 |
37 | public float doNegate(final int i) {
38 | return i;
39 | }
40 |
41 |
see Pitest Mutators
42 |
43 |
--------------------------------------------------------------------------------
/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/MATH.html:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
Math Mutator (MATH)
23 |
24 |
25 | This operator mutates binary arithmetic operations for integer or floating point arithmetic by replacing one
26 | operator with another. Operation according the following ruleset.
27 |
28 |
29 |
30 |
31 |
32 |
33 | Original operation
34 | |
35 |
36 | Mutated operation
37 | |
38 |
39 |
40 |
41 |
42 | + |
43 | - |
44 |
45 |
46 | - |
47 | + |
48 |
49 |
50 | * |
51 | / |
52 |
53 |
54 | / |
55 | * |
56 |
57 |
58 | % |
59 | * |
60 |
61 |
62 | & |
63 | | |
64 |
65 |
66 | | |
67 | & |
68 |
69 |
70 | ^ |
71 | & |
72 |
73 |
74 | << |
75 | >> |
76 |
77 |
78 | >> |
79 | << |
80 |
81 |
82 | >>> |
83 | <<< |
84 |
85 |
86 |
87 |
88 |
Example
89 |
90 |
91 | int a = b + c;
92 |
93 |
94 |
will be mutated to
95 |
96 |
97 | int a = b - c;
98 |
99 |
100 |
101 | The +
operator on String
s is overloaded and is not mutated by this operator.
102 |
103 |
104 |
105 | String a = "a" + "Text";
106 |
107 |
108 |
109 | is a string concatenation and not a mathematical operator. The compiler replaces this concatenation with something like
110 |
111 |
112 |
113 | String a = new StringBuilder("a").append("Text").toString();
114 |
115 |
116 |
117 | For increments, decrements, assignment increments, and assignemnt decrements of member variables (non-local) the
118 | compiler will also use binary arithmetic operations. Although a special iinc
opcode exists for
119 | increments, but it is restricted to local variables (stack variables) and is not applicable to member variables.
120 | Hence, the math mutators will also mutate.
121 |
122 | Please note that the compiler will also use binary arithmetic operations for increments, decrements and
123 | assignment increments and decrements of non-local variables (member variables) although a special iinc
124 |
opcode for increments exists. This special opcode is restricted to local variables (also called stack
125 | variables) and cannot be used for member variables. That means the math operator will also mutate.
126 |
127 |
128 |
129 | public class Example {
130 | private int a;
131 |
132 | public void inc() {
133 | this.a++;
134 | }
135 | }
136 |
137 |
138 |
to
139 |
140 |
141 | public class Example {
142 | private int a;
143 |
144 | public void inc() {
145 | this.a = this.a - 1;
146 | }
147 | }
148 |
149 |
see Pitest Mutators
150 |
151 |
--------------------------------------------------------------------------------
/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/NEGATE_CONDITIONALS.html:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
Negate Conditionals Mutator (NEGATE_CONDITIONALS)
23 |
24 |
This operator mutates conditional expressions negation.
25 |
26 |
27 |
28 |
29 |
30 | Original
31 | |
32 |
33 | Mutated
34 | |
35 |
36 |
37 |
38 |
39 | == |
40 | != |
41 |
42 |
43 | != |
44 | == |
45 |
46 |
47 | <= |
48 | > |
49 |
50 |
51 | >= |
52 | < |
53 |
54 |
55 | < |
56 | >= |
57 |
58 |
59 | > |
60 | <= |
61 |
62 |
63 |
64 |
65 |
66 |
Example
67 |
68 |
69 | if (a != b) {
70 | // some code
71 | }
72 |
73 |
74 |
will be mutated to
75 |
76 |
77 | if (a == b) {
78 | // some code
79 | }
80 |
81 |
82 |
or
83 |
84 |
85 | if (a <= b) {
86 | // some code
87 | }
88 |
89 |
90 |
will be mutated to
91 |
92 |
93 | if (a > b) {
94 | // some code
95 | }
96 |
97 |
98 |
99 | There is an overlap with the conditional boundary operator. The conditionals negation however is more unstable and
100 | is therefore easier to be killed by a test.
101 |
102 |
see Pitest Mutators
103 |
104 |
--------------------------------------------------------------------------------
/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/NON_VOID_METHOD_CALLS.html:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
Non Void Method Call Mutator (NON_VOID_METHOD_CALLS)
23 |
24 |
25 | This operator mutates calls to a method with a non-void return value. Instead of the return value the
26 | default value according the following table is used instead.
27 |
28 |
29 |
Default Values for Primitives and Reference Types
30 |
31 |
32 |
33 |
34 | Type |
35 | Default Value |
36 |
37 |
38 |
39 |
40 | boolean |
41 | false |
42 |
43 |
44 | int byte short long |
45 | 0 |
46 |
47 |
48 | float double |
49 | 0.0 |
50 |
51 |
52 | char |
53 | '\u0000' |
54 |
55 |
56 | Object |
57 | null |
58 |
59 |
60 |
61 |
62 |
Example
63 |
64 |
65 | public long aMethod() {
66 | return 5L;
67 | }
68 |
69 | public void bMethod() {
70 | long l = aMethod();
71 | // do something l
72 | }
73 |
74 |
75 |
will be mutated to
76 |
77 |
78 | public long aMethod() {
79 | return 5L;
80 | }
81 |
82 | public void foo() {
83 | int l = 0L;
84 | // do something with l
85 | }
86 |
87 |
88 |
or
89 |
90 |
91 | public Object aMethod() {
92 | return new Object();
93 | }
94 |
95 | public void bMethod() {
96 | Object o = bMethod();
97 | // do something with o
98 | }
99 |
100 |
101 |
will be mutated to
102 |
103 |
104 | public Object aMethod() {
105 | return new Object();
106 | }
107 |
108 | public void bMethod() {
109 | Object o = null;
110 | // do something o
111 | }
112 |
113 |
114 |
115 | This operator produces mutations that are fairly easy to detect by a test, especially when Reference types
116 | are mutated causing NullPointerException
s. Further, it may create mutations that are an equivalent
117 | of the original code because the original code already returns a default value, that make it impossible to be
118 | detected by a test.
119 |
120 |
121 |
122 | This operator does not affect void methods or constructor calls. See
123 | Void Method Call Mutator for mutations of
124 | void methods and
125 | Constructor Call Mutator for mutations of
126 | constructors.
127 |
128 |
see Pitest Mutators
129 |
130 |
--------------------------------------------------------------------------------
/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/NULL_RETURN_VALUES.html:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
Null Return Values Mutator (NULL_RETURN_VALUES)
23 |
24 |
This operator mutates all non-primitive return values by replacing them with null
.
25 |
26 | As this mutator produces mutations that are very likely to be unstable - causing a NullPointerException that
27 | is even found by trivial tests - this operator does not mutate the return type, if a more stable return
28 | value mutation exists.
29 |
30 | It does further not mutate methods that are annotated with
@NotNull
31 |
32 |
--------------------------------------------------------------------------------
/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/PRIMITIVE_RETURN_VALS.html:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
Primitive Return Values Mutator (PRIMITIVE_RETURN_VALS)
23 |
24 |
This operator mutates all number primitive return values with 0 or 0.0.
25 |
26 |
--------------------------------------------------------------------------------
/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/REMOVE_CONDITIONALS.html:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
Remove Conditionals Mutator (REMOVE_CONDITIONALS)
23 |
24 |
25 | This operator will replace all conditional expressions so that they always evaluate to true, so the path guarded
26 | by the condition will always be executed.
27 |
28 |
29 |
For Example
30 |
31 |
32 | if (a != b) {
33 | // some code
34 | }
35 |
36 |
37 |
will be mutated to
38 |
39 |
40 | if (true) {
41 | // some code
42 | }
43 |
44 |
45 |
46 | This operator is not enabled by default. Nevertheless, it is recommended to enable this operator to get full
47 | coverage on conditional expressions
48 |
49 |
see Pitest Mutators
50 |
51 |
--------------------------------------------------------------------------------
/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/RETURN_VALS.html:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
Return Values Mutator (RETURN_VALS)
23 |
24 |
25 | This operator mutates method returns of non-void return values. The actual mutation applied depends on the
26 | return type, according the following table:
27 |
28 |
29 |
30 |
31 |
32 | Return Type |
33 | Mutation Function |
34 | Description |
35 |
36 |
37 |
38 |
39 | boolean |
40 | f(x) -> !x |
41 |
42 | true is replaced with false and
43 | false is replaced with true
44 | |
45 |
46 |
47 |
48 | int byte short |
49 |
50 | f(x) -> x == 0 ? 1 : 0
51 | |
52 |
53 | 0 is mutated to 1 and
54 | any other value is mutated to 0
55 | |
56 |
57 |
58 | long |
59 | f(x) -> x+1 |
60 |
61 | the original value is incremented by 1, for example 5 -> 6
62 | |
63 |
64 |
65 | float double |
66 | f(x) -> x == NAN ? 0 : -(x+1.0) |
67 |
68 | if the value is NAN it is replaced with 0, otherwise - if it is a number - it is
69 | replaced with the negated increment of the value
70 | |
71 |
72 |
73 | Object |
74 | f(x) -> x == null ? java.lang.RuntimeException : null |
75 |
76 | if the return value would be null a java.lang.RuntimeException is thrown,
77 | otherwise null is returned instead of the original value
78 | |
79 |
80 |
81 |
82 |
83 |
For Example
84 |
85 |
86 | public boolean aMethod() {
87 | return false;
88 | }
89 |
90 |
91 |
will be mutated to
92 |
93 |
94 | public boolean aMethod() {
95 | return true;
96 | }
97 |
98 |
99 |
or
100 |
101 |
102 | public Object aMethod() {
103 | return new Object();
104 | }
105 |
106 |
107 |
will be mutated to
108 |
109 |
110 | public Object aMethod() {
111 | new Object();
112 | return null;
113 | }
114 |
115 |
see Pitest Mutators
116 |
117 |
--------------------------------------------------------------------------------
/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/VOID_METHOD_CALLS.html:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
Void Method Call Mutator (VOIDMETHODCALLS)
23 |
24 |
25 | This operator mutates methods without a return value - void methods - that do not produce an explicit result that
26 | is assigned to a variable. This could be a call of a setter or a message to an external system or underlying
27 | component for which the effects requires more effort to verify. This operator removes such method calls.
28 |
29 |
30 |
31 | public void aMethod(String s) {
32 | // some Code
33 | }
34 |
35 | public String bMethod() {
36 | String s = "example";
37 | aMethod(s);
38 | return s;
39 | }
40 |
41 |
42 |
will be changed to
43 |
44 |
45 | public void aMethod(String s) {
46 | // someCode
47 | }
48 |
49 | public String bMethod() {
50 | String s = "example";
51 | return s;
52 | }
53 |
54 |
55 |
56 | Constructor calls are not considered as method calls and are therefore not affected by this operator.
57 | To mutate constructor calls, refer to the
58 | Constructor Call Mutator or the
59 | Non Void Method Call Mutator if you
60 | want to mutate method calls that produce a return value.
61 |
62 |
see Pitest Mutators
63 |
64 |
--------------------------------------------------------------------------------
/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/model/empty.html:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 | no description yet
23 |
24 |
--------------------------------------------------------------------------------
/src/main/resources/ch/devcon5/sonar/plugins/mutationanalysis/rules.xml:
--------------------------------------------------------------------------------
1 |
2 |
21 |
22 |
23 |
24 | mutant.survived
25 | Survived Mutants
26 |
27 | Survived mutant
28 | A mutant of a class has not been killed although appearing to be covered by a test.
29 | If you don't want to activate any mutation operator specific rule, you may use this rule which is triggered by
30 | any Mutator. You should not activate this rule, when you use mutation operator specific rules, as it may result
31 | in redundant issued.
32 |
33 | For more information check out the PIT documentation
34 |
35 | This rule is deprecated and is likely to be removed in future releases. Instead of this rule, use one of
36 | the mutation specific rules.
37 |
38 | ]]>
39 |
40 | DEPRECATED
41 | BUG
42 | aggregate
43 | pitest
44 | test
45 | test-quality
46 | mutation
47 |
48 |
49 | mutant.lurking
50 | Lurking Mutants
51 |
52 | Uncovered mutant
54 | A mutant of a class has not been covered by a test at all.
55 | An uncovered mutant is also a survived mutant. If you activate this rule, it will produce more issues
56 | than the Survived Mutant rule. It's advised to have either this or the Survived Mutant rule activated, but
57 | not both, as it will result in duplicate issues.Further it is not recommended to have this rule activated
58 | with any of the mutation operator specific rules.
59 | For more information check out the PIT documentation
60 |
61 | This rule is deprecated and is likely to be removed in future releases. Instead of this rule, use one of
62 | the mutation specific rules.
63 |
64 | ]]>
65 |
66 | DEPRECATED
67 | BUG
68 | aggregate
69 | pitest
70 | test
71 | test-quality
72 | mutation
73 |
74 |
75 | mutant.unknownStatus
76 | Mutant with unknown Status
77 |
78 | This rule is deprecated and is likely to be removed in future releases. Instead of this rule, use one of
80 | the mutation specific rules.
81 |
82 | ]]>
83 |
84 | MINOR
85 | DEPRECATED
86 | BUG
87 | aggregate
88 | pitest
89 | test
90 | test-quality
91 | mutation
92 |
93 |
94 | mutant.coverage
95 | Mutation Coverage below threshold
96 |
97 |
98 |
99 | pitest
100 | test
101 | test-quality
102 | mutation
103 | coverage
104 |
105 | mutant.coverage.threshold
106 |
107 |
110 |
111 | 80.0
112 |
113 |
114 |
115 |
--------------------------------------------------------------------------------
/src/main/resources/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/MutationAnalysisPluginTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis;
22 |
23 | import static org.junit.jupiter.api.Assertions.assertFalse;
24 | import static org.junit.jupiter.api.Assertions.assertTrue;
25 |
26 | import ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationAnalysisMetrics;
27 | import ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationDensityComputer;
28 | import ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationScoreComputer;
29 | import ch.devcon5.sonar.plugins.mutationanalysis.metrics.QuantitativeMeasureComputer;
30 | import ch.devcon5.sonar.plugins.mutationanalysis.metrics.TestKillRatioComputer;
31 | import ch.devcon5.sonar.plugins.mutationanalysis.metrics.TotalMutationsComputer;
32 | import ch.devcon5.sonar.plugins.mutationanalysis.report.PitestReportParser;
33 | import ch.devcon5.sonar.plugins.mutationanalysis.report.ReportFinder;
34 | import ch.devcon5.sonar.plugins.mutationanalysis.rules.JavaProfileDefinition;
35 | import ch.devcon5.sonar.plugins.mutationanalysis.rules.JavaRulesDefinition;
36 | import ch.devcon5.sonar.plugins.mutationanalysis.rules.KotlinProfileDefinition;
37 | import ch.devcon5.sonar.plugins.mutationanalysis.rules.KotlinRulesDefinition;
38 | import ch.devcon5.sonar.plugins.mutationanalysis.sensors.PitestSensor;
39 | import ch.devcon5.sonar.plugins.mutationanalysis.testharness.TestConfiguration;
40 | import org.junit.jupiter.api.Test;
41 | import org.sonar.api.Plugin;
42 | import org.sonar.api.SonarEdition;
43 | import org.sonar.api.SonarQubeSide;
44 | import org.sonar.api.SonarRuntime;
45 | import org.sonar.api.internal.SonarRuntimeImpl;
46 | import org.sonar.api.utils.Version;
47 |
48 | class MutationAnalysisPluginTest {
49 |
50 | private final SonarRuntime sonarRuntime = SonarRuntimeImpl.forSonarQube(
51 | Version.create(8, 9), SonarQubeSide.SCANNER, SonarEdition.COMMUNITY);
52 |
53 |
54 | @Test
55 | void testDefine() {
56 | MutationAnalysisPlugin plugin = new MutationAnalysisPlugin();
57 | Plugin.Context context = new Plugin.Context(sonarRuntime);
58 |
59 | plugin.define(context);
60 |
61 | assertTrue(context.getExtensions().contains(PitestReportParser.class));
62 | assertTrue(context.getExtensions().contains(PitestReportParser.class));
63 | assertTrue(context.getExtensions().contains(ReportFinder.class));
64 | assertTrue(context.getExtensions().contains(JavaRulesDefinition.class));
65 | assertTrue(context.getExtensions().contains(KotlinRulesDefinition.class));
66 | assertTrue(context.getExtensions().contains(JavaProfileDefinition.class));
67 | assertTrue(context.getExtensions().contains(KotlinProfileDefinition.class));
68 | assertTrue(context.getExtensions().contains(PitestSensor.class));
69 | assertTrue(context.getExtensions().contains(MutationAnalysisMetrics.class));
70 | assertTrue(context.getExtensions().contains(MutationScoreComputer.class));
71 | assertTrue(context.getExtensions().contains(MutationDensityComputer.class));
72 | assertTrue(context.getExtensions().contains(TotalMutationsComputer.class));
73 | assertTrue(context.getExtensions().contains(TestKillRatioComputer.class));
74 | assertTrue(context.getExtensions().contains(QuantitativeMeasureComputer.class));
75 | }
76 |
77 | @Test
78 | void experimentalFeaturesEnabled_default_false() {
79 | TestConfiguration configuration = new TestConfiguration();
80 |
81 | assertFalse(MutationAnalysisPlugin.isExperimentalFeaturesEnabled(configuration));
82 | }
83 |
84 | @Test
85 | void experimentalFeaturesEnabled_configured_true() {
86 | TestConfiguration configuration = new TestConfiguration();
87 | configuration.set("dc5.mutationAnalysis.experimentalFeatures.enabled", true);
88 |
89 | assertTrue(MutationAnalysisPlugin.isExperimentalFeaturesEnabled(configuration));
90 | }
91 |
92 | }
93 |
--------------------------------------------------------------------------------
/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/StreamsTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis;
22 |
23 | import static org.junit.jupiter.api.Assertions.assertEquals;
24 | import static org.junit.jupiter.api.Assertions.assertFalse;
25 | import static org.junit.jupiter.api.Assertions.assertTrue;
26 |
27 | import java.util.Arrays;
28 | import java.util.stream.Stream;
29 | import org.junit.jupiter.api.Test;
30 |
31 | class StreamsTest {
32 |
33 | @Test
34 | void sequentialStream() {
35 | Stream stream = Streams.sequentialStream(Arrays.asList("one", "two"));
36 | assertFalse(stream.isParallel());
37 | assertEquals(2, stream.count());
38 | }
39 |
40 | @Test
41 | void parallelStream() {
42 | Stream stream = Streams.parallelStream(Arrays.asList("one", "two"));
43 | assertTrue(stream.isParallel());
44 | assertEquals(2, stream.count());
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationAnalysisMetricsTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis.metrics;
22 |
23 |
24 | import static org.junit.jupiter.api.Assertions.assertFalse;
25 | import static org.junit.jupiter.api.Assertions.assertNotNull;
26 |
27 | import java.util.List;
28 | import org.junit.jupiter.api.Test;
29 | import org.sonar.api.measures.Metric;
30 |
31 | /**
32 | * Mutation Analysis Metrics Tests
33 | */
34 | class MutationAnalysisMetricsTest {
35 |
36 | @Test
37 | void testGetMetrics() {
38 | final List metrics = new MutationAnalysisMetrics().getMetrics();
39 | assertNotNull(metrics);
40 | assertFalse(metrics.isEmpty());
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationDensityComputerTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis.metrics;
22 |
23 | import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.MUTATIONS_DENSITY_KEY;
24 | import static ch.devcon5.sonar.plugins.mutationanalysis.metrics.MutationMetrics.MUTATIONS_TOTAL_KEY;
25 | import static org.junit.jupiter.api.Assertions.assertEquals;
26 | import static org.junit.jupiter.api.Assertions.assertNull;
27 | import static org.junit.jupiter.api.Assertions.assertTrue;
28 | import static org.sonar.api.measures.CoreMetrics.LINES_TO_COVER_KEY;
29 |
30 | import ch.devcon5.sonar.plugins.mutationanalysis.testharness.MeasureComputerTestHarness;
31 | import java.util.Arrays;
32 | import java.util.Collections;
33 | import org.junit.jupiter.api.BeforeEach;
34 | import org.junit.jupiter.api.Test;
35 | import org.sonar.api.ce.measure.Measure;
36 | import org.sonar.api.ce.measure.MeasureComputer;
37 | import org.sonar.api.ce.measure.test.TestMeasureComputerContext;
38 | import org.sonar.api.ce.measure.test.TestMeasureComputerDefinitionContext;
39 |
40 | /**
41 | * Mutation Density Computer Tests
42 | */
43 | class MutationDensityComputerTest {
44 |
45 | private MeasureComputerTestHarness harness;
46 | private MutationDensityComputer computer;
47 |
48 | @BeforeEach
49 | public void setUp() throws Exception {
50 | this.harness = MeasureComputerTestHarness.createFor(MutationDensityComputer.class);
51 | this.computer = harness.getComputer();
52 | }
53 |
54 | @Test
55 | void define() {
56 | final TestMeasureComputerDefinitionContext context = new TestMeasureComputerDefinitionContext();
57 |
58 | final MeasureComputer.MeasureComputerDefinition def = computer.define(context);
59 |
60 | assertTrue(def.getInputMetrics()
61 | .containsAll(Arrays.asList(MUTATIONS_TOTAL_KEY, LINES_TO_COVER_KEY)));
62 | assertTrue(def.getOutputMetrics().containsAll(Collections.singletonList(MUTATIONS_DENSITY_KEY)));
63 | }
64 |
65 | @Test
66 | void compute_experimentalFeaturesDisabled_noMeasure() {
67 | harness.enableExperimentalFeatures(false);
68 |
69 | final TestMeasureComputerContext measureContext = harness.createMeasureContextForSourceFile("compKey");
70 |
71 | measureContext.addInputMeasure(MUTATIONS_TOTAL_KEY, 30);
72 | measureContext.addInputMeasure(LINES_TO_COVER_KEY, 20);
73 |
74 | computer.compute(measureContext);
75 |
76 | assertNull(measureContext.getMeasure(MUTATIONS_DENSITY_KEY));
77 | }
78 |
79 | @Test
80 | void compute_noMutations_noMeasure() {
81 | final TestMeasureComputerContext measureContext = harness.createMeasureContextForSourceFile("compKey");
82 |
83 | computer.compute(measureContext);
84 |
85 | assertNull(measureContext.getMeasure(MUTATIONS_DENSITY_KEY));
86 | }
87 |
88 | @Test
89 | void compute_withInputMeausresMutations_correctOutputMeasure() {
90 | final TestMeasureComputerContext measureContext = harness.createMeasureContextForSourceFile("compKey");
91 |
92 | measureContext.addInputMeasure(MUTATIONS_TOTAL_KEY, 30);
93 | measureContext.addInputMeasure(LINES_TO_COVER_KEY, 20);
94 |
95 | computer.compute(measureContext);
96 |
97 | Measure density = measureContext.getMeasure(MUTATIONS_DENSITY_KEY);
98 | assertEquals(150.0, density.getDoubleValue(), 0.05);
99 | }
100 |
101 | @Test
102 | void compute_ZeroMutationsAndZeroLines_noDensityMetric() {
103 | final TestMeasureComputerContext measureContext = harness.createMeasureContextForSourceFile("compKey");
104 |
105 | measureContext.addInputMeasure(MUTATIONS_TOTAL_KEY, 0);
106 | measureContext.addInputMeasure(LINES_TO_COVER_KEY, 0);
107 |
108 | computer.compute(measureContext);
109 |
110 | assertNull(measureContext.getMeasure(MUTATIONS_DENSITY_KEY));
111 | }
112 |
113 | @Test
114 | void compute_noLineMetrics_densitySetToZero() {
115 | final TestMeasureComputerContext measureContext = harness.createMeasureContextForSourceFile("compKey");
116 |
117 | measureContext.addInputMeasure(MUTATIONS_TOTAL_KEY, 30);
118 |
119 | computer.compute(measureContext);
120 |
121 | Measure density = measureContext.getMeasure(MUTATIONS_DENSITY_KEY);
122 | assertEquals(0.0, density.getDoubleValue(), 0.05);
123 | }
124 |
125 | }
126 |
--------------------------------------------------------------------------------
/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/metrics/MutationMetricsTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis.metrics;
22 |
23 | import static org.junit.jupiter.api.Assertions.assertFalse;
24 | import static org.junit.jupiter.api.Assertions.assertNotNull;
25 |
26 | import java.io.Serializable;
27 | import java.util.List;
28 | import org.junit.jupiter.api.Test;
29 | import org.sonar.api.measures.Metric;
30 |
31 | class MutationMetricsTest {
32 |
33 | @Test
34 | void testMetricConstants() {
35 | assertNotNull(MutationMetrics.MUTATIONS_COVERAGE);
36 | assertNotNull(MutationMetrics.MUTATIONS_TEST_STRENGTH);
37 | assertNotNull(MutationMetrics.MUTATIONS_DATA);
38 | assertNotNull(MutationMetrics.MUTATIONS_DETECTED);
39 | assertNotNull(MutationMetrics.MUTATIONS_KILLED);
40 | assertNotNull(MutationMetrics.MUTATIONS_MEMORY_ERROR);
41 | assertNotNull(MutationMetrics.MUTATIONS_NO_COVERAGE);
42 | assertNotNull(MutationMetrics.MUTATIONS_SURVIVED);
43 | assertNotNull(MutationMetrics.MUTATIONS_TIMED_OUT);
44 | assertNotNull(MutationMetrics.MUTATIONS_TOTAL);
45 | assertNotNull(MutationMetrics.MUTATIONS_UNKNOWN);
46 | }
47 |
48 | @Test
49 | void testGetQuantitativeMetrics() {
50 | final List> metrics = MutationMetrics.getQuantitativeMetrics();
51 | assertNotNull(metrics);
52 | assertFalse(metrics.isEmpty());
53 | }
54 |
55 | @Test
56 | void testGetSensorMetrics() {
57 | final List> metrics = MutationMetrics.getSensorMetrics();
58 | assertNotNull(metrics);
59 | assertFalse(metrics.isEmpty());
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/MutationOperatorsTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis.model;
22 |
23 | import static org.junit.jupiter.api.Assertions.assertEquals;
24 | import static org.junit.jupiter.api.Assertions.assertFalse;
25 | import static org.junit.jupiter.api.Assertions.assertNotNull;
26 |
27 | import java.util.Collection;
28 | import java.util.HashSet;
29 | import org.junit.jupiter.api.Test;
30 | import org.junit.jupiter.params.ParameterizedTest;
31 | import org.junit.jupiter.params.provider.ValueSource;
32 |
33 | /**
34 | * Mutation Operators Test
35 | */
36 | class MutationOperatorsTest {
37 |
38 | @ParameterizedTest
39 | @ValueSource(strings = {
40 | "ARGUMENT_PROPAGATION",
41 | "org.pitest.mutationtest.engine.gregor.mutators.ArgumentPropagationMutator",
42 | "org.pitest.mutationtest.engine.gregor.mutators.experimental.ArgumentPropagationMutator",
43 | "org.pitest.mutationtest.engine.gregor.mutators.ArgumentPropagationMutator_WITH_SUFFIX",
44 | "org.pitest.mutationtest.engine.gregor.mutators.experimental.ArgumentPropagationMutator_WITH_SUFFIX"
45 | })
46 | void testFind_knownMutator_byID(String mutagenKey) {
47 | final MutationOperator mutationOperator = MutationOperators.find(mutagenKey);
48 | assertNotNull(mutationOperator);
49 | assertEquals("ARGUMENT_PROPAGATION", mutationOperator.getId());
50 | assertEquals(new HashSet() {{
51 | add("org.pitest.mutationtest.engine.gregor.mutators.ArgumentPropagationMutator");
52 | add("org.pitest.mutationtest.engine.gregor.mutators.experimental.ArgumentPropagationMutator");
53 | }}, mutationOperator.getClassNames());
54 | assertNotNull(mutationOperator.getViolationDescription());
55 | }
56 |
57 | @Test
58 | void testAllMutators() {
59 | // act
60 | final Collection mutationOperators = MutationOperators.allMutationOperators();
61 |
62 | // assert
63 | assertNotNull(mutationOperators);
64 | assertFalse(mutationOperators.isEmpty());
65 | assertEquals(23, mutationOperators.size());
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/StateTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis.model;
22 |
23 | import static org.junit.jupiter.api.Assertions.assertEquals;
24 | import static org.junit.jupiter.api.Assertions.assertFalse;
25 | import static org.junit.jupiter.api.Assertions.assertTrue;
26 |
27 | import org.junit.jupiter.api.Test;
28 |
29 | class StateTest {
30 |
31 | @Test
32 | void testIsAlive_enumValues() {
33 | assertTrue(Mutant.State.NO_COVERAGE.isAlive());
34 | assertTrue(Mutant.State.UNKNOWN.isAlive());
35 | assertTrue(Mutant.State.SURVIVED.isAlive());
36 | assertFalse(Mutant.State.MEMORY_ERROR.isAlive());
37 | assertFalse(Mutant.State.TIMED_OUT.isAlive());
38 | assertFalse(Mutant.State.KILLED.isAlive());
39 | }
40 |
41 | @Test
42 | void testParse_enumValues() {
43 | assertEquals(Mutant.State.NO_COVERAGE, Mutant.State.parse("NO_COVERAGE"));
44 | assertEquals(Mutant.State.KILLED, Mutant.State.parse("KILLED"));
45 | assertEquals(Mutant.State.SURVIVED, Mutant.State.parse("SURVIVED"));
46 | assertEquals(Mutant.State.MEMORY_ERROR, Mutant.State.parse("MEMORY_ERROR"));
47 | assertEquals(Mutant.State.TIMED_OUT, Mutant.State.parse("TIMED_OUT"));
48 | assertEquals(Mutant.State.UNKNOWN, Mutant.State.parse("UNKNOWN"));
49 | }
50 |
51 | @Test
52 | void testParse_null_unknown() {
53 | assertEquals(Mutant.State.UNKNOWN, Mutant.State.parse(null));
54 | }
55 |
56 | @Test
57 | void testParse_unknown_unknown() {
58 | assertEquals(Mutant.State.UNKNOWN, Mutant.State.parse("xxx"));
59 | }
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/model/TestDescriptorTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis.model;
22 |
23 | import static org.junit.jupiter.api.Assertions.assertEquals;
24 | import static org.junit.jupiter.api.Assertions.assertNotEquals;
25 |
26 | import java.util.Objects;
27 | import org.junit.jupiter.api.Test;
28 | import org.junit.jupiter.params.ParameterizedTest;
29 | import org.junit.jupiter.params.provider.ValueSource;
30 |
31 | /**
32 | * Test Descriptor Tests
33 | */
34 | class TestDescriptorTest {
35 |
36 | @ParameterizedTest
37 | @ValueSource(strings = {
38 | "some.package.ClassName.testMethod(some.package.ClassName)",
39 | "some.package.ClassName",
40 | "some.package.ClassName$SubTest.testMethod(some.package.ClassName$SubTest)",
41 | "some.package.ClassName$SubTest"
42 | })
43 | void standardClassOrNested_methodOrNot(String spec) {
44 | TestDescriptor td = new TestDescriptor(spec);
45 | assertEquals("some.package.ClassName", td.getClassName());
46 | assertEquals(spec.contains("testMethod") ? "testMethod" : "unknown", td.getMethodName());
47 | }
48 |
49 | @Test
50 | void unknownSpec() {
51 | TestDescriptor td = new TestDescriptor("some/package/ClassName");
52 | assertEquals("some/package/ClassName", td.getSpec());
53 | assertEquals("some/package/ClassName", td.getClassName());
54 | assertEquals("unknown", td.getMethodName());
55 | }
56 |
57 | @Test
58 | void getSpec() {
59 | TestDescriptor td = new TestDescriptor("some.package.ClassName.testMethod(some.package.ClassName)");
60 | assertEquals("some.package.ClassName.testMethod(some.package.ClassName)", td.getSpec());
61 | }
62 |
63 | @Test
64 | void test_toString() {
65 | TestDescriptor td = new TestDescriptor("some.package.ClassName.testMethod(some.package.ClassName)");
66 | assertEquals("TestDescriptor{class='some.package.ClassName', method='testMethod'}", td.toString());
67 | }
68 |
69 | @Test
70 | void test_equals() {
71 | TestDescriptor td1 = new TestDescriptor("some.package.ClassName.testMethod_one(some.package.ClassName)");
72 | TestDescriptor td2 = new TestDescriptor("some.package.ClassName.testMethod_two(some.package.ClassName)");
73 | assertEquals(td1, td2);
74 | }
75 |
76 | @Test
77 | void test_equals_sameRef() {
78 | TestDescriptor td1 = new TestDescriptor("some.package.ClassName.testMethod_one(some.package.ClassName)");
79 | assertEquals(td1, td1);
80 | }
81 |
82 | @Test
83 | void test_equals_null() {
84 | TestDescriptor td1 = new TestDescriptor("some.package.ClassName.testMethod_one(some.package.ClassName)");
85 | assertNotEquals(null, td1);
86 | }
87 |
88 | @Test
89 | void test_equals_differentClass() {
90 | TestDescriptor td1 = new TestDescriptor("some.package.ClassName.testMethod_one(some.package.ClassName)");
91 | assertNotEquals(new Object(), td1);
92 | }
93 |
94 | @Test
95 | void test_equals_not() {
96 | TestDescriptor td1 = new TestDescriptor("some.package.ClassName.testMethod_one(some.package.ClassName)");
97 | TestDescriptor td2 = new TestDescriptor("some.otherpackage.ClassName.testMethod_two(some.package.ClassName)");
98 | assertNotEquals(td1, td2);
99 | }
100 |
101 | @Test
102 | void test_hashCode() {
103 | TestDescriptor td = new TestDescriptor("some.package.ClassName.testMethod_one(some.package.ClassName)");
104 | assertEquals(Objects.hash("some.package.ClassName"), td.hashCode());
105 | }
106 |
107 | }
108 |
--------------------------------------------------------------------------------
/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/report/ReportsTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis.report;
22 |
23 |
24 | import static org.junit.jupiter.api.Assertions.assertEquals;
25 | import static org.junit.jupiter.api.Assertions.assertNotNull;
26 | import static org.junit.jupiter.api.Assertions.assertTrue;
27 |
28 | import ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant;
29 | import java.io.File;
30 | import java.io.IOException;
31 | import java.nio.file.Files;
32 | import java.nio.file.Path;
33 | import java.util.Collection;
34 | import org.apache.commons.io.IOUtils;
35 | import org.junit.jupiter.api.Test;
36 | import org.junit.jupiter.api.io.TempDir;
37 |
38 | class ReportsTest {
39 |
40 | @TempDir
41 | public Path folder;
42 |
43 | @Test
44 | void testReadMutants_fromDirectory_noReport() throws Exception {
45 | // act
46 | final Collection mutants = Reports.readMutants(folder);
47 |
48 | // assert
49 | assertNotNull(mutants);
50 | assertTrue(mutants.isEmpty());
51 | }
52 |
53 | @Test
54 | void testReadMutants_fromDirectory_withReport() throws Exception {
55 | // prepare
56 | fileFromResource("ReportsTest_mutations.xml", "mutations.xml");
57 |
58 | // act
59 | final Collection mutants = Reports.readMutants(folder);
60 |
61 | // assert
62 | assertNotNull(mutants);
63 | assertEquals(3, mutants.size());
64 | }
65 |
66 | @Test
67 | void testReadMutants_fromFile() throws Exception {
68 | // prepare
69 | final File file = fileFromResource("ReportsTest_mutations.xml", "mutations.xml");
70 |
71 | // act
72 | final Collection mutants = Reports.readMutants(file.toPath());
73 |
74 | // assert
75 | assertNotNull(mutants);
76 | assertEquals(3, mutants.size());
77 | }
78 |
79 | private File fileFromResource(final String resourcePath, final String fileName) throws IOException {
80 | final File newFile = Files.createFile(folder.resolve(fileName)).toFile();
81 | IOUtils.copy(getClass().getResourceAsStream(resourcePath), Files.newOutputStream(newFile.toPath()));
82 | return newFile;
83 | }
84 |
85 | }
86 |
--------------------------------------------------------------------------------
/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/JavaProfileDefinitionTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis.rules;
22 |
23 | import static ch.devcon5.sonar.plugins.mutationanalysis.rules.MutationAnalysisRulesDefinition.REPOSITORY_KEY;
24 | import static org.junit.jupiter.api.Assertions.assertEquals;
25 |
26 | import org.junit.jupiter.api.Test;
27 | import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition;
28 | import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition.BuiltInQualityProfile;
29 |
30 | class JavaProfileDefinitionTest {
31 |
32 | private final BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context();
33 |
34 | @Test
35 | void define_java() {
36 | MutationAnalysisProfileDefinition def = new JavaProfileDefinition();
37 |
38 | def.define(context);
39 |
40 | BuiltInQualityProfile javaProfile = context.profile("java", "Mutation Analysis");
41 |
42 | assertEquals(23, javaProfile.rules().stream().filter(r -> (REPOSITORY_KEY + ".java").equals(r.repoKey())).count());
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/JavaRulesDefinitionTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 | package ch.devcon5.sonar.plugins.mutationanalysis.rules;
21 |
22 | import static ch.devcon5.sonar.plugins.mutationanalysis.rules.MutationAnalysisRulesDefinition.MUTANT_RULES_PREFIX;
23 | import static org.junit.jupiter.api.Assertions.assertEquals;
24 | import static org.junit.jupiter.api.Assertions.assertNotNull;
25 | import static org.junit.jupiter.api.Assertions.assertTrue;
26 |
27 | import ch.devcon5.sonar.plugins.mutationanalysis.testharness.TestConfiguration;
28 | import java.util.List;
29 | import org.junit.jupiter.api.BeforeEach;
30 | import org.junit.jupiter.api.Test;
31 | import org.sonar.api.config.Configuration;
32 | import org.sonar.api.rule.RuleStatus;
33 | import org.sonar.api.rules.RuleType;
34 | import org.sonar.api.server.rule.RulesDefinition;
35 | import org.sonar.api.server.rule.RulesDefinition.Context;
36 | import org.sonar.api.server.rule.RulesDefinitionXmlLoader;
37 |
38 | class JavaRulesDefinitionTest {
39 |
40 | private final Configuration configuration = new TestConfiguration();
41 |
42 | private JavaRulesDefinition subject;
43 |
44 | @BeforeEach
45 | public void setUp() throws Exception {
46 | subject = new JavaRulesDefinition(configuration, new RulesDefinitionXmlLoader());
47 | }
48 |
49 | @Test
50 | void testDefine() {
51 | // prepare
52 | Context context = new RulesDefinition.Context();
53 |
54 | // act
55 | subject.define(context);
56 |
57 | // assert
58 | RulesDefinition.Repository repository = context.repository(
59 | MutationAnalysisRulesDefinition.REPOSITORY_KEY + ".java");
60 | assertNotNull(repository);
61 |
62 | assertEquals("java", repository.language());
63 | assertEquals(MutationAnalysisRulesDefinition.REPOSITORY_NAME, repository.name());
64 | assertRules(repository.rules());
65 | }
66 |
67 | private void assertRules(final List rules) {
68 | assertEquals(50, rules.size());
69 | for (RulesDefinition.Rule rule : rules) {
70 | assertNotNull(rule.debtRemediationFunction());
71 | assertNotNull(rule.gapDescription());
72 | assertNotNull(rule.htmlDescription());
73 | }
74 |
75 | //@ denotes a toString'ed object reference
76 | //"null " would be exactly 5 characters
77 | assertTrue(rules.stream()
78 | .filter(r -> r.key().startsWith(MUTANT_RULES_PREFIX) && r.key().endsWith("CODE_SMELL"))
79 | .allMatch(r -> r.name().matches("[^@]{6,}\\(Code Smell\\)")));
80 |
81 | assertTrue(rules.stream()
82 | .filter(r -> r.key().startsWith(MUTANT_RULES_PREFIX) && !r.key().endsWith("CODE_SMELL"))
83 | .noneMatch(r -> r.name().matches("[^@]{6,}\\(Code Smell\\)")));
84 |
85 | //all mutator rules
86 | assertEquals(26,
87 | rules.stream()
88 | .filter(rule -> rule.key().startsWith(MUTANT_RULES_PREFIX))
89 | .filter(rule -> RuleType.BUG.equals(rule.type()))
90 | .count());
91 | assertEquals(24,
92 | rules.stream()
93 | .filter(rule -> rule.key().startsWith(MUTANT_RULES_PREFIX))
94 | .filter(rule -> RuleType.CODE_SMELL.equals(rule.type()))
95 | .count());
96 | assertEquals(34,
97 | rules.stream()
98 | .filter(rule -> rule.status() == RuleStatus.READY)
99 | .filter(RulesDefinition.Rule::activatedByDefault)
100 | .count());
101 | assertEquals(12,
102 | rules.stream()
103 | .filter(rule -> rule.status() == RuleStatus.BETA)
104 | .filter(rule -> !rule.activatedByDefault())
105 | .count());
106 | assertEquals(3,
107 | rules.stream()
108 | .filter(rule -> rule.status() == RuleStatus.DEPRECATED)
109 | .filter(rule -> !rule.activatedByDefault())
110 | .count());
111 | }
112 |
113 | }
114 |
--------------------------------------------------------------------------------
/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/KotlinProfileDefinitionTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis.rules
22 |
23 | import ch.devcon5.sonar.plugins.mutationanalysis.rules.MutationAnalysisRulesDefinition.REPOSITORY_KEY
24 | import org.junit.jupiter.api.Assertions.assertEquals
25 | import org.junit.jupiter.api.Test as test
26 | import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition
27 |
28 |
29 | class KotlinProfileDefinitionTest {
30 |
31 | val context = BuiltInQualityProfilesDefinition.Context()
32 |
33 | @test
34 | fun define() {
35 | val def = KotlinProfileDefinition()
36 |
37 | def.define(context)
38 |
39 | val kotlinProfile = context.profile("kotlin", "Mutation Analysis")
40 |
41 | assertEquals(23, kotlinProfile.rules().stream().filter { r -> "$REPOSITORY_KEY.kotlin" == r.repoKey() }.count())
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/rules/KotlinRulesDefinitionTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis.rules
22 |
23 | import ch.devcon5.sonar.plugins.mutationanalysis.rules.MutationAnalysisRulesDefinition.MUTANT_RULES_PREFIX
24 | import ch.devcon5.sonar.plugins.mutationanalysis.testharness.TestConfiguration
25 | import org.junit.jupiter.api.Assertions.assertEquals
26 | import org.junit.jupiter.api.Assertions.assertNotNull
27 | import org.sonar.api.config.Configuration
28 | import org.sonar.api.rule.RuleStatus
29 | import org.sonar.api.rules.RuleType
30 | import org.sonar.api.server.rule.RulesDefinition
31 | import org.sonar.api.server.rule.RulesDefinitionXmlLoader
32 | import org.junit.jupiter.api.BeforeEach as BeforeTest
33 | import org.junit.jupiter.api.Test as test
34 |
35 |
36 | class KotlinRulesDefinitionTest {
37 |
38 |
39 | private val configuration: Configuration = TestConfiguration()
40 |
41 | private var subject: KotlinRulesDefinition? = null
42 |
43 | @BeforeTest
44 | @Throws(Exception::class)
45 | fun setUp() {
46 | subject = KotlinRulesDefinition(configuration, RulesDefinitionXmlLoader())
47 | }
48 |
49 | @test
50 | @Throws(Exception::class)
51 | fun testDefine() {
52 | // prepare
53 | val context = RulesDefinition.Context()
54 |
55 | // act
56 | subject!!.define(context)
57 |
58 | // assert
59 | val repository = context.repository(MutationAnalysisRulesDefinition.REPOSITORY_KEY + ".kotlin")
60 |
61 | assertEquals("kotlin", repository!!.language())
62 | assertEquals(MutationAnalysisRulesDefinition.REPOSITORY_NAME, repository.name())
63 | assertRules(repository.rules())
64 | }
65 |
66 | private fun assertRules(rules: List) {
67 | assertEquals(50, rules.size.toLong())
68 | for (rule in rules) {
69 | assertNotNull(rule.debtRemediationFunction())
70 | assertNotNull(rule.gapDescription())
71 | assertNotNull(rule.htmlDescription())
72 | }
73 |
74 | assertEquals(26, rules.stream()
75 | .filter { rule -> rule.key().startsWith(MUTANT_RULES_PREFIX) }
76 | .filter { rule -> RuleType.BUG == rule.type() }.count())
77 | assertEquals(24, rules.stream()
78 | .filter { rule -> rule.key().startsWith(MUTANT_RULES_PREFIX) }
79 | .filter { rule -> RuleType.CODE_SMELL == rule.type() }.count())
80 | assertEquals(12, rules.stream().filter { rule -> rule.status() == RuleStatus.BETA }.count())
81 | assertEquals(3, rules.stream().filter { rule -> rule.status() == RuleStatus.DEPRECATED }.count())
82 | }
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/sensors/ResourceResolverTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis.sensors;
22 |
23 | import static org.junit.jupiter.api.Assertions.assertEquals;
24 | import static org.junit.jupiter.api.Assertions.assertTrue;
25 |
26 | import ch.devcon5.sonar.plugins.mutationanalysis.testharness.SensorTestHarness;
27 | import java.nio.file.Path;
28 | import org.junit.jupiter.api.BeforeAll;
29 | import org.junit.jupiter.api.BeforeEach;
30 | import org.junit.jupiter.api.Test;
31 | import org.junit.jupiter.api.io.TempDir;
32 | import org.sonar.api.batch.fs.FileSystem;
33 |
34 | public class ResourceResolverTest {
35 |
36 | @TempDir
37 | public static Path folder;
38 |
39 | private static FileSystem FILESYSTEM;
40 |
41 | private ResourceResolver resolver;
42 |
43 | @BeforeAll
44 | public static void setUpFilesystem() throws Exception {
45 | SensorTestHarness harness = SensorTestHarness.builder().withTempFolder(folder).build();
46 |
47 | harness.createSourceFile("src/main/java/ch/example/java/", "Example.java");
48 | harness.createSourceFile("src/main/java/ch/example/java/", "Example$Nested.java");
49 | harness.createSourceFile("src/main/java/ch/example/java/", "Example$Nested$Nested.java");
50 | harness.createSourceFile("src/main/kotlin/ch/example/kotlin/", "Example.kt");
51 | harness.createSourceFile("src/main/kotlin/ch/example/kotlin/", "Example$Nested.kt");
52 | harness.createSourceFile("src/main/kotlin/ch/example/kotlin/", "Example$Nested$Nested.kt");
53 |
54 | FILESYSTEM = harness.createSensorContext().scanFiles().fileSystem();
55 | }
56 |
57 | @BeforeEach
58 | public void setUp() throws Exception {
59 | resolver = new ResourceResolver(FILESYSTEM);
60 | }
61 |
62 | @Test
63 | void resolve_javaFile() {
64 | assertTrue(resolver.resolve("ch.example.java.Example").isPresent());
65 | assertEquals("Example.java", resolver.resolve("ch.example.java.Example").get().filename());
66 | }
67 |
68 | @Test
69 | void resolve_nestedJavaClass() {
70 | assertTrue(resolver.resolve("ch.example.java.Example$Nested").isPresent());
71 | assertEquals("Example.java", resolver.resolve("ch.example.java.Example$Nested").get().filename());
72 | }
73 |
74 | @Test
75 | void resolve_deeplyNestedJavaClass() {
76 | assertTrue(resolver.resolve("ch.example.java.Example$Nested$Nested").isPresent());
77 | assertEquals("Example.java", resolver.resolve("ch.example.java.Example$Nested$Nested").get().filename());
78 | }
79 |
80 | @Test
81 | void resolve_kotlinClass() {
82 | assertTrue(resolver.resolve("ch.example.kotlin.Example").isPresent());
83 | assertEquals("Example.kt", resolver.resolve("ch.example.kotlin.Example").get().filename());
84 | }
85 |
86 | @Test
87 | void resolve_nestedKotlinClass() {
88 | assertTrue(resolver.resolve("ch.example.kotlin.Example$Nested").isPresent());
89 | assertEquals("Example.kt", resolver.resolve("ch.example.kotlin.Example$Nested").get().filename());
90 | }
91 |
92 | @Test
93 | void resolve_deeplyNestedKotlinClass() {
94 | assertTrue(resolver.resolve("ch.example.kotlin.Example$Nested$Nested").isPresent());
95 | assertEquals("Example.kt", resolver.resolve("ch.example.kotlin.Example$Nested$Nested").get().filename());
96 | }
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/LogRecordingAppender.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis.testharness;
22 |
23 | import java.util.List;
24 | import java.util.concurrent.CopyOnWriteArrayList;
25 | import org.apache.logging.log4j.Level;
26 | import org.apache.logging.log4j.LogManager;
27 | import org.apache.logging.log4j.core.LogEvent;
28 | import org.apache.logging.log4j.core.LoggerContext;
29 | import org.apache.logging.log4j.core.appender.AbstractAppender;
30 | import org.apache.logging.log4j.core.config.LoggerConfig;
31 |
32 | public class LogRecordingAppender extends AbstractAppender implements AutoCloseable {
33 |
34 | private final List events = new CopyOnWriteArrayList<>();
35 |
36 | public LogRecordingAppender() {
37 | super("listAppender", null, null);
38 | getLoggerConfig().addAppender(this, Level.ALL, null);
39 | this.start();
40 | }
41 |
42 | @Override
43 | public void append(final LogEvent logEvent) {
44 | events.add(logEvent.toImmutable());
45 | }
46 |
47 | public List getEvents() {
48 | return events;
49 | }
50 |
51 | private LoggerConfig getLoggerConfig() {
52 | final LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);
53 | return loggerContext.getConfiguration().getLoggerConfig("");
54 | }
55 |
56 | @Override
57 | public void close() throws Exception {
58 | getLoggerConfig().removeAppender(getName());
59 | this.stop();
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/MeasureComputerTestHarness.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis.testharness;
22 |
23 |
24 | import ch.devcon5.sonar.plugins.mutationanalysis.MutationAnalysisPlugin;
25 | import java.lang.reflect.Constructor;
26 | import java.lang.reflect.InvocationTargetException;
27 | import org.sonar.api.ce.measure.Component;
28 | import org.sonar.api.ce.measure.MeasureComputer;
29 | import org.sonar.api.ce.measure.test.TestComponent;
30 | import org.sonar.api.ce.measure.test.TestMeasureComputerContext;
31 | import org.sonar.api.ce.measure.test.TestMeasureComputerDefinitionContext;
32 | import org.sonar.api.ce.measure.test.TestSettings;
33 | import org.sonar.api.config.Configuration;
34 |
35 | /**
36 | *
37 | */
38 | public class MeasureComputerTestHarness {
39 |
40 | private final T computer;
41 | private final TestConfiguration config;
42 |
43 | public MeasureComputerTestHarness(final T computer) {
44 | this(computer, new TestConfiguration());
45 | }
46 |
47 | public MeasureComputerTestHarness(final T computer, final TestConfiguration configuration) {
48 | this.computer = computer;
49 | this.config = configuration;
50 | enableExperimentalFeatures(true);
51 | }
52 |
53 | public static MeasureComputerTestHarness createFor(Class computer) {
54 | try {
55 | final Constructor c = computer.getConstructor(Configuration.class);
56 | final TestConfiguration configuration = new TestConfiguration();
57 | return new MeasureComputerTestHarness<>(c.newInstance(configuration), configuration);
58 | } catch (NoSuchMethodException e) {
59 | try {
60 | return new MeasureComputerTestHarness<>(computer.getConstructor().newInstance());
61 | } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e1) {
62 | throw new RuntimeException("Could not create computer from default constructor", e1);
63 | }
64 | } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
65 | throw new RuntimeException("Could not create computer with configuration", e);
66 | }
67 | }
68 |
69 | public T getComputer() {
70 | return computer;
71 | }
72 |
73 | public void enableExperimentalFeatures(final boolean enabled) {
74 | config.set(MutationAnalysisPlugin.EXPERIMENTAL_FEATURE_ENABLED, enabled);
75 | }
76 |
77 | public TestConfiguration getConfig() {
78 | return config;
79 | }
80 |
81 | public TestMeasureComputerContext createMeasureContextForSourceFile(String componentKey) {
82 | return createMeasureContext(componentKey, Component.Type.FILE, "java", false);
83 | }
84 |
85 | public TestMeasureComputerContext createMeasureContextForUnitTest(String componentKey) {
86 | return createMeasureContext(componentKey, Component.Type.FILE, "java", true);
87 |
88 | }
89 |
90 | public TestMeasureComputerContext createMeasureContextForDirectory(String componentKey) {
91 | return createMeasureContext(componentKey, Component.Type.DIRECTORY, "java", false);
92 | }
93 |
94 | public TestMeasureComputerContext createMeasureContextForModule(String componentKey) {
95 | return createMeasureContext(componentKey, Component.Type.MODULE, "java", false);
96 | }
97 |
98 | public TestMeasureComputerContext createMeasureContextForProject(String componentKey) {
99 | return createMeasureContext(componentKey, Component.Type.PROJECT, "java", false);
100 | }
101 |
102 | public TestMeasureComputerContext createMeasureContext(String componentKey, Component.Type type) {
103 | return createMeasureContext(componentKey, type, "java", false);
104 | }
105 |
106 | public TestMeasureComputerContext createMeasureContext(String componentKey, Component.Type type, String language, boolean unitTest) {
107 | final TestMeasureComputerDefinitionContext context = new TestMeasureComputerDefinitionContext();
108 | final MeasureComputer.MeasureComputerDefinition def = computer.define(context);
109 | final TestSettings settings = new TestSettings();
110 |
111 | if (type == Component.Type.FILE) {
112 | return new TestMeasureComputerContext(
113 | new TestComponent(componentKey, type, new TestComponent.FileAttributesImpl(language, unitTest)), settings, def);
114 | }
115 | return new TestMeasureComputerContext(new TestComponent(componentKey, type, null), settings, def);
116 | }
117 |
118 | public TestMeasureComputerContext createMeasureContext(String componentKey, Component.Type type, boolean unitTest) {
119 | return createMeasureContext(componentKey, type, "java", unitTest);
120 | }
121 |
122 | }
123 |
--------------------------------------------------------------------------------
/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/SystemLocaleExtension.java:
--------------------------------------------------------------------------------
1 | package ch.devcon5.sonar.plugins.mutationanalysis.testharness;
2 |
3 | import java.util.Locale;
4 | import org.junit.jupiter.api.extension.AfterEachCallback;
5 | import org.junit.jupiter.api.extension.BeforeEachCallback;
6 | import org.junit.jupiter.api.extension.ExtensionContext;
7 |
8 | public class SystemLocaleExtension implements AfterEachCallback, BeforeEachCallback {
9 |
10 | private final Locale overrideLocale;
11 | private Locale defaultLocale = Locale.getDefault();
12 |
13 | public SystemLocaleExtension(Locale locale) {
14 | this.overrideLocale = locale;
15 | }
16 |
17 | public static SystemLocaleExtension overrideDefault(Locale locale) {
18 | return new SystemLocaleExtension(locale);
19 | }
20 |
21 | @Override
22 | public void beforeEach(ExtensionContext context) throws Exception {
23 | defaultLocale = Locale.getDefault();
24 | Locale.setDefault(overrideLocale);
25 | }
26 |
27 | @Override
28 | public void afterEach(ExtensionContext context) throws Exception {
29 | Locale.setDefault(defaultLocale);
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/test/java/ch/devcon5/sonar/plugins/mutationanalysis/testharness/TestConfiguration.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Mutation Analysis Plugin
3 | * Copyright (C) 2015-2018 DevCon5 GmbH, Switzerland
4 | * info@devcon5.ch
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU Lesser General Public
8 | * License as published by the Free Software Foundation; either
9 | * version 3 of the License, or (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | * Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program; if not, write to the Free Software Foundation,
18 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 | */
20 |
21 | package ch.devcon5.sonar.plugins.mutationanalysis.testharness;
22 |
23 | import java.util.Map;
24 | import java.util.Optional;
25 | import java.util.concurrent.ConcurrentHashMap;
26 | import org.sonar.api.config.Configuration;
27 |
28 | /**
29 | * Basic Implementation to be used in tests instead of using a mock
30 | */
31 | public class TestConfiguration implements Configuration {
32 |
33 | private final Map settings = new ConcurrentHashMap<>();
34 |
35 | public TestConfiguration() {}
36 |
37 | public TestConfiguration set(String key, Object value) {
38 | this.settings.put(key, String.valueOf(value));
39 | return this;
40 | }
41 |
42 | @Override
43 | public Optional get(final String key) {
44 | return Optional.ofNullable(settings.get(key));
45 | }
46 |
47 | @Override
48 | public boolean hasKey(final String key) {
49 | return settings.containsKey(key);
50 | }
51 |
52 | @Override
53 | public String[] getStringArray(final String key) {
54 | return get(key).map(v -> v.split(",")).orElse(new String[0]);
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/src/test/resources/ch/devcon5/sonar/plugins/mutationanalysis/report/PitestReportParserTest_XXE.xml:
--------------------------------------------------------------------------------
1 |
2 |
21 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | ]>
35 |
36 |
37 | Mutant.java
38 | ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant
39 | equals
40 | &xxe;
41 | 162
42 | org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator
43 | 5
44 | ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest.testEquals_different_false(ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest)
45 |
46 |
47 |
--------------------------------------------------------------------------------
/src/test/resources/ch/devcon5/sonar/plugins/mutationanalysis/report/PitestReportParserTest_broken.xml:
--------------------------------------------------------------------------------
1 |
2 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/test/resources/ch/devcon5/sonar/plugins/mutationanalysis/report/PitestReportParserTest_mutations.xml:
--------------------------------------------------------------------------------
1 |
2 |
21 |
22 |
23 |
24 | Mutant.java
25 | ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant
26 | equals
27 | (Ljava/lang/Object;)Z
28 | 162
29 | org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator
30 | 5
31 | ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest.testEquals_different_false(ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest)
32 |
33 |
34 | Mutant.java
35 | ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant
36 | equals
37 | (Ljava/lang/Object;)Z
38 | 172
39 | org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator
40 | 43
41 |
42 |
43 |
44 | Mutant.java
45 | ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant
46 | equals
47 | (Ljava/lang/Object;)Z
48 | 175
49 | org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator
50 | 55
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/src/test/resources/ch/devcon5/sonar/plugins/mutationanalysis/report/PitestReportParserTest_mutationsWithDescriptions.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Mutant.java
5 | ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant
6 | equals
7 | (Ljava/lang/Object;)Z
8 | 268
9 | org.pitest.mutationtest.engine.gregor.mutators.InlineConstantMutator
10 | 8
11 | 20
12 | ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest.testEquals_same_true(ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest)
13 | Substituted 1 with 0
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/test/resources/ch/devcon5/sonar/plugins/mutationanalysis/report/PitestReportParserTest_mutationsWithNumTests.xml:
--------------------------------------------------------------------------------
1 |
2 |
21 |
22 |
23 |
24 | Mutant.java
25 | ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant
26 | equals
27 | (Ljava/lang/Object;)Z
28 | 162
29 | org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator
30 | 5
31 | ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest.testEquals_different_false(ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest)
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/test/resources/ch/devcon5/sonar/plugins/mutationanalysis/report/ReportFinderTest_mutations.xml:
--------------------------------------------------------------------------------
1 |
2 |
21 |
22 |
23 |
24 | Mutant.java
25 | ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant
26 | equals
27 | (Ljava/lang/Object;)Z
28 | 162
29 | org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator
30 | 5
31 |
32 | ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest.testEquals_different_false(org.sonar.plugins.pitest.model.MutantTest)
33 |
34 |
35 |
36 | Mutant.java
37 | ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant
38 | equals
39 | (Ljava/lang/Object;)Z
40 | 172
41 | org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator
42 | 43
43 |
44 |
45 |
46 | Mutant.java
47 | ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant
48 | equals
49 | (Ljava/lang/Object;)Z
50 | 175
51 | org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator
52 | 55
53 |
54 |
55 |
56 | Mutant.java
57 | ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant
58 | equals
59 | (Ljava/lang/Object;)Z
60 | 175
61 | org.pitest.mutationtest.engine.gregor.mutators.ArgumentPropagationMutator
62 | 55
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/src/test/resources/ch/devcon5/sonar/plugins/mutationanalysis/report/ReportsTest_mutations.xml:
--------------------------------------------------------------------------------
1 |
2 |
21 |
22 |
23 |
24 | Mutant.java
25 | ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant
26 | equals
27 | (Ljava/lang/Object;)Z
28 | 162
29 | org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator
30 | 5
31 |
32 | ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest.testEquals_different_false(org.sonar.plugins.pitest.model.MutantTest)
33 |
34 |
35 |
36 | Mutant.java
37 | ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant
38 | equals
39 | (Ljava/lang/Object;)Z
40 | 172
41 | org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator
42 | 43
43 |
44 |
45 |
46 | Mutant.java
47 | ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant
48 | equals
49 | (Ljava/lang/Object;)Z
50 | 175
51 | org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator
52 | 55
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/src/test/resources/ch/devcon5/sonar/plugins/mutationanalysis/sensors/PitestSensorTest_Java_mutations.xml:
--------------------------------------------------------------------------------
1 |
2 |
21 |
22 |
23 |
24 | Mutant.java
25 | ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant
26 | equals
27 | (Ljava/lang/Object;)Z
28 | 162
29 | org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator
30 | 5
31 |
32 | ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest.testEquals_different_false(org.sonar.plugins.pitest.model.MutantTest)
33 |
34 |
35 |
36 | Mutant.java
37 | ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant
38 | equals
39 | (Ljava/lang/Object;)Z
40 | 172
41 | org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator
42 | 43
43 |
44 |
45 |
46 | Mutant.java
47 | ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant
48 | equals
49 | (Ljava/lang/Object;)Z
50 | 175
51 | org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator_WITH_SUFFIX
52 | 55
53 |
54 |
55 |
56 | Mutant.java
57 | ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant
58 | equals
59 | (Ljava/lang/Object;)Z
60 | 160
61 | org.pitest.mutationtest.engine.gregor.mutators.ConditionalsBoundaryMutator
62 | 5
63 |
64 | ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest.testEquals_different_false(org.sonar.plugins.pitest.model.MutantTest)
65 |
66 |
67 |
68 | Mutant.java
69 | ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant
70 | equals
71 | (Ljava/lang/Object;)Z
72 | 164
73 | org.pitest.mutationtest.engine.gregor.mutators.ConditionalsBoundaryMutator_WITH_SUFFIX
74 | 5
75 |
76 | ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest.testEquals_different_false(org.sonar.plugins.pitest.model.MutantTest)
77 |
78 |
79 |
80 | Mutant.java
81 | ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant
82 | equals
83 | (Ljava/lang/Object;)Z
84 | 175
85 | org.pitest.mutationtest.engine.gregor.mutators.ArgumentPropagationMutator
86 | 55
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/src/test/resources/ch/devcon5/sonar/plugins/mutationanalysis/sensors/PitestSensorTest_KotlinJava_mutations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | KotlinExample.kt
5 | ch.devcon5.sonar.plugins.mutationanalysis.KotlinExample
6 | helloWorld
7 | ()V
8 | 28
9 | org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator
10 | 8
11 | 0
12 |
13 | negated conditional
14 |
15 |
16 | Mutant.java
17 | ch.devcon5.sonar.plugins.mutationanalysis.JavaExample
18 | equals
19 | (Ljava/lang/Object;)Z
20 | 162
21 | org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator
22 | 5
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/test/resources/ch/devcon5/sonar/plugins/mutationanalysis/sensors/PitestSensorTest_Kotlin_mutations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | KotlinExample.ktch.devcon5.sonar.plugins.mutationanalysis.KotlinExamplehelloWorld()V28org.pitest.mutationtest.engine.gregor.mutators.InlineConstantMutator40Substituted 2 with 3
4 | KotlinExample.ktch.devcon5.sonar.plugins.mutationanalysis.KotlinExamplehelloWorld()V28org.pitest.mutationtest.engine.gregor.mutators.MathMutator60Replaced long modulus with multiplication
5 | KotlinExample.ktch.devcon5.sonar.plugins.mutationanalysis.KotlinExamplehelloWorld()V28org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator80negated conditional
6 | KotlinExample.ktch.devcon5.sonar.plugins.mutationanalysis.KotlinExamplehelloWorld()V28org.pitest.mutationtest.engine.gregor.mutators.NonVoidMethodCallMutator30removed call to java/lang/System::currentTimeMillis
7 | KotlinExample.ktch.devcon5.sonar.plugins.mutationanalysis.KotlinExamplehelloWorld()V28org.pitest.mutationtest.engine.gregor.mutators.RemoveConditionalMutator_EQUAL_ELSE80removed conditional - replaced equality check with false
8 | KotlinExample.ktch.devcon5.sonar.plugins.mutationanalysis.KotlinExamplehelloWorld()V28org.pitest.mutationtest.engine.gregor.mutators.RemoveConditionalMutator_EQUAL_IF80removed conditional - replaced equality check with true
9 | KotlinExample.ktch.devcon5.sonar.plugins.mutationanalysis.KotlinExamplehelloWorld()V29org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator131removed call to java/io/PrintStream::print
10 | KotlinExample.ktch.devcon5.sonar.plugins.mutationanalysis.KotlinExamplemain()V34org.pitest.mutationtest.engine.gregor.mutators.ConstructorCallMutator52removed call to ch/devcon5/sonar/plugins/mutationanalysis/KotlinExample::<init>
11 | KotlinExample.ktch.devcon5.sonar.plugins.mutationanalysis.KotlinExamplemain()V34org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator62removed call to ch/devcon5/sonar/plugins/mutationanalysis/KotlinExample::helloWorld
12 |
13 |
--------------------------------------------------------------------------------
/src/test/resources/ch/devcon5/sonar/plugins/mutationanalysis/sensors/ReportCollectorTest_mutations.xml:
--------------------------------------------------------------------------------
1 |
2 |
21 |
22 |
23 |
24 | Mutant.java
25 | ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant
26 | equals
27 | (Ljava/lang/Object;)Z
28 | 162
29 | org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator
30 | 5
31 |
32 | ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest.testEquals_different_false(org.sonar.plugins.pitest.model.MutantTest)
33 |
34 |
35 |
36 | Mutant.java
37 | ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant
38 | equals
39 | (Ljava/lang/Object;)Z
40 | 172
41 | org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator
42 | 43
43 |
44 |
45 |
46 | Mutant.java
47 | ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant
48 | equals
49 | (Ljava/lang/Object;)Z
50 | 175
51 | org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator_WITH_SUFFIX
52 | 55
53 |
54 |
55 |
56 | Mutant.java
57 | ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant
58 | equals
59 | (Ljava/lang/Object;)Z
60 | 160
61 | org.pitest.mutationtest.engine.gregor.mutators.ConditionalsBoundaryMutator
62 | 5
63 |
64 | ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest.testEquals_different_false(org.sonar.plugins.pitest.model.MutantTest)
65 |
66 |
67 |
68 | Mutant.java
69 | ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant
70 | equals
71 | (Ljava/lang/Object;)Z
72 | 164
73 | org.pitest.mutationtest.engine.gregor.mutators.ConditionalsBoundaryMutator_WITH_SUFFIX
74 | 5
75 |
76 | ch.devcon5.sonar.plugins.mutationanalysis.model.MutantTest.testEquals_different_false(org.sonar.plugins.pitest.model.MutantTest)
77 |
78 |
79 |
80 | Mutant.java
81 | ch.devcon5.sonar.plugins.mutationanalysis.model.Mutant
82 | equals
83 | (Ljava/lang/Object;)Z
84 | 175
85 | org.pitest.mutationtest.engine.gregor.mutators.ArgumentPropagationMutator
86 | 55
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/src/test/resources/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------