diff --git a/.gitignore b/.gitignore index f322c19..3fcc4a0 100644 --- a/.gitignore +++ b/.gitignore @@ -127,3 +127,4 @@ work .vscode/ .DS_Store +.aider* diff --git a/README.md b/README.md index 9f8feec..a90feee 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Configure report path, e.g. `target/pit-reports/**/mutations.xml` for a Maven bu ## Jenkins Pipeline You can use the following step in pipeline to use this plugin in pipeline: -`pitmutation killRatioMustImprove: false, minimumKillRatio: 50.0, mutationStatsFile: '**/target/pit-reports/**/mutations.xml'` +`pitmutation ignoreMissingReports: false, killRatioMustImprove: false, minimumKillRatio: 50.0, mutationStatsFile: '**/target/pit-reports/**/mutations.xml'` The plugin needs the XML and HTML output from PIT. Also make sure that a clean target is executed before building, otherwise PIT will diff --git a/pom.xml b/pom.xml index 2b709c5..5c0a99f 100644 --- a/pom.xml +++ b/pom.xml @@ -9,14 +9,14 @@ pitmutation - 1.0-19-SNAPSHOT + 2.0.2 Jenkins PIT Mutation Plugin hpi http://wiki.jenkins-ci.org/display/JENKINS/pitmutation - scm:git:ssh://git@github.com/jenkinsci/pitmutation-plugin.git - scm:git:ssh://git@github.com/jenkinsci/pitmutation-plugin.git + scm:git:git://git@github.com/jenkinsci/pitmutation-plugin.git + scm:git:git://git@github.com/jenkinsci/pitmutation-plugin.git https://github.com/jenkinsci/pitmutation-plugin HEAD @@ -37,6 +37,12 @@ Benjamin Sproule benjamin@benjaminsproule.com + + vasilej + Vasile Jureschi + vasile.jureschi@gmail.com + + @@ -54,6 +60,19 @@ + + + com.fasterxml.jackson.core + jackson-databind + 2.14.1 + + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + 2.14.1 + + org.projectlombok lombok @@ -61,11 +80,6 @@ provided - - org.apache.commons - commons-digester3 - 3.2 - org.jenkins-ci symbol-annotation @@ -85,16 +99,16 @@ test - junit - junit - 4.13.2 + org.mockito + mockito-junit-jupiter + 5.4.0 + test + + + org.junit.jupiter + junit-jupiter-engine + 5.9.2 test - - - org.hamcrest - hamcrest-core - - @@ -129,8 +143,8 @@ org.apache.maven.plugins maven-compiler-plugin - 11 - 11 + 17 + 17 @@ -141,7 +155,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 8 + 17 @@ -164,6 +178,13 @@ org.pitest pitest-maven 1.16.1 + + + org.pitest + pitest-junit5-plugin + 1.1.1 + + org.jenkinsci.plugins.pitmutation.* diff --git a/src/main/java/org/jenkinsci/plugins/pitmutation/DescriptorImpl.java b/src/main/java/org/jenkinsci/plugins/pitmutation/DescriptorImpl.java new file mode 100644 index 0000000..e7a6cf2 --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/pitmutation/DescriptorImpl.java @@ -0,0 +1,44 @@ +package org.jenkinsci.plugins.pitmutation; + +import hudson.Extension; +import hudson.model.AbstractProject; +import hudson.tasks.BuildStepDescriptor; +import hudson.tasks.Publisher; +import net.sf.json.JSONObject; +import org.jenkinsci.Symbol; +import org.kohsuke.stapler.StaplerRequest; + +/** + * The type Descriptor. + */ +@Extension +@Symbol("pitmutation") +public class DescriptorImpl extends BuildStepDescriptor { + + /** + * Instantiates a new Descriptor. + */ + public DescriptorImpl() { + super(PitPublisher.class); + } + + @Override + public String getDisplayName() { + return Messages.PitPublisher_DisplayName(); + } + + @Override + public boolean isApplicable(Class aClass) { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean configure(StaplerRequest req, JSONObject formData) throws FormException { + req.bindParameters(this, "pitmutation"); + save(); + return super.configure(req, formData); + } +} diff --git a/src/main/java/org/jenkinsci/plugins/pitmutation/FileProcessor.java b/src/main/java/org/jenkinsci/plugins/pitmutation/FileProcessor.java new file mode 100644 index 0000000..1f8891c --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/pitmutation/FileProcessor.java @@ -0,0 +1,54 @@ +package org.jenkinsci.plugins.pitmutation; + +import hudson.FilePath; +import org.apache.commons.lang.StringUtils; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import static java.util.Optional.ofNullable; + +public class FileProcessor { + + public static final String SINGLE_MODULE_REPORT_FOLDER = "mutation-report-all"; + public static final String MULTI_MODULE_REPORT_FORMAT = "mutation-report-%s"; + + public void copySingleModuleReport(FilePath source, FilePath buildTarget) throws IOException, InterruptedException { + copyMutationReports(source, buildTarget, SINGLE_MODULE_REPORT_FOLDER); + } + + public void copyMultiModuleReport(FilePath source, FilePath buildTarget, String module) throws IOException, + InterruptedException { + copyMutationReports(source, buildTarget, String.format(MULTI_MODULE_REPORT_FORMAT, module)); + } + + private void copyMutationReports(FilePath source, FilePath buildTarget, String mutationFilePath) throws IOException, + InterruptedException { + var targetPath = new FilePath(buildTarget, mutationFilePath); + var parent = ofNullable(source.getParent()).orElseThrow(() -> new IOException("Mutation file not found")); + parent.copyRecursiveTo(targetPath); + } + + public Map getNames(FilePath[] reports, String base) { + Map names = new HashMap<>(); + for (int i = 0; i < reports.length; i++) { + FilePath report = reports[i]; + + final String moduleName; + if (StringUtils.isBlank(base)) { + moduleName = String.valueOf(i == 0 ? null : i); + } else { + String[] partsFromRemoteWithoutBase = report.getRemote().replace(base, "").split("[/\\\\]"); + if (partsFromRemoteWithoutBase.length > 1) { + moduleName = partsFromRemoteWithoutBase[1]; + } else { + moduleName = String.valueOf(i == 0 ? null : i); + } + } + + names.put(report, moduleName); + } + return names; + } +} \ No newline at end of file diff --git a/src/main/java/org/jenkinsci/plugins/pitmutation/MustImproveCondition.java b/src/main/java/org/jenkinsci/plugins/pitmutation/MustImproveCondition.java new file mode 100644 index 0000000..ec1ac96 --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/pitmutation/MustImproveCondition.java @@ -0,0 +1,22 @@ +package org.jenkinsci.plugins.pitmutation; + +import hudson.model.Result; +import org.jenkinsci.plugins.pitmutation.targets.MutationStats; + +import static hudson.model.Result.SUCCESS; +import static hudson.model.Result.UNSTABLE; + +class MustImproveCondition implements Condition { + @Override + public Result decideResult(final PitBuildAction action) { + PitBuildAction previousAction = action.getPreviousAction(); + if (previousAction != null) { + MutationStats previousStats = previousAction.getReport().getMutationStats(); + return action.getReport().getMutationStats().getKillPercent() >= previousStats.getKillPercent() ? + SUCCESS : + UNSTABLE; + } else { + return SUCCESS; + } + } +} diff --git a/src/main/java/org/jenkinsci/plugins/pitmutation/Mutation.java b/src/main/java/org/jenkinsci/plugins/pitmutation/Mutation.java index ff08537..7b2443f 100644 --- a/src/main/java/org/jenkinsci/plugins/pitmutation/Mutation.java +++ b/src/main/java/org/jenkinsci/plugins/pitmutation/Mutation.java @@ -2,7 +2,12 @@ import lombok.Data; +import java.util.ArrayList; +import java.util.List; + /** + * ` + * * @author edward */ @Data @@ -12,19 +17,36 @@ public class Mutation { private String sourceFile; private String mutatedClass; private String mutatedMethod; + private int numberOfTestsRun; private int lineNumber; private String mutator; - private int index; + /** + * @deprecated from 1 .9 the mutation library generates a list of indexes JENKINS-68990 + */ + @Deprecated(forRemoval = true, since = "1.9") + private Integer index; + private List indexes = new ArrayList<>(); private String killingTest; private String methodDescription; private String description; + /** + * @deprecated from 1 .9 the mutation library generates a list of blocks JENKINS-68990 + */ + @Deprecated(forRemoval = true, since = "1.9") private String block; + private List blocks = new ArrayList<>(); + + public Mutation() { + } public String getMutatorClass() { int lastDot = mutator.lastIndexOf('.'); String className = mutator.substring(lastDot + 1); - return className.endsWith("Mutator") - ? className.substring(0, className.length() - 7) - : className; + return className.endsWith("Mutator") ? className.substring(0, className.length() - 7) : className; } + + protected boolean canEqual(final Object other) { + return other instanceof Mutation; + } + } diff --git a/src/main/java/org/jenkinsci/plugins/pitmutation/MutationReport.java b/src/main/java/org/jenkinsci/plugins/pitmutation/MutationReport.java index 7597a1f..cf8150d 100644 --- a/src/main/java/org/jenkinsci/plugins/pitmutation/MutationReport.java +++ b/src/main/java/org/jenkinsci/plugins/pitmutation/MutationReport.java @@ -1,63 +1,49 @@ package org.jenkinsci.plugins.pitmutation; -import org.apache.commons.digester3.Digester; +import com.fasterxml.jackson.dataformat.xml.JacksonXmlModule; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; import org.jenkinsci.plugins.pitmutation.targets.MutationStats; import org.xml.sax.SAXException; import java.io.IOException; import java.io.InputStream; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** * @author edward + * @author vasile.jureschi */ public class MutationReport { private final Map> mutationsByPackage; + private final Map> mutationsByClass; private int killCount = 0; - public MutationReport() { + public MutationReport(InputStream xmlReport) throws IOException, SAXException { this.mutationsByClass = new HashMap<>(); this.mutationsByPackage = new HashMap<>(); - } - public static MutationReport create(InputStream xmlReport) throws IOException, SAXException { - return digestMutations(xmlReport); - } + // https://github.com/FasterXML/jackson-dataformat-xml/issues/219 + JacksonXmlModule module = new JacksonXmlModule(); + module.setDefaultUseWrapper(false); + XmlMapper xmlMapper = new XmlMapper(module); + Mutations mutations = xmlMapper.readValue(xmlReport, Mutations.class); - private static MutationReport digestMutations(InputStream input) throws IOException, SAXException { - Digester digester = new Digester(); - digester.addObjectCreate("mutations", MutationReport.class); - digester.addObjectCreate("mutations/mutation", Mutation.class); - digester.addSetNext("mutations/mutation", "addMutation", "org.jenkinsci.plugins.pitmutation.Mutation"); - digester.addSetProperties("mutations/mutation"); - digester.addSetNestedProperties("mutations/mutation"); - - MutationReport report = digester.parse(input); - report.mutationsByClass.forEach((className, mutations) -> { - String packageName = packageNameFromClass(className); - List existingPackageMutations = report.mutationsByPackage - .computeIfAbsent(packageName, k -> new ArrayList<>()); - mutations.stream().filter(mutation -> !existingPackageMutations.contains(mutation)) - .forEach(mutation -> report.mutationsByPackage.get(packageName).add(mutation)); - }); - return report; - } + mutations.getMutation().forEach(mutation -> { + mutationsByClass.computeIfAbsent(mutation.getMutatedClass(), k -> new ArrayList<>()).add(mutation); + if (mutation.isDetected()) { + killCount++; + } + mutationsByPackage + .computeIfAbsent(packageNameFromClass(mutation.getMutatedClass()), k -> new ArrayList<>()) + .add(mutation); - /** - * Called by digester. - * - * @param mutation {@link Mutation} to add - */ - public void addMutation(Mutation mutation) { - mutationsByClass.computeIfAbsent(mutation.getMutatedClass(), k -> new ArrayList<>()) - .add(mutation); - if (mutation.isDetected()) { - killCount++; - } - mutationsByPackage.computeIfAbsent(packageNameFromClass(mutation.getMutatedClass()), k -> new ArrayList<>()) - .add(mutation); + }); } public Collection getMutationsForPackage(String packageName) { diff --git a/src/main/java/org/jenkinsci/plugins/pitmutation/Mutations.java b/src/main/java/org/jenkinsci/plugins/pitmutation/Mutations.java new file mode 100644 index 0000000..9f49f99 --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/pitmutation/Mutations.java @@ -0,0 +1,14 @@ +package org.jenkinsci.plugins.pitmutation; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@AllArgsConstructor +@Data +@NoArgsConstructor +public class Mutations { + private List mutation; +} diff --git a/src/main/java/org/jenkinsci/plugins/pitmutation/ParseReportCallable.java b/src/main/java/org/jenkinsci/plugins/pitmutation/ParseReportCallable.java new file mode 100644 index 0000000..bfbc7a4 --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/pitmutation/ParseReportCallable.java @@ -0,0 +1,41 @@ +package org.jenkinsci.plugins.pitmutation; + +import hudson.FilePath; +import hudson.remoting.VirtualChannel; +import org.jenkinsci.remoting.RoleChecker; + +import java.io.File; +import java.io.IOException; + +/** + * The type Parse report callable. + */ +public class ParseReportCallable implements FilePath.FileCallable { + + private static final long serialVersionUID = 1L; + + private final String reportFilePath; + + /** + * Instantiates a new Parse report callable. + * + * @param reportFilePath the report file path + */ + public ParseReportCallable(String reportFilePath) { + this.reportFilePath = reportFilePath; + } + + @Override + public FilePath[] invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { + FilePath[] r = new FilePath(f).list(reportFilePath); + if (r.length < 1) { + throw new IOException("No reports found at location:" + reportFilePath); + } + return r; + } + + @Override + public void checkRoles(RoleChecker roleChecker) throws SecurityException { + + } +} diff --git a/src/main/java/org/jenkinsci/plugins/pitmutation/PercentageThresholdCondition.java b/src/main/java/org/jenkinsci/plugins/pitmutation/PercentageThresholdCondition.java new file mode 100644 index 0000000..3fd5269 --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/pitmutation/PercentageThresholdCondition.java @@ -0,0 +1,21 @@ +package org.jenkinsci.plugins.pitmutation; + +import hudson.model.Result; +import org.jenkinsci.plugins.pitmutation.targets.MutationStats; + +import static hudson.model.Result.FAILURE; +import static hudson.model.Result.SUCCESS; + +class PercentageThresholdCondition implements Condition { + private final float percentage; + + PercentageThresholdCondition(float percentage) { + super(); + this.percentage = percentage; + } + + @Override + public Result decideResult(PitBuildAction action) { + return action.getReport().getMutationStats().getKillPercent() >= percentage ? SUCCESS : FAILURE; + } +} diff --git a/src/main/java/org/jenkinsci/plugins/pitmutation/PitBuildAction.java b/src/main/java/org/jenkinsci/plugins/pitmutation/PitBuildAction.java index 998756b..91490a9 100644 --- a/src/main/java/org/jenkinsci/plugins/pitmutation/PitBuildAction.java +++ b/src/main/java/org/jenkinsci/plugins/pitmutation/PitBuildAction.java @@ -27,12 +27,15 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import static hudson.model.Result.FAILURE; + /** * @author edward */ @Slf4j -public class PitBuildAction implements HealthReportingAction, StaplerProxy { +public class PitBuildAction implements HealthReportingAction, + StaplerProxy { private static final Pattern MUTATION_REPORT_PATTERN = Pattern.compile(".*mutation-report-([^/\\\\]*).*"); @@ -45,16 +48,13 @@ public PitBuildAction(Run owner) { } public PitBuildAction getPreviousAction() { - Run b = owner; + Run build = owner; while (true) { - b = b.getPreviousBuild(); - if (b == null) - return null; - if (b.getResult() == Result.FAILURE) - continue; - PitBuildAction r = b.getAction(PitBuildAction.class); - if (r != null) - return r; + build = build.getPreviousBuild(); + if (build == null) {return null;} + if (build.getResult() == FAILURE) {continue;} + PitBuildAction action = build.getAction(PitBuildAction.class); + if (action != null) {return action;} } } @@ -94,7 +94,7 @@ private Map readReports() { } else { name = String.valueOf(i); } - reports.put(name, MutationReport.create(files[i].read())); + reports.put(name, new MutationReport(files[i].read())); } } catch (IOException | InterruptedException | SAXException e) { e.printStackTrace(); @@ -121,7 +121,7 @@ static PitBuildAction getPreviousResult(Run start) { if (b == null) { return null; } - assert b.getResult() != Result.FAILURE : "We asked for the previous not failed build"; + assert b.getResult() != FAILURE : "We asked for the previous not failed build"; PitBuildAction r = b.getAction(PitBuildAction.class); if (r != null) { return r; @@ -132,7 +132,7 @@ static PitBuildAction getPreviousResult(Run start) { @Override public HealthReport getBuildHealth() { return new HealthReport((int) getReport().getMutationStats().getKillPercent(), - Messages._BuildAction_Description(getReport().getMutationStats().getKillPercent())); + Messages._BuildAction_Description(getReport().getMutationStats().getKillPercent())); } @Override @@ -173,14 +173,14 @@ public void doGraph(StaplerRequest req, StaplerResponse rsp) throws IOException final JFreeChart chart = ChartFactory.createLineChart(null, // chart title - null, // unused - "%", // range axis label - dsb.build(), // data - PlotOrientation.VERTICAL, // orientation - true, // include legend - true, // tooltips - false // urls - );// JFreeChart chart = new MutationChart(this).createChart(); + null, // unused + "%", // range axis label + dsb.build(), // data + PlotOrientation.VERTICAL, // orientation + true, // include legend + true, // tooltips + false // urls + );// JFreeChart chart = new MutationChart(this).createChart(); ChartUtil.generateGraph(req, rsp, chart, 500, 200); } } diff --git a/src/main/java/org/jenkinsci/plugins/pitmutation/PitLogger.java b/src/main/java/org/jenkinsci/plugins/pitmutation/PitLogger.java new file mode 100644 index 0000000..5ea2380 --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/pitmutation/PitLogger.java @@ -0,0 +1,68 @@ +package org.jenkinsci.plugins.pitmutation; + +import hudson.FilePath; +import hudson.model.TaskListener; +import org.jenkinsci.plugins.pitmutation.targets.MutationStats; + +/** + * Logging class for various pit information. + * + * @author edward + * @author vasile.jureschi + */ +public class PitLogger { + + PitLogger() { + } + + public void logResults(TaskListener listener, PitBuildAction action) { + MutationStats mutationStats = action.getReport().getMutationStats(); + PitBuildAction previousAction = action.getPreviousAction(); + if (previousAction != null) { + float previousKillPercent = previousAction.getReport().getMutationStats().getKillPercent(); + logPrevious(mutationStats.getKillPercent(), previousKillPercent, listener); + } + logCurrent(mutationStats, listener); + } + + public void logMissingReportsIgnored(TaskListener listener) { + listener + .getLogger() + .println("Build successful as no reports generated and build set to ignore missing reports. " + + "If the reports should be present set 'ignoreMissingReports' to false and run the build again to fail the build."); + } + + public void logBuildFailedNoReports(TaskListener listener) { + listener + .getLogger() + .println("Build failed as no reports generated and build set to not ignore missing reports. " + + "If no reports should be generated set 'ignoreMissingReports' to true and run the build again to pass the build."); + } + + private void logCurrent(MutationStats currentStats, TaskListener listener) { + listener + .getLogger() + .printf("Kill ratio is %f \\u0025 (%d %d)%n", + currentStats.getKillPercent(), + currentStats.getKillCount(), + currentStats.getTotalMutations()); + } + + + private void logPrevious(float currentKillPercent, float previousKillPercent, TaskListener listener) { + listener.getLogger().println("Previous kill ratio was " + previousKillPercent + "%"); + listener.getLogger().println("This kill ration is " + currentKillPercent + "%"); + } + + public void logLookingForReports(TaskListener listener, FilePath workspace) { + listener.getLogger().println("Looking for PIT reports in " + workspace.getRemote()); + } + + public void logPublishReport(TaskListener listener, FilePath workspace) { + listener.getLogger().println("Publishing mutation report: " + workspace.getRemote()); + } + + public void log(TaskListener listener, String message) { + listener.getLogger().println(message); + } +} diff --git a/src/main/java/org/jenkinsci/plugins/pitmutation/PitProjectAction.java b/src/main/java/org/jenkinsci/plugins/pitmutation/PitProjectAction.java index 8db7ed7..75628b7 100644 --- a/src/main/java/org/jenkinsci/plugins/pitmutation/PitProjectAction.java +++ b/src/main/java/org/jenkinsci/plugins/pitmutation/PitProjectAction.java @@ -6,6 +6,8 @@ import java.io.IOException; +import static hudson.model.Result.FAILURE; + /** * @author Ed Kimber */ @@ -24,11 +26,9 @@ public PitProjectAction(AbstractProject project) { */ public PitBuildAction getLastResult() { for (AbstractBuild b = project.getLastSuccessfulBuild(); b != null; b = b.getPreviousNotFailedBuild()) { - if (b.getResult() == Result.FAILURE) - continue; + if (b.getResult() == FAILURE) {continue;} PitBuildAction r = b.getAction(PitBuildAction.class); - if (r != null) - return r; + if (r != null) {return r;} } return null; } @@ -44,11 +44,9 @@ public PitBuildAction getLastResult() { */ public Integer getLastResultBuild() { for (AbstractBuild b = project.getLastSuccessfulBuild(); b != null; b = b.getPreviousNotFailedBuild()) { - if (b.getResult() == Result.FAILURE) - continue; + if (b.getResult() == FAILURE) {continue;} PitBuildAction r = b.getAction(PitBuildAction.class); - if (r != null) - return b.getNumber(); + if (r != null) {return b.getNumber();} } return null; } @@ -87,7 +85,6 @@ public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException } public void doGraph(StaplerRequest req, StaplerResponse rsp) throws IOException { - if (getLastResult() != null) - getLastResult().doGraph(req, rsp); + if (getLastResult() != null) {getLastResult().doGraph(req, rsp);} } } diff --git a/src/main/java/org/jenkinsci/plugins/pitmutation/PitPublisher.java b/src/main/java/org/jenkinsci/plugins/pitmutation/PitPublisher.java index 847d961..c6b40d6 100644 --- a/src/main/java/org/jenkinsci/plugins/pitmutation/PitPublisher.java +++ b/src/main/java/org/jenkinsci/plugins/pitmutation/PitPublisher.java @@ -1,65 +1,74 @@ package org.jenkinsci.plugins.pitmutation; -import hudson.Extension; +import edu.umd.cs.findbugs.annotations.NonNull; +import hudson.EnvVars; import hudson.FilePath; import hudson.Launcher; import hudson.Util; -import hudson.model.*; -import hudson.remoting.VirtualChannel; -import hudson.tasks.BuildStepDescriptor; +import hudson.model.AbstractProject; +import hudson.model.Action; +import hudson.model.Run; +import hudson.model.TaskListener; import hudson.tasks.BuildStepMonitor; -import hudson.tasks.Publisher; import hudson.tasks.Recorder; import jenkins.tasks.SimpleBuildStep; -import net.sf.json.JSONObject; -import org.apache.commons.lang.StringUtils; -import org.jenkinsci.Symbol; -import org.jenkinsci.plugins.pitmutation.targets.MutationStats; -import org.jenkinsci.remoting.RoleChecker; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.DataBoundSetter; -import org.kohsuke.stapler.StaplerRequest; import javax.annotation.Nonnull; import java.io.File; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; +import java.util.Arrays; +import java.util.Map; + +import static hudson.model.Result.FAILURE; +import static hudson.model.Result.SUCCESS; +import static hudson.tasks.BuildStepMonitor.STEP; /** * The type Pit publisher. * * @author edward + * @author vasile.jureschi */ public class PitPublisher extends Recorder implements SimpleBuildStep { - public static final String SINGLE_MODULE_REPORT_FOLDER = "mutation-report-all"; - public static final String MULTI_MODULE_REPORT_FORMAT = "mutation-report-%s"; - - private List buildConditions; + public static final String GLOB_MUTATIONS_XML = "**/target/pit-reports/**/mutations.xml"; private String mutationStatsFile; private boolean killRatioMustImprove; + /** + * If true and no reports are generated, the missing reports will not cause a build failure. Uses for cases where + * there are test but nothing is mutated them and there are no reports generated. + */ + private boolean ignoreMissingReports; private float minimumKillRatio; - private transient TaskListener listener; - private Run build; + + private FileProcessor fileProcessor; + private ResultDecider resultDecider; + private PitLogger pitLogger; /** - * Instantiates a new Pit publisher. + * Instantiates a new Pit publisher. Only used in tests. * * @param mutationStatsFile the mutation stats file * @param minimumKillRatio the minimum kill ratio * @param killRatioMustImprove the kill ratio must improve + * @param ignoreMissingReports do not fail the build if there are no reports */ - protected PitPublisher(String mutationStatsFile, float minimumKillRatio, boolean killRatioMustImprove) { + PitPublisher(String mutationStatsFile, + float minimumKillRatio, + boolean killRatioMustImprove, + boolean ignoreMissingReports, + FileProcessor fileProcessor, + ResultDecider resultDecider, + PitLogger pitLogger) { this.mutationStatsFile = mutationStatsFile; this.killRatioMustImprove = killRatioMustImprove; this.minimumKillRatio = minimumKillRatio; - this.buildConditions = new ArrayList<>(); - this.buildConditions.add(percentageThreshold(minimumKillRatio)); - if (killRatioMustImprove) { - this.buildConditions.add(mustImprove()); - } + this.ignoreMissingReports = ignoreMissingReports; + this.fileProcessor = fileProcessor; + this.resultDecider = resultDecider; + this.pitLogger = pitLogger; } /** @@ -68,132 +77,123 @@ protected PitPublisher(String mutationStatsFile, float minimumKillRatio, boolean * {@link #mutationStatsFile} is set to {@code **{@literal /}target/pit-reports/**{@literal /}mutations.xml}, * {@link #minimumKillRatio} is set to {@code 0.0}, * {@link #killRatioMustImprove} is set to {@code false}, + * {@link #ignoreMissingReports} is set to {@code false}, */ @DataBoundConstructor - public PitPublisher() { - this("**/target/pit-reports/**/mutations.xml", 0, false); + public PitPublisher(String mutationStatsFile, + float minimumKillRatio, + boolean killRatioMustImprove, + boolean ignoreMissingReports) { + this(mutationStatsFile, + minimumKillRatio, + killRatioMustImprove, + ignoreMissingReports, + new FileProcessor(), + killRatioMustImprove ? + new ResultDecider(new PercentageThresholdCondition(minimumKillRatio)) : + new ResultDecider(new PercentageThresholdCondition(minimumKillRatio), new MustImproveCondition()), + new PitLogger()); } @DataBoundSetter - public void setMutationStatsFile(final String mutationStatsFile) { - this.mutationStatsFile = mutationStatsFile; - } - - @DataBoundSetter - public void setMinimumKillRatio(final float minimumKillRatio) { - this.minimumKillRatio = minimumKillRatio; - } - - @DataBoundSetter - public void setKillRatioMustImprove(final boolean killRatioMustImprove) { - this.killRatioMustImprove = killRatioMustImprove; + public void setIgnoreMissingReports(final boolean ignoreMissingReports) { + this.ignoreMissingReports = ignoreMissingReports; } @Override - public void perform(@Nonnull Run build, @Nonnull FilePath workspace, @Nonnull Launcher launcher, @Nonnull TaskListener listener) throws InterruptedException, IOException { - this.listener = listener; - this.build = build; - - Result result = build.getResult(); - if (build instanceof AbstractBuild && result != null && result.isBetterOrEqualTo(Result.UNSTABLE)) { - AbstractBuild abstractBuild = (AbstractBuild) build; - this.listener.getLogger().println("Looking for PIT reports in " + abstractBuild.getModuleRoot().getRemote()); + public void perform(@Nonnull Run build, + @Nonnull FilePath workspace, + @NonNull EnvVars env, + @Nonnull Launcher launcher, + @Nonnull TaskListener listener) { + try { + process(build, workspace, env, launcher, listener); + } catch (IOException e) { + Util.displayIOException(e, listener); + build.setResult(FAILURE); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } - final FilePath[] moduleRoots = abstractBuild.getModuleRoots(); - final boolean multipleModuleRoots = moduleRoots != null && moduleRoots.length > 1; - final FilePath moduleRoot = multipleModuleRoots ? abstractBuild.getWorkspace() : abstractBuild.getModuleRoot(); - if (moduleRoot == null) { - this.listener.getLogger().println("Module root was returned as null"); - return; - } + private void process(@Nonnull Run build, + @Nonnull FilePath workspace, + @NonNull EnvVars env, + @Nonnull Launcher launcher, + @Nonnull TaskListener listener) throws InterruptedException, IOException { + FilePath[] reports = workspace.act(new ParseReportCallable(mutationStatsFile)); - ParseReportCallable fileCallable = new ParseReportCallable(mutationStatsFile); - FilePath[] reports = moduleRoot.act(fileCallable); - publishReports(reports, new FilePath(abstractBuild.getRootDir()), abstractBuild.getModuleRoot().getRemote()); + if (noReportsAndMissingReportsIgnored(reports)) { + pitLogger.logMissingReportsIgnored(listener); + build.setResult(SUCCESS); + return; + } else if (noReportsAndMissingReportsNotIgnored(reports)) { + pitLogger.logBuildFailedNoReports(listener); + build.setResult(FAILURE); + return; + } - //publish latest reports - PitBuildAction action = new PitBuildAction(abstractBuild); - abstractBuild.addAction(action); - abstractBuild.setResult(decideBuildResult(action)); + pitLogger.logLookingForReports(listener, workspace); + FilePath buildTarget = new FilePath(build.getRootDir()); + if (reports.length == 1) { + fileProcessor.copySingleModuleReport(reports[0], buildTarget); } else { - this.listener.getLogger().println("Looking for PIT reports in " + workspace.getRemote()); - - ParseReportCallable fileCallable = new ParseReportCallable(mutationStatsFile); - FilePath[] reports = workspace.act(fileCallable); - FilePath buildTarget = new FilePath(build.getRootDir()); - if (reports.length == 1) { - copyMutationReports(reports[0], buildTarget, SINGLE_MODULE_REPORT_FOLDER); - } else { - publishReports(reports, buildTarget, workspace.getRemote()); + for (Map.Entry entry : fileProcessor + .getNames(reports, workspace.getRemote()) + .entrySet()) { + FilePath filePath = entry.getKey(); + String module = entry.getValue(); + pitLogger.logPublishReport(listener, workspace); + fileProcessor.copyMultiModuleReport(filePath, buildTarget, module); } - PitBuildAction action = new PitBuildAction(build); - build.addAction(action); - build.setResult(decideBuildResult(action)); } - } - /** - * {@inheritDoc} - */ - @Override - public Action getProjectAction(AbstractProject project) { - return new PitProjectAction(project); - } - /** - * Publish reports. - * - * @param reports the reports - * @param buildTarget the build target - * @param base the base path of the report location - */ - void publishReports(FilePath[] reports, FilePath buildTarget, final String base) { - for (int i = 0; i < reports.length; i++) { - FilePath report = reports[i]; - listener.getLogger().println("Publishing mutation report: " + report.getRemote()); + PitBuildAction action = new PitBuildAction(build); + build.addAction(action); + build.setResult(resultDecider.decideBuildResult(action)); - final String moduleName; - if (StringUtils.isBlank(base)) { - moduleName = String.valueOf(i == 0 ? null : i); - } else { - String[] partsFromRemoteWithoutBase = report.getRemote().replace(base, "").split("[/\\\\]"); - if (partsFromRemoteWithoutBase.length > 1) { - moduleName = partsFromRemoteWithoutBase[1]; - } else { - moduleName = String.valueOf(i == 0 ? null : i); - } + pitLogger.logResults(listener, action); + } + + private boolean noReportsAndMissingReportsIgnored(FilePath[] reports) { + if (reports == null && ignoreMissingReports) { + return true; + } else if (reports == null) { + return false; + } + return Arrays.stream(reports).anyMatch(report -> { + try { + return (!report.exists() || report.isDirectory()) && ignoreMissingReports; + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + return false; } - copyMutationReports(reports[i], buildTarget, String.format(MULTI_MODULE_REPORT_FORMAT, moduleName)); - } + }); } - private void copyMutationReports(FilePath source, FilePath buildTarget, String mutationFilePath) { - final FilePath targetPath = new FilePath(buildTarget, mutationFilePath); - try { - FilePath parent = Optional.ofNullable(source.getParent()).orElseThrow(() -> new IOException()); - parent.copyRecursiveTo(targetPath); - } catch (IOException e) { - Util.displayIOException(e, listener); - e.printStackTrace(listener.fatalError("Unable to copy coverage from " + source + " to " + buildTarget)); - build.setResult(Result.FAILURE); - } catch (InterruptedException e) { - e.printStackTrace(); + private boolean noReportsAndMissingReportsNotIgnored(FilePath[] reports) { + if (reports == null && !ignoreMissingReports) { + return false; + } else if (reports == null) { + return true; } + return Arrays.stream(reports).anyMatch(report -> { + try { + return (!report.exists() || report.isDirectory()) && !ignoreMissingReports; + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + return false; + } + }); } /** - * Decide build result result. - * - * @param action the action - * @return the worst result from all conditions + * {@inheritDoc} */ - public Result decideBuildResult(PitBuildAction action) { - Result result = Result.SUCCESS; - for (Condition condition : buildConditions) { - Result conditionResult = condition.decideResult(action); - result = conditionResult.isWorseThan(result) ? conditionResult : result; - } - return result; + @Override + public Action getProjectAction(AbstractProject project) { + return new PitProjectAction(project); } @@ -224,126 +224,18 @@ public String getMutationStatsFile() { return mutationStatsFile; } - Condition percentageThreshold(final float percentage) { - return new PercentageThresholdCondition(percentage); - } - - class PercentageThresholdCondition implements Condition { - private final float percentage; - - PercentageThresholdCondition(float percentage) { - super(); - this.percentage = percentage; - } - - @Override - public Result decideResult(PitBuildAction action) { - MutationStats stats = action.getReport().getMutationStats(); - dologging(stats); - return stats.getKillPercent() >= percentage ? Result.SUCCESS : Result.FAILURE; - } - - void dologging(MutationStats stats) { - listener.getLogger().println("Kill ratio is " + stats.getKillPercent() + "% (" + stats.getKillCount() - + " " + stats.getTotalMutations() + ")"); - } - } - - class MustImproveCondition implements Condition { - @Override - public Result decideResult(final PitBuildAction action) { - PitBuildAction previousAction = action.getPreviousAction(); - if (previousAction != null) { - MutationStats previousStats = previousAction.getReport().getMutationStats(); - logInfo(action, previousStats); - return action.getReport().getMutationStats().getKillPercent() >= previousStats.getKillPercent() ? Result.SUCCESS - : Result.UNSTABLE; - } else { - return Result.SUCCESS; - } - } - - void logInfo(final PitBuildAction action, MutationStats stats) { - listener.getLogger().println("Previous kill ratio was " + stats.getKillPercent() + "%"); - listener.getLogger() - .println("This kill ration is " + action.getReport().getMutationStats().getKillPercent() + "%"); - } - } - - Condition mustImprove() { - return new MustImproveCondition(); + /** + * Required by plugin config + * + * @return ignore missing reports flag + */ + public boolean getIgnoreMissingReports() { + return ignoreMissingReports; } @Override public BuildStepMonitor getRequiredMonitorService() { - return BuildStepMonitor.BUILD; - } - - /** - * The type Descriptor. - */ - @Extension - @Symbol("pitmutation") - public static class DescriptorImpl extends BuildStepDescriptor { - - /** - * Instantiates a new Descriptor. - */ - public DescriptorImpl() { - super(PitPublisher.class); - } - - @Override - public String getDisplayName() { - return Messages.PitPublisher_DisplayName(); - } - - @Override - public boolean isApplicable(Class aClass) { - return true; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean configure(StaplerRequest req, JSONObject formData) throws FormException { - req.bindParameters(this, "pitmutation"); - save(); - return super.configure(req, formData); - } + return STEP; } - /** - * The type Parse report callable. - */ - public static class ParseReportCallable implements FilePath.FileCallable { - - private static final long serialVersionUID = 1L; - - private final String reportFilePath; - - /** - * Instantiates a new Parse report callable. - * - * @param reportFilePath the report file path - */ - public ParseReportCallable(String reportFilePath) { - this.reportFilePath = reportFilePath; - } - - @Override - public FilePath[] invoke(File f, VirtualChannel channel) throws IOException, InterruptedException { - FilePath[] r = new FilePath(f).list(reportFilePath); - if (r.length < 1) { - throw new IOException("No reports found at location:" + reportFilePath); - } - return r; - } - - @Override - public void checkRoles(RoleChecker roleChecker) throws SecurityException { - - } - } } diff --git a/src/main/java/org/jenkinsci/plugins/pitmutation/ResultDecider.java b/src/main/java/org/jenkinsci/plugins/pitmutation/ResultDecider.java new file mode 100644 index 0000000..6965555 --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/pitmutation/ResultDecider.java @@ -0,0 +1,43 @@ +package org.jenkinsci.plugins.pitmutation; + +import hudson.model.Result; + +import java.util.Collection; +import java.util.List; + +import static hudson.model.Result.SUCCESS; + +/** + * Decides if a build is failed or not based on conditions. + * + * @author edward + * @author vasile.jureschi + */ +public class ResultDecider { + + private final Collection buildConditions; + + protected ResultDecider(PercentageThresholdCondition thresholdCondition) { + buildConditions = List.of(thresholdCondition); + } + + protected ResultDecider(PercentageThresholdCondition thresholdCondition, + MustImproveCondition mustImproveCondition) { + buildConditions = List.of(thresholdCondition, mustImproveCondition); + } + + /** + * Decide build result result. + * + * @param action the action + * @return the worst result from all conditions + */ + public Result decideBuildResult(PitBuildAction action) { + Result result = SUCCESS; + for (Condition condition : buildConditions) { + Result conditionResult = condition.decideResult(action); + result = conditionResult.isWorseThan(result) ? conditionResult : result; + } + return result; + } +} diff --git a/src/main/java/org/jenkinsci/plugins/pitmutation/portlets/CoverageRange.java b/src/main/java/org/jenkinsci/plugins/pitmutation/portlets/CoverageRange.java index 9f2ced4..f0d455e 100644 --- a/src/main/java/org/jenkinsci/plugins/pitmutation/portlets/CoverageRange.java +++ b/src/main/java/org/jenkinsci/plugins/pitmutation/portlets/CoverageRange.java @@ -63,14 +63,14 @@ public static Color fillColorOf(final double amount) { return ABYSMAL.fillColor; } - private static Color blendedColor(final Color fillColor0, final Color fillColor1, final double t0, final double t1) { + private static Color blendedColor(final Color fillColor0, + final Color fillColor1, + final double t0, + final double t1) { final double total = t0 + t1; - final int r = (int) ((fillColor0.getRed() * t1 + fillColor1.getRed() - * t0) / total); - final int g = (int) ((fillColor0.getGreen() * t1 + fillColor1 - .getGreen() * t0) / total); - final int b = (int) ((fillColor0.getBlue() * t1 + fillColor1.getBlue() - * t0) / total); + final int r = (int) ((fillColor0.getRed() * t1 + fillColor1.getRed() * t0) / total); + final int g = (int) ((fillColor0.getGreen() * t1 + fillColor1.getGreen() * t0) / total); + final int b = (int) ((fillColor0.getBlue() * t1 + fillColor1.getBlue() * t0) / total); return new Color(r, g, b); } diff --git a/src/main/java/org/jenkinsci/plugins/pitmutation/portlets/PitColumn.java b/src/main/java/org/jenkinsci/plugins/pitmutation/portlets/PitColumn.java index 1702a62..61a153a 100644 --- a/src/main/java/org/jenkinsci/plugins/pitmutation/portlets/PitColumn.java +++ b/src/main/java/org/jenkinsci/plugins/pitmutation/portlets/PitColumn.java @@ -52,11 +52,10 @@ private Double getLinePercent(final Run lastSuccessfulBuild) { final double doubleValue = percentageFloat.doubleValue(); final int decimalPlaces = 2; - BigDecimal bigDecimal = new BigDecimal(doubleValue); + BigDecimal bigDecimal = BigDecimal.valueOf(doubleValue); // setScale is immutable - bigDecimal = bigDecimal.setScale(decimalPlaces, - RoundingMode.HALF_UP); + bigDecimal = bigDecimal.setScale(decimalPlaces, RoundingMode.HALF_UP); return bigDecimal.doubleValue(); } diff --git a/src/main/java/org/jenkinsci/plugins/pitmutation/targets/BaseResultFactory.java b/src/main/java/org/jenkinsci/plugins/pitmutation/targets/BaseResultFactory.java index f08ea2d..441038f 100644 --- a/src/main/java/org/jenkinsci/plugins/pitmutation/targets/BaseResultFactory.java +++ b/src/main/java/org/jenkinsci/plugins/pitmutation/targets/BaseResultFactory.java @@ -12,7 +12,6 @@ private BaseResultFactory() { public static MutationResult getBaseMutationResultFrom(PitBuildAction action) { Map reports = action.getReports(); - return reports.size() == 1 ? new SingleModuleResult(action) : - new ProjectMutations(action); + return reports.size() == 1 ? new SingleModuleResult(action) : new ProjectMutations(action); } } diff --git a/src/main/java/org/jenkinsci/plugins/pitmutation/targets/ModuleChildMapBuilder.java b/src/main/java/org/jenkinsci/plugins/pitmutation/targets/ModuleChildMapBuilder.java index 7080516..f5c2f2e 100644 --- a/src/main/java/org/jenkinsci/plugins/pitmutation/targets/ModuleChildMapBuilder.java +++ b/src/main/java/org/jenkinsci/plugins/pitmutation/targets/ModuleChildMapBuilder.java @@ -33,8 +33,6 @@ public Map build() { private String getClassName(Mutation mutation) { String mutatedClassName = mutation.getMutatedClass(); int firstDollar = mutatedClassName.indexOf('$'); - return firstDollar >= 0 - ? mutatedClassName.substring(0, firstDollar) - : mutatedClassName; + return firstDollar >= 0 ? mutatedClassName.substring(0, firstDollar) : mutatedClassName; } } diff --git a/src/main/java/org/jenkinsci/plugins/pitmutation/targets/ModuleResult.java b/src/main/java/org/jenkinsci/plugins/pitmutation/targets/ModuleResult.java index 8326de0..eb07ae7 100644 --- a/src/main/java/org/jenkinsci/plugins/pitmutation/targets/ModuleResult.java +++ b/src/main/java/org/jenkinsci/plugins/pitmutation/targets/ModuleResult.java @@ -1,15 +1,14 @@ package org.jenkinsci.plugins.pitmutation.targets; -import static org.jenkinsci.plugins.pitmutation.PitPublisher.MULTI_MODULE_REPORT_FORMAT; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.jenkinsci.plugins.pitmutation.MutationReport; +import javax.annotation.Nonnull; import java.util.Map; import java.util.Objects; -import javax.annotation.Nonnull; -import org.jenkinsci.plugins.pitmutation.MutationReport; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; +import static org.jenkinsci.plugins.pitmutation.FileProcessor.MULTI_MODULE_REPORT_FORMAT; /** * @author edward @@ -17,9 +16,9 @@ @Slf4j public class ModuleResult extends MutationResult { - private MutationReport report; + private final MutationReport report; @Getter - private String name; + private final String name; public ModuleResult(String name, MutationResult parent, MutationReport report) { super(name, parent); @@ -54,13 +53,10 @@ public int compareTo(@Nonnull ModuleResult other) { @Override public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; + if (this == o) {return true;} + if (o == null || getClass() != o.getClass()) {return false;} ModuleResult that = (ModuleResult) o; - return Objects.equals(report, that.report) && - Objects.equals(name, that.name); + return Objects.equals(report, that.report) && Objects.equals(name, that.name); } @Override diff --git a/src/main/java/org/jenkinsci/plugins/pitmutation/targets/MutatedClass.java b/src/main/java/org/jenkinsci/plugins/pitmutation/targets/MutatedClass.java index a942a50..dcf2a1c 100644 --- a/src/main/java/org/jenkinsci/plugins/pitmutation/targets/MutatedClass.java +++ b/src/main/java/org/jenkinsci/plugins/pitmutation/targets/MutatedClass.java @@ -43,7 +43,8 @@ public MutatedClass(String name, MutationResult parent, Collection mut } private Map createMutatedLines(Collection mutations) { - return mutations.stream() + return mutations + .stream() .collect(groupingBy(Mutation::getLineNumber)) .values() .stream() @@ -58,17 +59,20 @@ public boolean isSourceLevel() { /** * Gets the contents of the coverage report for the file, but removes the header and the stylesheet as this needs to be handled separately in jelly. + * * @return The source of the coverage report to show in the UI */ @Override public String getSourceFileContent() { String fullContents = getFileContents(package_ + File.separator + fileName); - return fullContents.contains(END_HEADER_TAG) ? fullContents.substring(fullContents.indexOf(END_HEADER_TAG) + 5) : - fullContents; + return fullContents.contains(END_HEADER_TAG) ? + fullContents.substring(fullContents.indexOf(END_HEADER_TAG) + 5) : + fullContents; } /** * Gets the contents of the style sheet for the coverage report. + * * @return The source of the coverage report to show in the UI. */ @Override @@ -78,12 +82,10 @@ public String getStyleSheetContent() { private String getFileContents(String path) { String filePath = - getOwner().getRootDir() + File.separator + getMutationReportDirectory() + - File.separator + path; + getOwner().getRootDir() + File.separator + getMutationReportDirectory() + File.separator + path; try { return new TextFile(new File(filePath)).read(); - } - catch (IOException exception) { + } catch (IOException exception) { return "Could not read file: " + filePath + "\n"; } } @@ -110,16 +112,15 @@ public int compareTo(@Nonnull MutatedClass other) { @Override public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; + if (this == o) {return true;} + if (o == null || getClass() != o.getClass()) {return false;} MutatedClass that = (MutatedClass) o; - return Objects.equals(name, that.name) && - Objects.equals(package_, that.package_) && - Objects.equals(fileName, that.fileName) && - Objects.equals(mutations, that.mutations) && - Objects.equals(mutatedLines, that.mutatedLines); + return Objects.equals(name, that.name) + && Objects.equals(package_, that.package_) + && Objects.equals(fileName, + that.fileName) + && Objects.equals(mutations, that.mutations) + && Objects.equals(mutatedLines, that.mutatedLines); } @Override diff --git a/src/main/java/org/jenkinsci/plugins/pitmutation/targets/MutatedLine.java b/src/main/java/org/jenkinsci/plugins/pitmutation/targets/MutatedLine.java index 8de043d..eaf7821 100644 --- a/src/main/java/org/jenkinsci/plugins/pitmutation/targets/MutatedLine.java +++ b/src/main/java/org/jenkinsci/plugins/pitmutation/targets/MutatedLine.java @@ -68,13 +68,10 @@ public int compareTo(@Nonnull MutatedLine other) { @Override public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; + if (this == o) {return true;} + if (o == null || getClass() != o.getClass()) {return false;} MutatedLine that = (MutatedLine) o; - return lineNumber == that.lineNumber && - Objects.equals(mutations, that.mutations); + return lineNumber == that.lineNumber && Objects.equals(mutations, that.mutations); } @Override diff --git a/src/main/java/org/jenkinsci/plugins/pitmutation/targets/MutatedPackage.java b/src/main/java/org/jenkinsci/plugins/pitmutation/targets/MutatedPackage.java index 6ed76d5..e9e476c 100644 --- a/src/main/java/org/jenkinsci/plugins/pitmutation/targets/MutatedPackage.java +++ b/src/main/java/org/jenkinsci/plugins/pitmutation/targets/MutatedPackage.java @@ -31,7 +31,8 @@ public String getDisplayName() { @Override public MutationStats getMutationStats() { - return new MutationStatsImpl(getName(), classMutations.values().stream().flatMap(List::stream).collect(toList())); + return new MutationStatsImpl(getName(), + classMutations.values().stream().flatMap(List::stream).collect(toList())); } @Override @@ -53,10 +54,8 @@ public int compareTo(@Nonnull MutatedPackage other) { @Override public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; + if (this == o) {return true;} + if (o == null || getClass() != o.getClass()) {return false;} MutatedPackage that = (MutatedPackage) o; return Objects.equals(classMutations, that.classMutations); } diff --git a/src/main/java/org/jenkinsci/plugins/pitmutation/targets/MutationResult.java b/src/main/java/org/jenkinsci/plugins/pitmutation/targets/MutationResult.java index 30184b2..46254b4 100644 --- a/src/main/java/org/jenkinsci/plugins/pitmutation/targets/MutationResult.java +++ b/src/main/java/org/jenkinsci/plugins/pitmutation/targets/MutationResult.java @@ -81,10 +81,7 @@ public boolean isCoverageAltered() { } public Collection getChildren() { - return getChildMap().values().stream() - .sorted() - .sorted(Collections.reverseOrder()) - .collect(Collectors.toList()); + return getChildMap().values().stream().sorted().sorted(Collections.reverseOrder()).collect(Collectors.toList()); } public MutationStats getStatsDelta() { @@ -129,9 +126,7 @@ static String urlTransform(String token) { StringBuilder buf = new StringBuilder(token.length()); for (int i = 0; i < token.length(); i++) { final char c = token.charAt(i); - if (('0' <= c && '9' >= c) - || ('A' <= c && 'Z' >= c) - || ('a' <= c && 'z' >= c)) { + if (('0' <= c && '9' >= c) || ('A' <= c && 'Z' >= c) || ('a' <= c && 'z' >= c)) { buf.append(c); } else { buf.append('_'); diff --git a/src/main/java/org/jenkinsci/plugins/pitmutation/targets/MutationStats.java b/src/main/java/org/jenkinsci/plugins/pitmutation/targets/MutationStats.java index f068775..c727797 100644 --- a/src/main/java/org/jenkinsci/plugins/pitmutation/targets/MutationStats.java +++ b/src/main/java/org/jenkinsci/plugins/pitmutation/targets/MutationStats.java @@ -28,7 +28,9 @@ public String getKillPercentFillColor() { return getCoverageColors().getFillHexString(); } - public String getKillPercentTextColor() { return getCoverageColors().getLineHexString();} + public String getKillPercentTextColor() { + return getCoverageColors().getLineHexString(); + } private CoverageRange getCoverageColors() { return CoverageRange.valueOf(getKillPercent()); @@ -36,7 +38,7 @@ private CoverageRange getCoverageColors() { private float round(float ratio) { //TODO NaN mutation test - if (Float.isNaN(ratio) || Float.isInfinite(ratio)) return ratio; + if (Float.isNaN(ratio) || Float.isInfinite(ratio)) {return ratio;} BigDecimal bd = new BigDecimal(ratio); BigDecimal rounded = bd.setScale(3, RoundingMode.HALF_UP); return rounded.floatValue(); diff --git a/src/main/java/org/jenkinsci/plugins/pitmutation/targets/ProjectMutations.java b/src/main/java/org/jenkinsci/plugins/pitmutation/targets/ProjectMutations.java index dc553f5..eec9a02 100644 --- a/src/main/java/org/jenkinsci/plugins/pitmutation/targets/ProjectMutations.java +++ b/src/main/java/org/jenkinsci/plugins/pitmutation/targets/ProjectMutations.java @@ -70,10 +70,8 @@ public int compareTo(@Nonnull ProjectMutations other) { @Override public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; + if (this == o) {return true;} + if (o == null || getClass() != o.getClass()) {return false;} ProjectMutations that = (ProjectMutations) o; return Objects.equals(action, that.action); } diff --git a/src/main/java/org/jenkinsci/plugins/pitmutation/targets/SingleModuleResult.java b/src/main/java/org/jenkinsci/plugins/pitmutation/targets/SingleModuleResult.java index 10992e4..eda0c4f 100644 --- a/src/main/java/org/jenkinsci/plugins/pitmutation/targets/SingleModuleResult.java +++ b/src/main/java/org/jenkinsci/plugins/pitmutation/targets/SingleModuleResult.java @@ -1,16 +1,16 @@ package org.jenkinsci.plugins.pitmutation.targets; +import hudson.model.Run; +import org.jenkinsci.plugins.pitmutation.MutationReport; +import org.jenkinsci.plugins.pitmutation.PitBuildAction; + +import javax.annotation.Nonnull; import java.util.ArrayList; import java.util.Collection; import java.util.Map; import java.util.Objects; -import javax.annotation.Nonnull; -import org.jenkinsci.plugins.pitmutation.MutationReport; -import org.jenkinsci.plugins.pitmutation.PitBuildAction; -import org.jenkinsci.plugins.pitmutation.PitPublisher; - -import hudson.model.Run; +import static org.jenkinsci.plugins.pitmutation.FileProcessor.SINGLE_MODULE_REPORT_FOLDER; public class SingleModuleResult extends MutationResult { @@ -57,7 +57,7 @@ public String getDisplayName() { @Override protected String getMutationReportDirectory() { - return PitPublisher.SINGLE_MODULE_REPORT_FOLDER; + return SINGLE_MODULE_REPORT_FOLDER; } @Override @@ -77,10 +77,8 @@ public int compareTo(@Nonnull SingleModuleResult other) { @Override public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; + if (this == o) {return true;} + if (o == null || getClass() != o.getClass()) {return false;} SingleModuleResult that = (SingleModuleResult) o; return Objects.equals(action, that.action); } diff --git a/src/main/resources/org/jenkinsci/plugins/pitmutation/PitPublisher/config.jelly b/src/main/resources/org/jenkinsci/plugins/pitmutation/PitPublisher/config.jelly index c3d21e8..d243ebf 100644 --- a/src/main/resources/org/jenkinsci/plugins/pitmutation/PitPublisher/config.jelly +++ b/src/main/resources/org/jenkinsci/plugins/pitmutation/PitPublisher/config.jelly @@ -23,4 +23,9 @@ description="The build will be marked unstable if the kill ratio is lower than the last build."> + + + diff --git a/src/test/java/org/jenkinsci/plugins/pitmutation/FileProcessorTest.java b/src/test/java/org/jenkinsci/plugins/pitmutation/FileProcessorTest.java new file mode 100644 index 0000000..dfb1ecc --- /dev/null +++ b/src/test/java/org/jenkinsci/plugins/pitmutation/FileProcessorTest.java @@ -0,0 +1,47 @@ +package org.jenkinsci.plugins.pitmutation; + +import hudson.FilePath; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.IOException; + +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class FileProcessorTest { + + @Mock + private FilePath source; + + @Mock + private FilePath buildTarget; + + private FileProcessor fileProcessor; + + @BeforeEach + public void setUp() { + MockitoAnnotations.openMocks(this); + fileProcessor = new FileProcessor(); + when(source.getParent()).thenReturn(source); + } + + @Test + void copySingleModuleReport() throws Exception { + fileProcessor.copySingleModuleReport(source, buildTarget); + verify(source, times(1)).getParent(); + } + + @Test + void copyMultiModuleReport() throws Exception { + String module = "module1"; + fileProcessor.copyMultiModuleReport(source, buildTarget, module); + verify(source, times(1)).getParent(); + } + + // TODO: Add more tests for other methods in FileProcessor +} diff --git a/src/test/java/org/jenkinsci/plugins/pitmutation/MustImproveConditionTest.java b/src/test/java/org/jenkinsci/plugins/pitmutation/MustImproveConditionTest.java new file mode 100644 index 0000000..268d757 --- /dev/null +++ b/src/test/java/org/jenkinsci/plugins/pitmutation/MustImproveConditionTest.java @@ -0,0 +1,64 @@ +package org.jenkinsci.plugins.pitmutation; + +import hudson.model.Result; +import org.jenkinsci.plugins.pitmutation.targets.MutationResult; +import org.jenkinsci.plugins.pitmutation.targets.MutationStats; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import java.util.stream.Stream; + +import static hudson.model.Result.SUCCESS; +import static hudson.model.Result.UNSTABLE; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.DynamicTest.dynamicTest; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class MustImproveConditionTest { + + @TestFactory + Stream testFactory() { + return Stream + .of(new TestCase(null, 90f, SUCCESS, "noPreviousAction"), + new TestCase(85f, 90f, SUCCESS, "currentKillPercentGreater"), + new TestCase(85f, 85f, SUCCESS, "currentKillPercentEqual"), + new TestCase(85f, 80f, UNSTABLE, "currentKillPercentLess")) + .map(testCase -> dynamicTest(testCase.name, () -> { + PitBuildAction action = mock(PitBuildAction.class); + PitBuildAction previousAction = mock(PitBuildAction.class); + MutationResult currentReport = mock(MutationResult.class); + MutationResult previousReport = mock(MutationResult.class); + MutationStats currentStats = mock(MutationStats.class); + MutationStats previousStats = mock(MutationStats.class); + + when(action.getReport()).thenReturn(currentReport); + when(currentReport.getMutationStats()).thenReturn(currentStats); + when(currentStats.getKillPercent()).thenReturn(testCase.currentKillPercent); + + if (testCase.previousKillPercent != null) { + when(action.getPreviousAction()).thenReturn(previousAction); + when(previousAction.getReport()).thenReturn(previousReport); + when(previousReport.getMutationStats()).thenReturn(previousStats); + when(previousStats.getKillPercent()).thenReturn(testCase.previousKillPercent); + } + + MustImproveCondition condition = new MustImproveCondition(); + assertEquals(testCase.expectedResult, condition.decideResult(action)); + })); + } + + private static class TestCase { + Float previousKillPercent; + float currentKillPercent; + Result expectedResult; + String name; + + TestCase(Float previousKillPercent, float currentKillPercent, Result expectedResult, String name) { + this.previousKillPercent = previousKillPercent; + this.currentKillPercent = currentKillPercent; + this.expectedResult = expectedResult; + this.name = name; + } + } +} diff --git a/src/test/java/org/jenkinsci/plugins/pitmutation/MutationReportTest.java b/src/test/java/org/jenkinsci/plugins/pitmutation/MutationReportTest.java index 21129fb..b7c1624 100644 --- a/src/test/java/org/jenkinsci/plugins/pitmutation/MutationReportTest.java +++ b/src/test/java/org/jenkinsci/plugins/pitmutation/MutationReportTest.java @@ -1,7 +1,7 @@ package org.jenkinsci.plugins.pitmutation; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.xml.sax.SAXException; import java.io.ByteArrayInputStream; @@ -17,73 +17,96 @@ /** * @author edward */ -public class MutationReportTest { - - private static final String MUTATIONS = - "" - + "\n" - + "SafeMultipartFile.java\n" - + "com.mediagraft.podsplice.controllers.massupload.SafeMultipartFile\n" - + "getSize\n" - + "54\n" - + "org.pitest.mutationtest.engine.gregor.mutators.ReturnValsMutator\n" - + "5\n" - + "\n" - + "" - + "" - + "SafeMultipartFile.java" - + "com.mediagraft.podsplice.controllers.massupload.SafeMultipartFile" - + "getSize" - + "57" - + "org.pitest.mutationtest.engine.gregor.mutators.ReturnValsMutator" - + "6" - + "3" - + "" - + "" - + ""; +class MutationReportTest { + + private static final String MUTATIONS_OLD = "" + + "\n" + + "SafeMultipartFile.java\n" + + "com.mediagraft.podsplice.controllers.massupload.SafeMultipartFile\n" + + "getSize\n" + + "54\n" + + "org.pitest.mutationtest.engine.gregor.mutators.ReturnValsMutator\n" + + "5\n" + + "\n" + + "" + + "" + + "SafeMultipartFile.java" + + "com.mediagraft.podsplice.controllers.massupload.SafeMultipartFile" + + "getSize" + + "57" + + "org.pitest.mutationtest.engine.gregor.mutators.ReturnValsMutator" + + "6" + + "3" + + "" + + "" + + ""; + + private static final String MUTATIONS = "" + + "\n" + + "SafeMultipartFile.java\n" + + "com.mediagraft.podsplice.controllers.massupload.SafeMultipartFile\n" + + "getSize\n" + + "54\n" + + "org.pitest.mutationtest.engine.gregor.mutators.ReturnValsMutator\n" + + "5\n" + + "\n" + + "" + + "" + + "SafeMultipartFile.java" + + "com.mediagraft.podsplice.controllers.massupload.SafeMultipartFile" + + "getSize" + + "57" + + "org.pitest.mutationtest.engine.gregor.mutators.ReturnValsMutator" + + "6" + + "3" + + "" + + "" + + ""; private InputStream mutationsXml; - @Before + @BeforeEach public void setUp() { mutationsXml = getClass().getResourceAsStream("testmutations-00.xml"); } @Test - public void packageNameFinder() { + void packageNameFinder() { assertThat(MutationReport.packageNameFromClass("xxx.yyy.zzz.Foo"), is("xxx.yyy.zzz")); assertThat(MutationReport.packageNameFromClass("Foo"), is("")); } @Test - public void countsKills() throws IOException, SAXException { - MutationReport report = MutationReport.create(mutationsXml); + void countsKills() throws IOException, SAXException { + MutationReport report = new MutationReport(mutationsXml); assertThat(report.getMutationStats().getKillCount(), is(5)); assertThat(report.getMutationStats().getTotalMutations(), is(16)); } @Test - public void sortsMutationsByClassName() throws IOException, SAXException { - MutationReport report = MutationReport.create(mutationsXml); - Collection mutations = report.getMutationsForClassName("org.jenkinsci.plugins.pitmutation.MutationReport"); + void sortsMutationsByClassName() throws IOException, SAXException { + MutationReport report = new MutationReport(mutationsXml); + Collection mutations = + report.getMutationsForClassName("org.jenkinsci.plugins.pitmutation.MutationReport"); assertThat(mutations.size(), is(5)); } @Test - public void indexesMutationsByPackage() throws IOException, SAXException { - MutationReport report = MutationReport.create(mutationsXml); + void indexesMutationsByPackage() throws IOException, SAXException { + MutationReport report = new MutationReport(mutationsXml); assertThat(report.getMutationsForPackage("org.jenkinsci.plugins.pitmutation"), hasSize(16)); assertThat(report.getMutationsForPackage(""), hasSize(0)); } @Test - public void canDigestAMutation() throws IOException, SAXException { - MutationReport report = MutationReport.create(new ByteArrayInputStream(MUTATIONS.getBytes("UTF-8"))); + void canDigestAMutation() throws IOException, SAXException { + MutationReport report = new MutationReport(new ByteArrayInputStream(MUTATIONS.getBytes("UTF-8"))); assertThat(report.getMutationStats().getTotalMutations(), is(2)); - Iterator mutations = - report.getMutationsForClassName("com.mediagraft.podsplice.controllers.massupload.SafeMultipartFile").iterator(); + Iterator mutations = report + .getMutationsForClassName("com.mediagraft.podsplice.controllers.massupload.SafeMultipartFile") + .iterator(); Mutation m1 = mutations.next(); Mutation m2 = mutations.next(); @@ -97,6 +120,29 @@ public void canDigestAMutation() throws IOException, SAXException { } } + @Test + void canDigestAMutationOlderMutations() throws IOException, SAXException { + MutationReport report = new MutationReport(new ByteArrayInputStream(MUTATIONS_OLD.getBytes("UTF-8"))); + + assertThat(report.getMutationStats().getTotalMutations(), is(2)); + + Iterator mutations = report + .getMutationsForClassName("com.mediagraft.podsplice.controllers.massupload.SafeMultipartFile") + .iterator(); + + Mutation m1 = mutations.next(); + Mutation m2 = mutations.next(); + + if (m1.getStatus().equals("KILLED")) { + verifyKilled(m1); + verifyNoCoverage(m2); + } else { + verifyKilled(m2); + verifyNoCoverage(m1); + } + } + + private void verifyNoCoverage(Mutation m) { assertThat(m.getLineNumber(), is(54)); assertThat(m.isDetected(), is(true)); diff --git a/src/test/java/org/jenkinsci/plugins/pitmutation/MutationReportWithDescriptionTest.java b/src/test/java/org/jenkinsci/plugins/pitmutation/MutationReportWithDescriptionTest.java index 7e00a2b..991256a 100644 --- a/src/test/java/org/jenkinsci/plugins/pitmutation/MutationReportWithDescriptionTest.java +++ b/src/test/java/org/jenkinsci/plugins/pitmutation/MutationReportWithDescriptionTest.java @@ -1,53 +1,53 @@ package org.jenkinsci.plugins.pitmutation; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.core.Is.is; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.xml.sax.SAXException; import java.io.IOException; import java.io.InputStream; import java.util.Collection; -import org.junit.Before; -import org.junit.Test; -import org.xml.sax.SAXException; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.core.Is.is; /** * @author edward */ -public class MutationReportWithDescriptionTest { +class MutationReportWithDescriptionTest { private InputStream mutationsXml; - @Before + @BeforeEach public void setUp() { mutationsXml = getClass().getResourceAsStream("testmutations-02.xml"); } @Test - public void packageNameFinder() { + void packageNameFinder() { assertThat(MutationReport.packageNameFromClass("xxx.yyy.zzz.Foo"), is("xxx.yyy.zzz")); assertThat(MutationReport.packageNameFromClass("Foo"), is("")); } @Test - public void countsKills() throws IOException, SAXException { - MutationReport report = MutationReport.create(mutationsXml); + void countsKills() throws IOException, SAXException { + MutationReport report = new MutationReport(mutationsXml); assertThat(report.getMutationStats().getKillCount(), is(3)); assertThat(report.getMutationStats().getTotalMutations(), is(4)); } @Test - public void sortsMutationsByClassName() throws IOException, SAXException { - MutationReport report = MutationReport.create(mutationsXml); + void sortsMutationsByClassName() throws IOException, SAXException { + MutationReport report = new MutationReport(mutationsXml); Collection mutations = report.getMutationsForClassName("es.rodri.controllers.CompositorController"); assertThat(mutations.size(), is(4)); } @Test - public void indexesMutationsByPackage() throws IOException, SAXException { - MutationReport report = MutationReport.create(mutationsXml); + void indexesMutationsByPackage() throws IOException, SAXException { + MutationReport report = new MutationReport(mutationsXml); assertThat(report.getMutationsForPackage("es.rodri.controllers"), hasSize(4)); assertThat(report.getMutationsForPackage(""), notNullValue()); assertThat(report.getMutationsForPackage(""), hasSize(0)); diff --git a/src/test/java/org/jenkinsci/plugins/pitmutation/PercentageThresholdConditionTest.java b/src/test/java/org/jenkinsci/plugins/pitmutation/PercentageThresholdConditionTest.java new file mode 100644 index 0000000..7627004 --- /dev/null +++ b/src/test/java/org/jenkinsci/plugins/pitmutation/PercentageThresholdConditionTest.java @@ -0,0 +1,52 @@ +package org.jenkinsci.plugins.pitmutation; + +import hudson.model.Result; +import org.jenkinsci.plugins.pitmutation.targets.MutationResult; +import org.jenkinsci.plugins.pitmutation.targets.MutationStats; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import java.util.stream.Stream; + +import static hudson.model.Result.FAILURE; +import static hudson.model.Result.SUCCESS; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.DynamicTest.dynamicTest; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class PercentageThresholdConditionTest { + + @TestFactory + Stream testFactory() { + return Stream + .of(new TestCase(80f, 85f, SUCCESS, "killPercentGreaterThanThreshold"), + new TestCase(85f, 85f, SUCCESS, "killPercentEqualToThreshold"), + new TestCase(90f, 85f, FAILURE, "killPercentLessThanThreshold")) + .map(testCase -> dynamicTest(testCase.name, () -> { + PitBuildAction action = mock(PitBuildAction.class); + MutationResult report = mock(MutationResult.class); + MutationStats stats = mock(MutationStats.class); + when(action.getReport()).thenReturn(report); + when(report.getMutationStats()).thenReturn(stats); + when(stats.getKillPercent()).thenReturn(testCase.killPercent); + + PercentageThresholdCondition condition = new PercentageThresholdCondition(testCase.threshold); + assertEquals(testCase.expectedResult, condition.decideResult(action)); + })); + } + + private static class TestCase { + float threshold; + float killPercent; + Result expectedResult; + String name; + + TestCase(float threshold, float killPercent, Result expectedResult, String name) { + this.threshold = threshold; + this.killPercent = killPercent; + this.expectedResult = expectedResult; + this.name = name; + } + } +} diff --git a/src/test/java/org/jenkinsci/plugins/pitmutation/PitBuildActionTest.java b/src/test/java/org/jenkinsci/plugins/pitmutation/PitBuildActionTest.java index dc855ee..be34ed7 100644 --- a/src/test/java/org/jenkinsci/plugins/pitmutation/PitBuildActionTest.java +++ b/src/test/java/org/jenkinsci/plugins/pitmutation/PitBuildActionTest.java @@ -11,7 +11,8 @@ import java.io.FilenameFilter; import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import hudson.model.AbstractBuild; import hudson.model.Result; @@ -19,13 +20,13 @@ /** * @author edward */ -public class PitBuildActionTest { +class PitBuildActionTest { private PitBuildAction action; private AbstractBuild owner; private AbstractBuild failedBuild; private AbstractBuild successBuild; - @Before + @BeforeEach public void setUp() { failedBuild = mock(AbstractBuild.class); when(failedBuild.getResult()).thenReturn(Result.FAILURE); @@ -43,18 +44,18 @@ public void setUp() { } @Test - public void previousReturnsNullIfNoPreviousBuilds() { + void previousReturnsNullIfNoPreviousBuilds() { assertThat(action.getPreviousAction(), nullValue()); } @Test - public void previousReturnsNullIfAllPreviousBuildsFailed() { + void previousReturnsNullIfAllPreviousBuildsFailed() { when(owner.getPreviousBuild()).thenReturn(failedBuild); assertThat(action.getPreviousAction(), nullValue()); } @Test - public void previousReturnsLastSuccessfulBuild() { + void previousReturnsLastSuccessfulBuild() { PitBuildAction previousSucccessAction = mock(PitBuildAction.class); when(owner.getPreviousBuild()).thenReturn(failedBuild); when(failedBuild.getPreviousBuild()).thenReturn(successBuild); diff --git a/src/test/java/org/jenkinsci/plugins/pitmutation/PitLoggerTest.java b/src/test/java/org/jenkinsci/plugins/pitmutation/PitLoggerTest.java new file mode 100644 index 0000000..888c902 --- /dev/null +++ b/src/test/java/org/jenkinsci/plugins/pitmutation/PitLoggerTest.java @@ -0,0 +1,83 @@ +package org.jenkinsci.plugins.pitmutation; + +import hudson.FilePath; +import hudson.model.TaskListener; +import org.jenkinsci.plugins.pitmutation.targets.MutationResult; +import org.jenkinsci.plugins.pitmutation.targets.MutationStats; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.File; +import java.io.PrintStream; + +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class PitLoggerTest { + + @Mock + private TaskListener listener; + @Mock + private PitBuildAction action; + @Mock + private MutationResult report; + @Mock + private MutationStats stats; + @Mock + private PrintStream printStream; + + private PitLogger pitLogger; + + @BeforeEach + void setUp() { + pitLogger = new PitLogger(); + + when(listener.getLogger()).thenReturn(printStream); + } + + @Test + void logResults() { + when(action.getReport()).thenReturn(report); + when(action.getPreviousAction()).thenReturn(action); + when(report.getMutationStats()).thenReturn(stats); + when(stats.getKillPercent()).thenReturn(70.0f); + + pitLogger.logResults(listener, action); + + verify(printStream, atLeastOnce()).println(anyString()); + } + + @Test + void logMissingReportsIgnored() { + pitLogger.logMissingReportsIgnored(listener); + verify(printStream).println(anyString()); + } + + @Test + void logBuildFailedNoReports() { + pitLogger.logBuildFailedNoReports(listener); + verify(printStream).println(anyString()); + } + + @Test + void logLookingForReports() { + FilePath workspace = new FilePath(new File("path/to/workspace")); + pitLogger.logLookingForReports(listener, workspace); + + verify(printStream).println(anyString()); + } + + @Test + void logPublishReport() { + FilePath workspace = new FilePath(new File("path/to/workspace")); + pitLogger.logPublishReport(listener, workspace); + + verify(printStream).println(anyString()); + } +} diff --git a/src/test/java/org/jenkinsci/plugins/pitmutation/PitProjectActionTest.java b/src/test/java/org/jenkinsci/plugins/pitmutation/PitProjectActionTest.java new file mode 100644 index 0000000..2e022fa --- /dev/null +++ b/src/test/java/org/jenkinsci/plugins/pitmutation/PitProjectActionTest.java @@ -0,0 +1,126 @@ +package org.jenkinsci.plugins.pitmutation; + + +import hudson.model.AbstractBuild; +import hudson.model.AbstractProject; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.StaplerResponse; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.IOException; + +import static hudson.model.Result.SUCCESS; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.verify; + + +@ExtendWith(MockitoExtension.class) +class PitProjectActionTest { + + @Mock + private AbstractProject project; + @Mock + private AbstractBuild lastSuccessfulBuild; + @Mock + private AbstractBuild previousSuccessfulBuild; + @Mock + private AbstractBuild failedBuild; + @Mock + private PitBuildAction action; + @Mock + private StaplerRequest req; + @Mock + private StaplerResponse rsp; + + private PitProjectAction pitProjectAction; + + @BeforeEach + void setUp() { + pitProjectAction = new PitProjectAction(project); + } + + @Test + void getLastResult_noLastResult() { + doReturn(null).when(project).getLastSuccessfulBuild(); + PitBuildAction lastResult = pitProjectAction.getLastResult(); + assertNull(lastResult); + } + + @Test + void getLastResult_returnsLastResult() { + doReturn(lastSuccessfulBuild).when(project).getLastSuccessfulBuild(); + doReturn(SUCCESS).when(lastSuccessfulBuild).getResult(); + doReturn(action).when(lastSuccessfulBuild).getAction(PitBuildAction.class); + + PitBuildAction lastResult = pitProjectAction.getLastResult(); + assertEquals(action, lastResult); + } + + @Test + void getLastResultBuild_returnsLastBuildNumber() { + doReturn(lastSuccessfulBuild).when(project).getLastSuccessfulBuild(); + doReturn(action).when(lastSuccessfulBuild).getAction(PitBuildAction.class); + doReturn(10).when(lastSuccessfulBuild).getNumber(); + + Integer lastResultBuild = pitProjectAction.getLastResultBuild(); + assertEquals(10, lastResultBuild); + } + + @Test + void getIconFileName_returnsCorrectPath() { + String iconPath = pitProjectAction.getIconFileName(); + assertEquals("/plugin/pitmutation/pitest.png", iconPath); + } + + @Test + void getDisplayName_returnsCorrectName() { + String name = pitProjectAction.getDisplayName(); + assertEquals("PIT Mutation Report", name); + } + + @Test + void getSearchUrl_returnsCorrectUrl() { + String url = pitProjectAction.getSearchUrl(); + assertEquals("pitmutation", url); + } + + @Test + void isFloatingBoxActive_returnsTrue() { + assertTrue(pitProjectAction.isFloatingBoxActive()); + } + + @Test + void doIndex_redirectsCorrectly() throws IOException { + doReturn(lastSuccessfulBuild).when(project).getLastSuccessfulBuild(); + doReturn(action).when(lastSuccessfulBuild).getAction(PitBuildAction.class); + doReturn(10).when(lastSuccessfulBuild).getNumber(); + + pitProjectAction.doIndex(req, rsp); + + verify(rsp).sendRedirect2("../10/pitmutation"); + } + + @Test + void doIndex_buildNumberNull_redirectsCorrectly() throws IOException { + doReturn(null).when(project).getLastSuccessfulBuild(); + pitProjectAction.doIndex(req, rsp); + verify(rsp).sendRedirect2("nodata"); + } + + @Test + void doGraph_CallsDoGraphOnLastResult() throws IOException { + doReturn(lastSuccessfulBuild).when(project).getLastSuccessfulBuild(); + doReturn(action).when(lastSuccessfulBuild).getAction(PitBuildAction.class); + + pitProjectAction.doGraph(req, rsp); + + verify(action).doGraph(req, rsp); + } +} diff --git a/src/test/java/org/jenkinsci/plugins/pitmutation/PitPublisherTest.java b/src/test/java/org/jenkinsci/plugins/pitmutation/PitPublisherTest.java index 2bedfe1..97e0083 100644 --- a/src/test/java/org/jenkinsci/plugins/pitmutation/PitPublisherTest.java +++ b/src/test/java/org/jenkinsci/plugins/pitmutation/PitPublisherTest.java @@ -1,22 +1,45 @@ package org.jenkinsci.plugins.pitmutation; -import hudson.model.Result; +import hudson.EnvVars; +import hudson.FilePath; +import hudson.Launcher; +import hudson.model.AbstractProject; +import hudson.model.Action; +import hudson.model.Project; +import hudson.model.Run; +import hudson.model.TaskListener; import org.jenkinsci.plugins.pitmutation.targets.MutationStats; import org.jenkinsci.plugins.pitmutation.targets.ProjectMutations; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; +import org.mockito.junit.jupiter.MockitoExtension; -import static org.junit.Assert.*; +import java.io.File; +import java.io.IOException; +import java.util.Map; + +import static hudson.model.Result.FAILURE; +import static hudson.model.Result.SUCCESS; +import static hudson.tasks.BuildStepMonitor.STEP; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; + /** * User: Ed Kimber Date: 17/03/13 Time: 17:55 */ -public class PitPublisherTest { +@ExtendWith(MockitoExtension.class) +class PitPublisherTest { private static final float MINIMUM_KILL_RATIO = 0.25f; @@ -26,112 +49,228 @@ public class PitPublisherTest { private ProjectMutations report; @Mock private MutationStats stats; + @Mock + private PitLogger pitLogger; + + @Mock + private FileProcessor fileProcessor; - private PitPublisher publisher; - private PitPublisherTSS publisherTSS; + @Mock + private ResultDecider resultDecider; - static class PitPublisherTSS extends PitPublisher { - boolean infoImproveLogged = false; - boolean infoPercentLogged = false; + @Test + void ignoreMissingReports_ignore_noReports() throws Exception { + FileProcessor fileProcessor = mock(FileProcessor.class); + ResultDecider decider = mock(ResultDecider.class); + PitPublisher publisher = + new PitPublisher("**/mutations.xml", MINIMUM_KILL_RATIO, true, true, fileProcessor, decider, pitLogger); + TaskListener taskListener = mock(TaskListener.class); + Run build = mock(Run.class); + publisher.perform(build, mock(FilePath.class), mock(EnvVars.class), mock(Launcher.class), taskListener); - public PitPublisherTSS(String mutationStatsFile, float minimumKillRatio, boolean killRatioMustImprove) { - super(mutationStatsFile, minimumKillRatio, killRatioMustImprove); - } + verify(build).setResult(SUCCESS); + verify(pitLogger).logMissingReportsIgnored(any()); + } - class MustImproveConditionTSS extends MustImproveCondition { - void logInfo(final PitBuildAction action, MutationStats stats) { - infoImproveLogged = true; - } - } + @Test + void ignoreMissingReports_doNotIgnore_noReports() throws Exception { + FileProcessor fileProcessor = mock(FileProcessor.class); + ResultDecider decider = mock(ResultDecider.class); + PitPublisher publisher = + new PitPublisher("**/mutations.xml", MINIMUM_KILL_RATIO, true, false, fileProcessor, decider, pitLogger); + TaskListener taskListener = mock(TaskListener.class); + Run build = mock(Run.class); + FilePath workspace = mock(FilePath.class); + when(workspace.act(any(FilePath.FileCallable.class))).thenReturn(new FilePath[]{mock(FilePath.class), + mock(FilePath.class)}); - Condition mustImprove() { - return new MustImproveConditionTSS(); - } + publisher.perform(build, workspace, mock(EnvVars.class), mock(Launcher.class), taskListener); - class PercentageThresholdConditionTSS extends PercentageThresholdCondition { - PercentageThresholdConditionTSS(float percentage) { - super(percentage); - } + verify(build).setResult(FAILURE); + verify(pitLogger).logBuildFailedNoReports(any()); + } - void dologging(MutationStats stats) { - infoPercentLogged = true; - } - } + @Test + void oneReport_ioException() throws Exception { + FileProcessor fileProcessor = mock(FileProcessor.class); + ResultDecider decider = mock(ResultDecider.class); + PitPublisher publisher = new PitPublisher( + "./src/test/resources/org/jenkinsci/plugins/pitmutation/testmutations-00.xml", + MINIMUM_KILL_RATIO, + true, + false, + fileProcessor, + decider, + pitLogger); + TaskListener taskListener = mock(TaskListener.class); + FilePath workspace = mock(FilePath.class); + FilePath report = mock(FilePath.class); + when(report.exists()).thenReturn(true); + when(report.isDirectory()).thenReturn(false); + FilePath[] reports = {report}; + when(workspace.act(any(ParseReportCallable.class))).thenReturn(reports); + doThrow(IOException.class).when(fileProcessor).copySingleModuleReport(any(), any()); + Run build = mock(Run.class); + when(build.getRootDir()).thenReturn(new File("src/test/resources")); + publisher.perform(build, workspace, mock(EnvVars.class), mock(Launcher.class), taskListener); - Condition percentageThreshold(final float percentage) { - return new PercentageThresholdConditionTSS(percentage); - } + verify(build).setResult(FAILURE); + verify(pitLogger, never()).logResults(any(), any()); } - @Before - public void setup() { - MockitoAnnotations.openMocks(this); + @Test + void oneReport() throws Exception { + FileProcessor fileProcessor = mock(FileProcessor.class); + ResultDecider decider = mock(ResultDecider.class); + PitPublisher publisher = new PitPublisher( + "./src/test/resources/org/jenkinsci/plugins/pitmutation/testmutations-00.xml", + MINIMUM_KILL_RATIO, + true, + false, + fileProcessor, + decider, + pitLogger); + TaskListener taskListener = mock(TaskListener.class); + FilePath workspace = mock(FilePath.class); + FilePath report = mock(FilePath.class); + when(report.exists()).thenReturn(true); + when(report.isDirectory()).thenReturn(false); + FilePath[] reports = {report}; + when(workspace.act(any(ParseReportCallable.class))).thenReturn(reports); + doNothing().when(fileProcessor).copySingleModuleReport(any(), any()); + Run build = mock(Run.class); + when(build.getRootDir()).thenReturn(new File("src/test/resources")); + when(decider.decideBuildResult(any())).thenReturn(SUCCESS); + publisher.perform(build, workspace, mock(EnvVars.class), mock(Launcher.class), taskListener); + + verify(build).setResult(SUCCESS); + verify(pitLogger).logResults(any(), any()); } + @Test - public void testDecideBuildResultFailsIfKillRatioTooLow() { - publisherTSS = createPitPublisher(false); - publisher = publisherTSS; - final float TOO_LOW_RATIO = 0.2f; - setupBoilderPlateMocks(); - Mockito.when(stats.getKillPercent()).thenReturn(TOO_LOW_RATIO); - assertEquals(Result.FAILURE, publisher.decideBuildResult(action)); - assertFalse(publisherTSS.infoImproveLogged); - assertTrue(publisherTSS.infoPercentLogged); + void multiModuleReports() throws Exception { + FileProcessor fileProcessor = mock(FileProcessor.class); + ResultDecider decider = mock(ResultDecider.class); + PitPublisher publisher = new PitPublisher( + "./src/test/resources/org/jenkinsci/plugins/pitmutation/testmutations-00.xml", + MINIMUM_KILL_RATIO, + true, + false, + fileProcessor, + decider, + pitLogger); + TaskListener taskListener = mock(TaskListener.class); + FilePath workspace = mock(FilePath.class); + FilePath report1 = mock(FilePath.class); + when(report1.exists()).thenReturn(true); + when(report1.isDirectory()).thenReturn(false); + FilePath report2 = mock(FilePath.class); + when(report2.exists()).thenReturn(true); + when(report2.isDirectory()).thenReturn(false); + + FilePath[] reports = {report1, report2}; + when(workspace.act(any(ParseReportCallable.class))).thenReturn(reports); + when(fileProcessor.getNames(any(), any())).thenReturn(Map.of(mock(FilePath.class), + "1", + mock(FilePath.class), + "2")); + doNothing().when(fileProcessor).copyMultiModuleReport(any(), any(), any()); + Run build = mock(Run.class); + when(build.getRootDir()).thenReturn(new File("src/test/resources")); + when(decider.decideBuildResult(any())).thenReturn(SUCCESS); + publisher.perform(build, workspace, mock(EnvVars.class), mock(Launcher.class), taskListener); + + verify(build).setResult(SUCCESS); + verify(pitLogger).logResults(any(), any()); } + @Test - public void testDecideBuildResultSuccessIfKillRatioSame() { - publisher = createPitPublisher(false); - final float BIGGER_RATIO = MINIMUM_KILL_RATIO + 0.2f; - setupBoilderPlateMocks(); - Mockito.when(stats.getKillPercent()).thenReturn(MINIMUM_KILL_RATIO, BIGGER_RATIO); - assertEquals(Result.SUCCESS, publisher.decideBuildResult(action)); - assertEquals(Result.SUCCESS, publisher.decideBuildResult(action)); + void multiModuleReports_ioException() throws Exception { + FileProcessor fileProcessor = mock(FileProcessor.class); + ResultDecider decider = mock(ResultDecider.class); + PitPublisher publisher = new PitPublisher( + "./src/test/resources/org/jenkinsci/plugins/pitmutation/testmutations-00.xml", + MINIMUM_KILL_RATIO, + true, + false, + fileProcessor, + decider, + pitLogger); + TaskListener taskListener = mock(TaskListener.class); + FilePath workspace = mock(FilePath.class); + FilePath report1 = mock(FilePath.class); + when(report1.exists()).thenReturn(true); + when(report1.isDirectory()).thenReturn(false); + + FilePath report2 = mock(FilePath.class); + when(report2.exists()).thenReturn(true); + when(report2.isDirectory()).thenReturn(false); + + FilePath[] reports = {report1, report2}; + when(workspace.act(any(ParseReportCallable.class))).thenReturn(reports); + when(fileProcessor.getNames(any(), any())).thenReturn(Map.of(mock(FilePath.class), + "1", + mock(FilePath.class), + "2")); + doThrow(IOException.class).when(fileProcessor).copyMultiModuleReport(any(), any(), any()); + Run build = mock(Run.class); + when(build.getRootDir()).thenReturn(new File("src/test/resources")); + publisher.perform(build, workspace, mock(EnvVars.class), mock(Launcher.class), taskListener); + + verify(build).setResult(FAILURE); + verify(pitLogger, never()).logResults(any(), any()); } @Test - public void testMustImproveConditionSucceedsWhenPreviousActionIsNull() { - publisher = createPitPublisher(true); - final float BIGGER_RATIO = MINIMUM_KILL_RATIO + 0.2f; - setupBoilderPlateMocks(); - Mockito.when(stats.getKillPercent()).thenReturn(BIGGER_RATIO); - assertEquals(Result.SUCCESS, publisher.decideBuildResult(action)); + void getMinimumKillRatio() { + PitPublisher pitPublisher = new PitPublisher("target/pit-reports/mutations.xml", 0.5f, false, false); + assertEquals(0.5f, pitPublisher.getMinimumKillRatio()); } @Test - public void testMustImproveConditionFailsWhenWhenPreviousActionKillRatioIsBetter() { - publisher = createPitPublisher(true); - setupBoilderPlateMocks(); - setupPercentKillRatioOK(); - PitBuildAction prevAction = mock(PitBuildAction.class); - ProjectMutations prevReport_ = Mockito.mock(ProjectMutations.class); - MutationStats prevStats_ = Mockito.mock(MutationStats.class); - when(action.getPreviousAction()).thenReturn(prevAction); - when(prevAction.getReport()).thenReturn(prevReport_); - when(prevReport_.getMutationStats()).thenReturn(prevStats_); - when(stats.getKillPercent()).thenReturn(MINIMUM_KILL_RATIO + 0.5f); - when(prevStats_.getKillPercent()).thenReturn(MINIMUM_KILL_RATIO + 0.6f, // previous is better - MINIMUM_KILL_RATIO + 0.5f, // equal - MINIMUM_KILL_RATIO + 0.4f); //previous is worse - - assertEquals(Result.UNSTABLE, publisher.decideBuildResult(action)); //previous is better - assertEquals(Result.SUCCESS, publisher.decideBuildResult(action)); // previous is equal - assertEquals(Result.SUCCESS, publisher.decideBuildResult(action)); //previous is worse + void getKillRatioMustImprove() { + PitPublisher pitPublisher = new PitPublisher("target/pit-reports/mutations.xml", 0.5f, true, false); + assertTrue(pitPublisher.getKillRatioMustImprove()); } - private void setupPercentKillRatioOK() { - final float BIGGER_RATIO = MINIMUM_KILL_RATIO + 0.2f; - when(stats.getKillPercent()).thenReturn(BIGGER_RATIO); + @Test + void getIgnoreMissingReports() { + PitPublisher pitPublisher = new PitPublisher("target/pit-reports/mutations.xml", 0.5f, false, true); + assertTrue(pitPublisher.getIgnoreMissingReports()); } - private PitPublisherTSS createPitPublisher(boolean mustImprove) { - return new PitPublisherTSS("**/mutations.xml", MINIMUM_KILL_RATIO, mustImprove); + @Test + void setIgnoreMissingReports_true() { + PitPublisher pitPublisher = new PitPublisher("target/pit-reports/mutations.xml", 0.5f, false, false); + pitPublisher.setIgnoreMissingReports(true); + assertTrue(pitPublisher.getIgnoreMissingReports()); } - private void setupBoilderPlateMocks() { - Mockito.when(action.getReport()).thenReturn(report); - Mockito.when(report.getMutationStats()).thenReturn(stats); + @Test + void setIgnoreMissingReports_false() { + PitPublisher pitPublisher = new PitPublisher("target/pit-reports/mutations.xml", 0.5f, false, true); + pitPublisher.setIgnoreMissingReports(false); + assertFalse(pitPublisher.getIgnoreMissingReports()); } + @Test + void getProjectAction() { + PitPublisher pitPublisher = new PitPublisher("target/pit-reports/mutations.xml", 0.5f, false, true); + Action projectAction = pitPublisher.getProjectAction((AbstractProject) mock(Project.class)); + assertNotNull(projectAction); + } + + @Test + void getMutationStatsFile() { + PitPublisher pitPublisher = new PitPublisher("target/pit-reports/mutations.xml", 0.5f, false, true); + assertEquals("target/pit-reports/mutations.xml", pitPublisher.getMutationStatsFile()); + } + + @Test + void getRequiredMonitorService() { + PitPublisher pitPublisher = new PitPublisher("target/pit-reports/mutations.xml", 0.5f, false, true); + assertEquals(STEP, pitPublisher.getRequiredMonitorService()); + } } diff --git a/src/test/java/org/jenkinsci/plugins/pitmutation/ResultDeciderTest.java b/src/test/java/org/jenkinsci/plugins/pitmutation/ResultDeciderTest.java new file mode 100644 index 0000000..78efd6a --- /dev/null +++ b/src/test/java/org/jenkinsci/plugins/pitmutation/ResultDeciderTest.java @@ -0,0 +1,49 @@ +package org.jenkinsci.plugins.pitmutation; + +import hudson.model.Result; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; + +import java.util.Arrays; +import java.util.Collection; +import java.util.stream.Stream; + +import static hudson.model.Result.FAILURE; +import static hudson.model.Result.SUCCESS; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class ResultDeciderTest { + + @TestFactory + Stream testFactory() { + // Create a collection of inputs for the test. + Collection inputs = Arrays.asList(new Object[][]{{SUCCESS, SUCCESS, SUCCESS}, + {FAILURE, SUCCESS, FAILURE}, + {SUCCESS, FAILURE, FAILURE}, + {FAILURE, FAILURE, FAILURE}}); + + // Create and return the dynamic tests. + return inputs + .stream() + .map(input -> DynamicTest.dynamicTest("Testing with thresholdResult = " + + input[0] + + ", mustImproveResult = " + + input[1], + () -> runTest((Result) input[0], + (Result) input[1], + (Result) input[2]))); + } + + private void runTest(Result thresholdResult, Result mustImproveResult, Result expectedResult) { + PercentageThresholdCondition thresholdCondition = mock(PercentageThresholdCondition.class); + MustImproveCondition mustImproveCondition = mock(MustImproveCondition.class); + PitBuildAction action = mock(PitBuildAction.class); + when(thresholdCondition.decideResult(action)).thenReturn(thresholdResult); + when(mustImproveCondition.decideResult(action)).thenReturn(mustImproveResult); + ResultDecider decider = new ResultDecider(thresholdCondition, mustImproveCondition); + Result result = decider.decideBuildResult(action); + assertEquals(expectedResult, result); + } +} diff --git a/src/test/java/org/jenkinsci/plugins/pitmutation/portlets/CoverageRangeTest.java b/src/test/java/org/jenkinsci/plugins/pitmutation/portlets/CoverageRangeTest.java index eab509c..2331d31 100644 --- a/src/test/java/org/jenkinsci/plugins/pitmutation/portlets/CoverageRangeTest.java +++ b/src/test/java/org/jenkinsci/plugins/pitmutation/portlets/CoverageRangeTest.java @@ -1,177 +1,186 @@ package org.jenkinsci.plugins.pitmutation.portlets; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import java.awt.*; + +import java.awt.Color; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.jenkinsci.plugins.pitmutation.portlets.CoverageRange.*; +import static org.jenkinsci.plugins.pitmutation.portlets.CoverageRange.ABYSMAL; +import static org.jenkinsci.plugins.pitmutation.portlets.CoverageRange.AVERAGE; +import static org.jenkinsci.plugins.pitmutation.portlets.CoverageRange.EXCELLENT; +import static org.jenkinsci.plugins.pitmutation.portlets.CoverageRange.FAIR; +import static org.jenkinsci.plugins.pitmutation.portlets.CoverageRange.GOOD; +import static org.jenkinsci.plugins.pitmutation.portlets.CoverageRange.PERFECT; +import static org.jenkinsci.plugins.pitmutation.portlets.CoverageRange.POOR; +import static org.jenkinsci.plugins.pitmutation.portlets.CoverageRange.SUFFICIENT; +import static org.jenkinsci.plugins.pitmutation.portlets.CoverageRange.TRAGIC; -public class CoverageRangeTest { +class CoverageRangeTest { @Test - public void valueOfLessTHanZeroReturnsAbysmal() { + void valueOfLessTHanZeroReturnsAbysmal() { assertThat(CoverageRange.valueOf(-1D), is(ABYSMAL)); } @Test - public void valueOfZeroReturnsAbysmal() { + void valueOfZeroReturnsAbysmal() { assertThat(CoverageRange.valueOf(0D), is(ABYSMAL)); } @Test - public void valueOfTwentyFiveReturnsTragic() { + void valueOfTwentyFiveReturnsTragic() { assertThat(CoverageRange.valueOf(25D), is(TRAGIC)); } @Test - public void valueOfFiftyReturnsPoor() { + void valueOfFiftyReturnsPoor() { assertThat(CoverageRange.valueOf(50D), is(AVERAGE)); } @Test - public void valueOfSeventyFiveReturnsFair() { + void valueOfSeventyFiveReturnsFair() { assertThat(CoverageRange.valueOf(75D), is(SUFFICIENT)); } @Test - public void valueOfEightyFiveReturnsSufficient() { + void valueOfEightyFiveReturnsSufficient() { assertThat(CoverageRange.valueOf(85D), is(GOOD)); } @Test - public void valueOfNinetyTwoReturnsGood() { + void valueOfNinetyTwoReturnsGood() { assertThat(CoverageRange.valueOf(92D), is(EXCELLENT)); } @Test - public void valueOfNinetySevenReturnsExcellent() { + void valueOfNinetySevenReturnsExcellent() { assertThat(CoverageRange.valueOf(97D), is(EXCELLENT)); } @Test - public void valueOfOneHundredReturnsPerfect() { + void valueOfOneHundredReturnsPerfect() { assertThat(CoverageRange.valueOf(100D), is(PERFECT)); } @Test - public void valueOfOneHundredAndOneReturnsPerfect() { + void valueOfOneHundredAndOneReturnsPerfect() { assertThat(CoverageRange.valueOf(101D), is(PERFECT)); } @Test - public void getFillHexStringForAbysmal() { + void getFillHexStringForAbysmal() { assertThat(ABYSMAL.getFillHexString(), is("FF0000")); } @Test - public void getFillHexStringForTragic() { + void getFillHexStringForTragic() { assertThat(TRAGIC.getFillHexString(), is("FF4500")); } @Test - public void getFillHexStringForPoor() { + void getFillHexStringForPoor() { assertThat(POOR.getFillHexString(), is("FF7F00")); } @Test - public void getFillHexStringForFair() { + void getFillHexStringForFair() { assertThat(FAIR.getFillHexString(), is("FFFF00")); } @Test - public void getFillHexStringForSufficient() { + void getFillHexStringForSufficient() { assertThat(SUFFICIENT.getFillHexString(), is("C8FF3F")); } @Test - public void getFillHexStringForGood() { + void getFillHexStringForGood() { assertThat(GOOD.getFillHexString(), is("7AFF3F")); } @Test - public void getFillHexStringForExcellent() { + void getFillHexStringForExcellent() { assertThat(EXCELLENT.getFillHexString(), is("00CD00")); } @Test - public void getFillHexStringForPerfect() { + void getFillHexStringForPerfect() { assertThat(PERFECT.getFillHexString(), is("008B00")); } @Test - public void getLineHexStringForAbysmal() { + void getLineHexStringForAbysmal() { assertThat(ABYSMAL.getLineHexString(), is("EEEEEE")); } @Test - public void getLineHexStringForTragic() { + void getLineHexStringForTragic() { assertThat(TRAGIC.getLineHexString(), is("EEEEEE")); } @Test - public void getLineHexStringForPoor() { + void getLineHexStringForPoor() { assertThat(POOR.getLineHexString(), is("000000")); } @Test - public void getLineHexStringForFair() { + void getLineHexStringForFair() { assertThat(FAIR.getLineHexString(), is("000000")); } @Test - public void getLineHexStringForSufficient() { + void getLineHexStringForSufficient() { assertThat(SUFFICIENT.getLineHexString(), is("000000")); } @Test - public void getLineHexStringForGood() { + void getLineHexStringForGood() { assertThat(GOOD.getLineHexString(), is("000000")); } @Test - public void getLineHexStringForExcellent() { + void getLineHexStringForExcellent() { assertThat(EXCELLENT.getLineHexString(), is("000000")); } @Test - public void getLineHexStringForPerfect() { + void getLineHexStringForPerfect() { assertThat(PERFECT.getLineHexString(), is("EEEEEE")); } @Test - public void fillColorOfMinusOneReturnsAbysmalFloor() { + void fillColorOfMinusOneReturnsAbysmalFloor() { assertThat(CoverageRange.fillColorOf(-1D), is(new Color(255, 0, 0))); } @Test - public void fillColorOfThirtyReturnsProportionateBlend() { + void fillColorOfThirtyReturnsProportionateBlend() { assertThat(CoverageRange.fillColorOf(30D), is(new Color(255, 88, 0))); } @Test - public void fillColorOfThirtySevenFiveReturnsProportionateBlend() { + void fillColorOfThirtySevenFiveReturnsProportionateBlend() { assertThat(CoverageRange.fillColorOf(37.5), is(new Color(255, 117, 0))); } @Test - public void fillColorOfFortyFiveReturnsProportionateBlend() { + void fillColorOfFortyFiveReturnsProportionateBlend() { assertThat(CoverageRange.fillColorOf(45D), is(new Color(255, 146, 0))); } @Test - public void fillColorOfFiftyFloorFillColor() { + void fillColorOfFiftyFloorFillColor() { assertThat(CoverageRange.fillColorOf(50D), is(new Color(255, 165, 0))); } @Test - public void fillColorOfOneHundredAndOneReturnsPerfectFloor() { + void fillColorOfOneHundredAndOneReturnsPerfectFloor() { assertThat(CoverageRange.fillColorOf(101D), is(new Color(0, 139, 0))); } @Test - public void colorAsHexString() { + void colorAsHexString() { Color color = new Color(0, 128, 255); assertThat(CoverageRange.colorAsHexString(color), is("0080FF")); } diff --git a/src/test/java/org/jenkinsci/plugins/pitmutation/targets/MutatedClassTest.java b/src/test/java/org/jenkinsci/plugins/pitmutation/targets/MutatedClassTest.java index 81f7eba..dc6eaac 100644 --- a/src/test/java/org/jenkinsci/plugins/pitmutation/targets/MutatedClassTest.java +++ b/src/test/java/org/jenkinsci/plugins/pitmutation/targets/MutatedClassTest.java @@ -1,7 +1,7 @@ package org.jenkinsci.plugins.pitmutation.targets; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.xml.sax.SAXException; import java.io.IOException; @@ -15,27 +15,27 @@ * Date: 19/03/13 * Time: 18:45 */ -public class MutatedClassTest extends MutationResultTest { +class MutatedClassTest extends MutationResultTest { private MutatedClass mutatedClass; - @Before + @BeforeEach public void setUp() throws IOException, SAXException { super.setUp(); mutatedClass = new MutatedClass("TestClass", null, new ArrayList<>()); } @Test - public void isSourceLevelReturnsTrue() { + void isSourceLevelReturnsTrue() { assertThat(mutatedClass.isSourceLevel(), is(true)); } @Test - public void getDisplayNameReturnsName() { + void getDisplayNameReturnsName() { assertThat(mutatedClass.getDisplayName(), is("Class: TestClass")); } @Test - public void lineUrlsAreSet() { + void lineUrlsAreSet() { } } diff --git a/src/test/java/org/jenkinsci/plugins/pitmutation/targets/MutatedLineTest.java b/src/test/java/org/jenkinsci/plugins/pitmutation/targets/MutatedLineTest.java index 4b50b87..10066c6 100644 --- a/src/test/java/org/jenkinsci/plugins/pitmutation/targets/MutatedLineTest.java +++ b/src/test/java/org/jenkinsci/plugins/pitmutation/targets/MutatedLineTest.java @@ -1,7 +1,7 @@ package org.jenkinsci.plugins.pitmutation.targets; import org.jenkinsci.plugins.pitmutation.Mutation; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Arrays; @@ -14,10 +14,10 @@ /** * @author Ed Kimber */ -public class MutatedLineTest { +class MutatedLineTest { @Test - public void getsMutatorClassNames() { + void getsMutatorClassNames() { Mutation[] mutations = new Mutation[3]; mutations[0] = mock(Mutation.class); diff --git a/src/test/java/org/jenkinsci/plugins/pitmutation/targets/MutationResultTest.java b/src/test/java/org/jenkinsci/plugins/pitmutation/targets/MutationResultTest.java index e155de6..e9b0736 100644 --- a/src/test/java/org/jenkinsci/plugins/pitmutation/targets/MutationResultTest.java +++ b/src/test/java/org/jenkinsci/plugins/pitmutation/targets/MutationResultTest.java @@ -2,8 +2,8 @@ import org.jenkinsci.plugins.pitmutation.MutationReport; import org.jenkinsci.plugins.pitmutation.PitBuildAction; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.xml.sax.SAXException; import java.io.IOException; @@ -12,7 +12,11 @@ import java.util.Map; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.lessThan; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -24,10 +28,10 @@ public abstract class MutationResultTest { private MutationResult projectResult; private MutationResult moduleResult; - @Before + @BeforeEach public void setUp() throws IOException, SAXException { - MutationReport reportOld = MutationReport.create(MutationReport.class.getResourceAsStream("testmutations-00.xml")); - MutationReport reportNew = MutationReport.create(MutationReport.class.getResourceAsStream("testmutations-01.xml")); + MutationReport reportOld = new MutationReport(MutationReport.class.getResourceAsStream("testmutations-00.xml")); + MutationReport reportNew = new MutationReport(MutationReport.class.getResourceAsStream("testmutations-01.xml")); Map reportsNew = new HashMap<>(); Map reportsOld = new HashMap<>(); reportsNew.put("test_report", reportNew); @@ -49,7 +53,7 @@ public void setUp() throws IOException, SAXException { } @Test - public void mutationResultStatsDelta() { + void mutationResultStatsDelta() { MutationStats delta = projectResult.getStatsDelta(); assertThat(delta.getTotalMutations(), is(3)); assertThat(delta.getKillCount(), is(-1)); @@ -60,7 +64,7 @@ private MutationResult packageResult() { } @Test - public void packageResultsStatsDelta() { + void packageResultsStatsDelta() { MutationStats delta = packageResult().getStatsDelta(); assertThat(delta.getTotalMutations(), is(3)); assertThat(delta.getKillCount(), is(-1)); @@ -71,35 +75,35 @@ private MutationResult classResult(String className) { } @Test - public void classResultsStats() { + void classResultsStats() { MutationStats stats = classResult("org.jenkinsci.plugins.pitmutation.Mutation").getMutationStats(); assertThat(stats.getTotalMutations(), is(3)); assertThat(stats.getKillCount(), is(1)); } @Test - public void classResultsStatsDelta() { + void classResultsStatsDelta() { MutationStats delta = classResult("org.jenkinsci.plugins.pitmutation.Mutation").getStatsDelta(); assertThat(delta.getTotalMutations(), is(-1)); assertThat(delta.getKillCount(), is(-2)); } @Test - public void classResultsForNewClass() { + void classResultsForNewClass() { MutationStats stats = classResult("org.jenkinsci.plugins.pitmutation.NewMutatedClass").getMutationStats(); assertThat(stats.getTotalMutations(), is(1)); assertThat(stats.getKillCount(), is(0)); } @Test - public void classResultsForNewClassDelta() { + void classResultsForNewClassDelta() { MutationStats stats = classResult("org.jenkinsci.plugins.pitmutation.NewMutatedClass").getStatsDelta(); assertThat(stats.getTotalMutations(), is(1)); assertThat(stats.getKillCount(), is(0)); } @Test - public void classResultsOrdered() { + void classResultsOrdered() { Iterator classes = moduleResult.getChildren().iterator(); int undetected = classes.next().getMutationStats().getUndetected(); @@ -111,20 +115,23 @@ public void classResultsOrdered() { } @Test - public void urlTransformPackageName() { + void urlTransformPackageName() { assertThat(moduleResult.getChildMap().get("org.jenkinsci.plugins.pitmutation").getUrl(), - is("org_jenkinsci_plugins_pitmutation")); + is("org_jenkinsci_plugins_pitmutation")); } @Test - public void urlTransformClassName() { - assertThat(moduleResult.getChildMap().get("org.jenkinsci.plugins.pitmutation") - .getChildMap().get("org.jenkinsci.plugins.pitmutation.PitParser").getUrl(), - is("org_jenkinsci_plugins_pitmutation_PitParser")); + void urlTransformClassName() { + assertThat(moduleResult + .getChildMap() + .get("org.jenkinsci.plugins.pitmutation") + .getChildMap() + .get("org.jenkinsci.plugins.pitmutation.PitParser") + .getUrl(), is("org_jenkinsci_plugins_pitmutation_PitParser")); } @Test - public void findsMutationsOnPitParserClass() { + void findsMutationsOnPitParserClass() { MutationResult pitPackage = moduleResult.getChildMap().get("org.jenkinsci.plugins.pitmutation"); assertThat(pitPackage.getChildren(), hasSize(5)); MutationResult pitParser = pitPackage.getChildMap().get("org.jenkinsci.plugins.pitmutation.PitParser"); @@ -132,14 +139,14 @@ public void findsMutationsOnPitParserClass() { } @Test - public void collectsMutationStats() { + void collectsMutationStats() { MutationStats stats = projectResult.getMutationStats(); assertThat(stats.getTotalMutations(), is(19)); assertThat(stats.getUndetected(), is(15)); } @Test - public void correctSourceLevels() { + void correctSourceLevels() { MutationResult pitPackage = moduleResult.getChildMap().get("org.jenkinsci.plugins.pitmutation"); MutationResult pitParser = pitPackage.getChildMap().get("org.jenkinsci.plugins.pitmutation.PitParser"); MutationResult lineResult = pitParser.getChildMap().values().iterator().next(); @@ -152,12 +159,12 @@ public void correctSourceLevels() { } @Test - public void testXmlTransform() { + void testXmlTransform() { assertThat(MutationResult.xmlTransform("replace&and"), is("replace&and<and>")); } @Test - public void testUrlTransform() { + void testUrlTransform() { assertThat(MutationResult.urlTransform("^*!replace::non+'alphas@}129"), is("___replace__non__alphas__129")); } } diff --git a/src/test/java/org/jenkinsci/plugins/pitmutation/targets/MutationStatsTest.java b/src/test/java/org/jenkinsci/plugins/pitmutation/targets/MutationStatsTest.java index 9ccdb4c..fc4a4fa 100644 --- a/src/test/java/org/jenkinsci/plugins/pitmutation/targets/MutationStatsTest.java +++ b/src/test/java/org/jenkinsci/plugins/pitmutation/targets/MutationStatsTest.java @@ -1,7 +1,7 @@ package org.jenkinsci.plugins.pitmutation.targets; import org.jenkinsci.plugins.pitmutation.Mutation; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Arrays; @@ -15,10 +15,10 @@ * Date: 17/03/13 * Time: 09:50 */ -public class MutationStatsTest { +class MutationStatsTest { @Test - public void killPercentCorrectlyCalculated() { + void killPercentCorrectlyCalculated() { MutationStats stats = new MutationStats() { @Override @@ -41,7 +41,7 @@ public int getTotalMutations() { } @Test - public void mutationStatsImplTest() { + void mutationStatsImplTest() { MutationStats a = createMutationStatsA(); MutationStats b = createMutationStatsB(); assertThat(a.getTitle(), is("test")); @@ -54,7 +54,7 @@ public void mutationStatsImplTest() { } @Test - public void mutationStatsDelta() { + void mutationStatsDelta() { MutationStats delta = createMutationStatsA().delta(createMutationStatsB()); assertThat(delta.getTotalMutations(), is(0)); diff --git a/src/test/resources/org/jenkinsci/plugins/pitmutation/testmutations-00-new.xml b/src/test/resources/org/jenkinsci/plugins/pitmutation/testmutations-00-new.xml new file mode 100644 index 0000000..7af0573 --- /dev/null +++ b/src/test/resources/org/jenkinsci/plugins/pitmutation/testmutations-00-new.xml @@ -0,0 +1,386 @@ + + + + RabbitMqMessageListenerServiceImpl.java + com.stayble.badge_log.rabbitmq.impl.RabbitMqMessageListenerServiceImpl + onMessage + (Lcom/stayble/rabbitmq/binding/messages/BadgeLogMessage;)V + 30 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + + 25 + + + 5 + + + com.stayble.badge_log.rabbitmq.impl.RabbitMqMessageListenerServiceImplTest.[engine:junit-jupiter]/[class:com.stayble.badge_log.rabbitmq.impl.RabbitMqMessageListenerServiceImplTest]/[method:onPasswordResetRequestedMessage()] + + removed call to com/stayble/badge_log/service/BadgeRequestLogService::logBadgeRequest + + + BadgeRequestLogServiceImpl.java + com.stayble.badge_log.service.BadgeRequestLogServiceImpl + getBadgeInfo + (Ljava/lang/String;)Lcom/stayble/badge/service/to/ProductBadgeInfo; + 115 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + + 5 + + + 1 + + + com.stayble.badge_log.service.BadgeRequestLogServiceTest.[engine:junit-jupiter]/[class:com.stayble.badge_log.service.BadgeRequestLogServiceTest]/[method:getBadgeProductInfo_okEmptyProductId()] + + negated conditional + + + BadgeRequestLogServiceImpl.java + com.stayble.badge_log.service.BadgeRequestLogServiceImpl + getBadgeInfo + (Ljava/lang/String;)Lcom/stayble/badge/service/to/ProductBadgeInfo; + 118 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + + 32 + + + 7 + + + com.stayble.badge_log.service.BadgeRequestLogServiceTest.[engine:junit-jupiter]/[class:com.stayble.badge_log.service.BadgeRequestLogServiceTest]/[method:getBadgeProductInfo_okEmptyProductId()] + + replaced return value with null for + com/stayble/badge_log/service/BadgeRequestLogServiceImpl::getBadgeInfo + + + + BadgeRequestLogServiceImpl.java + com.stayble.badge_log.service.BadgeRequestLogServiceImpl + getBadgeStatus + + (Lcom/stayble/data/entity/Product;Lcom/stayble/data/entity/Retailer;)Lcom/stayble/data/enums/BadgeStatus; + + 86 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + + 4 + + + 0 + + + com.stayble.badge_log.service.BadgeRequestLogServiceTest.[engine:junit-jupiter]/[class:com.stayble.badge_log.service.BadgeRequestLogServiceTest]/[method:getBadgeStatus_okUpToDate()] + + negated conditional + + + BadgeRequestLogServiceImpl.java + com.stayble.badge_log.service.BadgeRequestLogServiceImpl + getBadgeStatus + + (Lcom/stayble/data/entity/Product;Lcom/stayble/data/entity/Retailer;)Lcom/stayble/data/enums/BadgeStatus; + + 89 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + + 17 + + + 3 + + + com.stayble.badge_log.service.BadgeRequestLogServiceTest.[engine:junit-jupiter]/[class:com.stayble.badge_log.service.BadgeRequestLogServiceTest]/[method:getBadgeStatus_okUpToDate()] + + negated conditional + + + BadgeRequestLogServiceImpl.java + com.stayble.badge_log.service.BadgeRequestLogServiceImpl + getBadgeStatus + + (Lcom/stayble/data/entity/Product;Lcom/stayble/data/entity/Retailer;)Lcom/stayble/data/enums/BadgeStatus; + + 92 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + + 30 + + + 6 + + + com.stayble.badge_log.service.BadgeRequestLogServiceTest.[engine:junit-jupiter]/[class:com.stayble.badge_log.service.BadgeRequestLogServiceTest]/[method:getBadgeStatus_okGrace()] + + negated conditional + + + BadgeRequestLogServiceImpl.java + com.stayble.badge_log.service.BadgeRequestLogServiceImpl + getBadgeStatus + + (Lcom/stayble/data/entity/Product;Lcom/stayble/data/entity/Retailer;)Lcom/stayble/data/enums/BadgeStatus; + + 90 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + + 21 + + + 4 + + + com.stayble.badge_log.service.BadgeRequestLogServiceTest.[engine:junit-jupiter]/[class:com.stayble.badge_log.service.BadgeRequestLogServiceTest]/[method:getBadgeStatus_okUpToDate()] + + replaced return value with null for + com/stayble/badge_log/service/BadgeRequestLogServiceImpl::getBadgeStatus + + + + BadgeRequestLogServiceImpl.java + com.stayble.badge_log.service.BadgeRequestLogServiceImpl + getBadgeStatus + + (Lcom/stayble/data/entity/Product;Lcom/stayble/data/entity/Retailer;)Lcom/stayble/data/enums/BadgeStatus; + + 93 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + + 34 + + + 7 + + + com.stayble.badge_log.service.BadgeRequestLogServiceTest.[engine:junit-jupiter]/[class:com.stayble.badge_log.service.BadgeRequestLogServiceTest]/[method:getBadgeStatus_okNotUpToDate()] + + replaced return value with null for + com/stayble/badge_log/service/BadgeRequestLogServiceImpl::getBadgeStatus + + + + BadgeRequestLogServiceImpl.java + com.stayble.badge_log.service.BadgeRequestLogServiceImpl + getBadgeStatus + + (Lcom/stayble/data/entity/Product;Lcom/stayble/data/entity/Retailer;)Lcom/stayble/data/enums/BadgeStatus; + + 95 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + + 39 + + + 8 + + + com.stayble.badge_log.service.BadgeRequestLogServiceTest.[engine:junit-jupiter]/[class:com.stayble.badge_log.service.BadgeRequestLogServiceTest]/[method:getBadgeStatus_okGrace()] + + replaced return value with null for + com/stayble/badge_log/service/BadgeRequestLogServiceImpl::getBadgeStatus + + + + BadgeRequestLogServiceImpl.java + com.stayble.badge_log.service.BadgeRequestLogServiceImpl + getBrandName + (Ljava/lang/String;)Ljava/lang/String; + 101 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + + 5 + + + 1 + + + com.stayble.badge_log.service.BadgeRequestLogServiceTest.[engine:junit-jupiter]/[class:com.stayble.badge_log.service.BadgeRequestLogServiceTest]/[method:logBadgeRequest_okWithBlankBrand()] + + negated conditional + + + BadgeRequestLogServiceImpl.java + com.stayble.badge_log.service.BadgeRequestLogServiceImpl + getBrandName + (Ljava/lang/String;)Ljava/lang/String; + 101 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + + 13 + + + 4 + + + com.stayble.badge_log.service.BadgeRequestLogServiceTest.[engine:junit-jupiter]/[class:com.stayble.badge_log.service.BadgeRequestLogServiceTest]/[method:logBadgeRequest_okWithBlankBrand()] + + replaced return value with "" for + com/stayble/badge_log/service/BadgeRequestLogServiceImpl::getBrandName + + + + BadgeRequestLogServiceImpl.java + com.stayble.badge_log.service.BadgeRequestLogServiceImpl + lambda$getBadgeInfo$2 + (Lcom/stayble/data/entity/Product;)Lcom/stayble/badge/service/to/ProductBadgeInfo; + + 121 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + + 10 + + + 2 + + + com.stayble.badge_log.service.BadgeRequestLogServiceTest.[engine:junit-jupiter]/[class:com.stayble.badge_log.service.BadgeRequestLogServiceTest]/[method:getBadgeProductInfo_okProductBadge()] + + replaced return value with null for + com/stayble/badge_log/service/BadgeRequestLogServiceImpl::lambda$getBadgeInfo$2 + + + + BadgeRequestLogServiceImpl.java + com.stayble.badge_log.service.BadgeRequestLogServiceImpl + lambda$getBadgeInfo$3 + (Ljava/lang/String;)Lcom/stayble/badge/service/to/ProductBadgeInfo; + 122 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + + 25 + + + 6 + + + com.stayble.badge_log.service.BadgeRequestLogServiceTest.[engine:junit-jupiter]/[class:com.stayble.badge_log.service.BadgeRequestLogServiceTest]/[method:getBadgeProductInfo_okNewGtinBadge()] + + replaced return value with null for + com/stayble/badge_log/service/BadgeRequestLogServiceImpl::lambda$getBadgeInfo$3 + + + + BadgeRequestLogServiceImpl.java + com.stayble.badge_log.service.BadgeRequestLogServiceImpl + logBadgeRequest + + (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + + 52 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + + 22 + + + 3 + + + com.stayble.badge_log.service.BadgeRequestLogServiceTest.[engine:junit-jupiter]/[class:com.stayble.badge_log.service.BadgeRequestLogServiceTest]/[method:logBadgeRequest_okNotExistingLocationExceptionCatch()] + + removed call to java/util/Optional::ifPresentOrElse + + + TomcatLoggingConfiguration.java + com.stayble.badge_log.configuration.TomcatLoggingConfiguration + servletContainer + ()Lorg/springframework/boot/web/servlet/server/ServletWebServerFactory; + 17 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + + 17 + + + 2 + + + removed call to ch/qos/logback/access/tomcat/LogbackValve::setAsyncSupported + + + TomcatLoggingConfiguration.java + com.stayble.badge_log.configuration.TomcatLoggingConfiguration + servletContainer + ()Lorg/springframework/boot/web/servlet/server/ServletWebServerFactory; + 19 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + + 22 + + + 3 + + + removed call to ch/qos/logback/access/tomcat/LogbackValve::setFilename + + + TomcatLoggingConfiguration.java + com.stayble.badge_log.configuration.TomcatLoggingConfiguration + servletContainer + ()Lorg/springframework/boot/web/servlet/server/ServletWebServerFactory; + 21 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + + 32 + + + 4 + + + removed call to + org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactory::addContextValves + + + + TomcatLoggingConfiguration.java + com.stayble.badge_log.configuration.TomcatLoggingConfiguration + servletContainer + ()Lorg/springframework/boot/web/servlet/server/ServletWebServerFactory; + 23 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + + 36 + + + 5 + + + replaced return value with null for + com/stayble/badge_log/configuration/TomcatLoggingConfiguration::servletContainer + + + + MethodTimingAspect.java + com.stayble.badge_log.configuration.MethodTimingAspect + measureExecutionTime + (Lorg/aspectj/lang/ProceedingJoinPoint;)Ljava/lang/Object; + 18 + org.pitest.mutationtest.engine.gregor.mutators.MathMutator + + 14 + + + 3 + + + Replaced long subtraction with addition + + + MethodTimingAspect.java + com.stayble.badge_log.configuration.MethodTimingAspect + measureExecutionTime + (Lorg/aspectj/lang/ProceedingJoinPoint;)Ljava/lang/Object; + 21 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + + 19 + + + 3 + + + replaced return value with null for + com/stayble/badge_log/configuration/MethodTimingAspect::measureExecutionTime + + + diff --git a/src/test/resources/org/jenkinsci/plugins/pitmutation/testmutations-00.xml b/src/test/resources/org/jenkinsci/plugins/pitmutation/testmutations-00.xml index 3b5fa68..0abaa20 100644 --- a/src/test/resources/org/jenkinsci/plugins/pitmutation/testmutations-00.xml +++ b/src/test/resources/org/jenkinsci/plugins/pitmutation/testmutations-00.xml @@ -1,160 +1,192 @@ - + PitParser.java org.jenkinsci.plugins.pitmutation.PitParser <init> 28 org.pitest.mutationtest.engine.gregor.mutators.NonVoidMethodCallMutator - 10 + + 10 + - + PitParser.java org.jenkinsci.plugins.pitmutation.PitParser <init> 23 org.pitest.mutationtest.engine.gregor.mutators.NonVoidMethodCallMutator - 2 + + 2 + - + PitParser.java org.jenkinsci.plugins.pitmutation.PitParser <init> 26 org.pitest.mutationtest.engine.gregor.mutators.NonVoidMethodCallMutator - 7 + + 7 + - + Mutation.java org.jenkinsci.plugins.pitmutation.Mutation equals 15 org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator - 0 + + 0 + - + Mutation.java org.jenkinsci.plugins.pitmutation.Mutation setStatus 36 org.pitest.mutationtest.engine.gregor.mutators.experimental.MemberVariableMutator - 1 + + 1 + org.jenkinsci.plugins.pitmutation.MutationReportTest.canDigestAMutation(org.jenkinsci.plugins.pitmutation.MutationReportTest) - + Mutation.java org.jenkinsci.plugins.pitmutation.Mutation getSourceFile 40 org.pitest.mutationtest.engine.gregor.mutators.ReturnValsMutator - 5 + + 5 + org.jenkinsci.plugins.pitmutation.MutationReportTest.canDigestAMutation(org.jenkinsci.plugins.pitmutation.MutationReportTest) - + Mutation.java org.jenkinsci.plugins.pitmutation.Mutation getMutator 72 org.pitest.mutationtest.engine.gregor.mutators.ReturnValsMutator - 9 + + 9 + org.jenkinsci.plugins.pitmutation.MutationReportTest.canDigestAMutation(org.jenkinsci.plugins.pitmutation.MutationReportTest) - + PitBuildAction.java org.jenkinsci.plugins.pitmutation.PitBuildAction <init> 18 org.pitest.mutationtest.engine.gregor.mutators.NonVoidMethodCallMutator - 0 + + 0 + - + PitBuildAction.java org.jenkinsci.plugins.pitmutation.PitBuildAction <init> 16 org.pitest.mutationtest.engine.gregor.mutators.experimental.MemberVariableMutator - 0 + + 0 + org.jenkinsci.plugins.pitmutation.PitBuildActionTest.previousReturnsNullIfNoPreviousBuilds(org.jenkinsci.plugins.pitmutation.PitBuildActionTest) - + PitBuildAction.java org.jenkinsci.plugins.pitmutation.PitBuildAction <init> 18 org.pitest.mutationtest.engine.gregor.mutators.experimental.MemberVariableMutator - 2 + + 2 + - + PitBuildAction.java org.jenkinsci.plugins.pitmutation.PitBuildAction <init> 18 org.pitest.mutationtest.engine.gregor.mutators.experimental.MemberVariableMutator - 1 + + 1 + - + MutationReport.java org.jenkinsci.plugins.pitmutation.MutationReport digestMutations 42 org.pitest.mutationtest.engine.gregor.mutators.experimental.InlineConstantMutator - 2 + + 2 + - + MutationReport.java org.jenkinsci.plugins.pitmutation.MutationReport digestMutations 39 org.pitest.mutationtest.engine.gregor.mutators.InlineConstantMutator - 1 + + 1 + - + MutationReport.java org.jenkinsci.plugins.pitmutation.MutationReport digestMutations 38 org.pitest.mutationtest.engine.gregor.mutators.experimental.InlineConstantMutator - 0 + + 0 + - + MutationReport.java org.jenkinsci.plugins.pitmutation.MutationReport digestMutations 29 org.pitest.mutationtest.engine.gregor.mutators.ConstructorCallMutator - 1 + + 1 + org.jenkinsci.plugins.pitmutation.MutationReportTest.canDigestAMutation(org.jenkinsci.plugins.pitmutation.MutationReportTest) - + MutationReport.java org.jenkinsci.plugins.pitmutation.MutationReport digestMutations 39 org.pitest.mutationtest.engine.gregor.mutators.experimental.InlineConstantMutator - 1 + + 1 + diff --git a/src/test/resources/org/jenkinsci/plugins/pitmutation/testmutations-01.xml b/src/test/resources/org/jenkinsci/plugins/pitmutation/testmutations-01.xml index c8a1a6a..fa53af5 100644 --- a/src/test/resources/org/jenkinsci/plugins/pitmutation/testmutations-01.xml +++ b/src/test/resources/org/jenkinsci/plugins/pitmutation/testmutations-01.xml @@ -6,7 +6,9 @@ <init> 28 org.pitest.mutationtest.engine.gregor.mutators.NonVoidMethodCallMutator - 10 + + 10 + @@ -15,7 +17,9 @@ <init> 23 org.pitest.mutationtest.engine.gregor.mutators.NonVoidMethodCallMutator - 2 + + 2 + @@ -24,7 +28,9 @@ <init> 26 org.pitest.mutationtest.engine.gregor.mutators.NonVoidMethodCallMutator - 7 + + 7 + @@ -34,7 +40,9 @@ equals 15 org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator - 0 + + 0 + @@ -43,7 +51,9 @@ setMutatedMethod 60 org.pitest.mutationtest.engine.gregor.mutators.experimental.MemberVariableMutator - 4 + + 4 + @@ -52,7 +62,9 @@ setStatus 36 org.pitest.mutationtest.engine.gregor.mutators.experimental.MemberVariableMutator - 1 + + 1 + org.jenkinsci.plugins.pitmutation.MutationReportTest.canDigestAMutation(org.jenkinsci.plugins.pitmutation.MutationReportTest) @@ -64,7 +76,9 @@ <init> 18 org.pitest.mutationtest.engine.gregor.mutators.NonVoidMethodCallMutator - 0 + + 0 + @@ -73,7 +87,9 @@ <init> 16 org.pitest.mutationtest.engine.gregor.mutators.experimental.MemberVariableMutator - 0 + + 0 + org.jenkinsci.plugins.pitmutation.PitBuildActionTest.previousReturnsNullIfNoPreviousBuilds(org.jenkinsci.plugins.pitmutation.PitBuildActionTest) @@ -84,7 +100,9 @@ <init> 18 org.pitest.mutationtest.engine.gregor.mutators.experimental.MemberVariableMutator - 2 + + 2 + @@ -93,7 +111,9 @@ <init> 17 org.pitest.mutationtest.engine.gregor.mutators.experimental.MemberVariableMutator - 1 + + 1 + @@ -102,7 +122,9 @@ getPreviousAction 29 org.pitest.mutationtest.engine.gregor.mutators.NonVoidMethodCallMutator - 3 + + 3 + org.jenkinsci.plugins.pitmutation.PitBuildActionTest.previousReturnsLastSuccessfulBuild(org.jenkinsci.plugins.pitmutation.PitBuildActionTest) @@ -113,7 +135,9 @@ getBuildHealth 40 org.pitest.mutationtest.engine.gregor.mutators.ConstructorCallMutator - 0 + + 0 + @@ -122,7 +146,9 @@ getPreviousAction 27 org.pitest.mutationtest.engine.gregor.mutators.NonVoidMethodCallMutator - 2 + + 2 + @@ -132,7 +158,9 @@ digestMutations 42 org.pitest.mutationtest.engine.gregor.mutators.experimental.InlineConstantMutator - 2 + + 2 + @@ -141,7 +169,9 @@ digestMutations 39 org.pitest.mutationtest.engine.gregor.mutators.InlineConstantMutator - 1 + + 1 + @@ -150,7 +180,9 @@ digestMutations 38 org.pitest.mutationtest.engine.gregor.mutators.experimental.InlineConstantMutator - 0 + + 0 + @@ -159,7 +191,9 @@ digestMutations 29 org.pitest.mutationtest.engine.gregor.mutators.ConstructorCallMutator - 1 + + 1 + org.jenkinsci.plugins.pitmutation.MutationReportTest.canDigestAMutation(org.jenkinsci.plugins.pitmutation.MutationReportTest) @@ -170,7 +204,9 @@ digestMutations 39 org.pitest.mutationtest.engine.gregor.mutators.experimental.InlineConstantMutator - 1 + + 1 + @@ -180,7 +216,9 @@ digestMutations 45 org.pitest.mutationtest.engine.gregor.mutators.ConstructorCallMutator - 5 + + 5 + org.jenkinsci.plugins.pitmutation.MutationReportTest.countsKills(org.jenkinsci.plugins.pitmutation.MutationReportTest) diff --git a/src/test/resources/org/jenkinsci/plugins/pitmutation/testmutations-02.xml b/src/test/resources/org/jenkinsci/plugins/pitmutation/testmutations-02.xml index 8ecabf7..4c9e217 100644 --- a/src/test/resources/org/jenkinsci/plugins/pitmutation/testmutations-02.xml +++ b/src/test/resources/org/jenkinsci/plugins/pitmutation/testmutations-02.xml @@ -9,7 +9,9 @@ 332 org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator - 5 + + 5 + negated conditional @@ -22,7 +24,9 @@ 342 org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator - 47 + + 47 + es.rodri.MSInvoiceDetailTest.testController7(es.rodri.MSInvoiceDetailTest) negated conditional @@ -35,7 +39,9 @@ 344 org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator - 56 + + 56 + negated conditional @@ -48,7 +54,9 @@ 344 org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator - 59 + + 59 + negated conditional