From 0ea0300addb98bca893b41ff43be00660fab7147 Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Fri, 30 Sep 2022 19:47:29 +0200 Subject: [PATCH 01/29] Create a first skeleton of the new recorder. --- .../coverage/model/CoverageRecorder.java | 285 ++++++++++++++++++ .../plugins/coverage/model/CoverageTool.java | 167 ++++++++++ .../coverage/model/ModelValidation.java | 219 ++++++++++++++ .../model/CoverageRecorder/config.jelly | 40 +++ .../model/CoverageRecorder/config.properties | 14 + .../help-aggregatingResults.html | 6 + .../CoverageRecorder/help-failOnError.html | 5 + .../model/CoverageRecorder/help-healthy.html | 5 + .../help-skipPublishingChecks.html | 6 + .../help-sourceCodeEncoding.html | 6 + .../help-sourceDirectories.html | 7 + .../model/CoverageRecorder/help-tools.html | 9 + .../coverage/model/CoverageTool/config.jelly | 11 + .../model/CoverageTool/config.properties | 4 + .../coverage/model/CoverageTool/help-id.html | 7 + .../model/CoverageTool/help-name.html | 4 + .../coverage/model/Messages.properties | 1 + .../coverage/model/CoverageRecorderTest.java | 32 ++ 18 files changed, 828 insertions(+) create mode 100644 plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageRecorder.java create mode 100644 plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTool.java create mode 100644 plugin/src/main/java/io/jenkins/plugins/coverage/model/ModelValidation.java create mode 100644 plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/config.jelly create mode 100644 plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/config.properties create mode 100644 plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-aggregatingResults.html create mode 100644 plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-failOnError.html create mode 100644 plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-healthy.html create mode 100644 plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-skipPublishingChecks.html create mode 100644 plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-sourceCodeEncoding.html create mode 100644 plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-sourceDirectories.html create mode 100644 plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-tools.html create mode 100644 plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageTool/config.jelly create mode 100644 plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageTool/config.properties create mode 100644 plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageTool/help-id.html create mode 100644 plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageTool/help-name.html create mode 100644 plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageRecorderTest.java diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageRecorder.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageRecorder.java new file mode 100644 index 000000000..fde5ef306 --- /dev/null +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageRecorder.java @@ -0,0 +1,285 @@ +package io.jenkins.plugins.coverage.model; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; + +import edu.hm.hafner.util.FilteredLog; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; + +import org.kohsuke.stapler.AncestorInPath; +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.DataBoundSetter; +import org.kohsuke.stapler.QueryParameter; +import org.kohsuke.stapler.verb.POST; +import org.jenkinsci.Symbol; +import hudson.EnvVars; +import hudson.Extension; +import hudson.FilePath; +import hudson.Launcher; +import hudson.model.AbstractProject; +import hudson.model.Item; +import hudson.model.Run; +import hudson.model.TaskListener; +import hudson.tasks.BuildStepDescriptor; +import hudson.tasks.BuildStepMonitor; +import hudson.tasks.Publisher; +import hudson.tasks.Recorder; +import hudson.util.ComboBoxModel; +import hudson.util.FormValidation; +import hudson.util.ListBoxModel; +import jenkins.tasks.SimpleBuildStep; + +import io.jenkins.plugins.prism.CharsetValidation; +import io.jenkins.plugins.prism.SourceCodeDirectory; +import io.jenkins.plugins.prism.SourceCodeRetention; +import io.jenkins.plugins.util.JenkinsFacade; +import io.jenkins.plugins.util.LogHandler; + +/** + * A pipeline {@code Step} or Freestyle or Maven {@link Recorder} that reads and parses coverage results in a build and + * adds the results to the persisted build results. + *

+ * Stores the created issues in a {@link CoverageNode}. This result is then attached to the {@link Run build} by + * registering a {@link CoverageBuildAction}. + *

+ * + * @author Ullrich Hafner + */ +public class CoverageRecorder extends Recorder implements SimpleBuildStep { + private String sourceCodeEncoding = StringUtils.EMPTY; + private Set sourceDirectories = new HashSet<>(); + private boolean skipPublishingChecks = false; + private SourceCodeRetention sourceCodeRetention = SourceCodeRetention.NEVER; + private List tools = new ArrayList<>(); + + private int healthy; + private int unhealthy; + + /** + * Creates a new instance of {@link CoverageRecorder}. + */ + @DataBoundConstructor + public CoverageRecorder() { + super(); + + // empty constructor required for Stapler + } + + /** + * Sets the coverage tools that will scan files and create coverage reports. + * + * @param tools + * the coverage tools + */ + @DataBoundSetter + public void setTools(final List tools) { + this.tools = new ArrayList<>(tools); + } + + /** + * Returns the static analysis tools that will scan files and create issues. + * + * @return the static analysis tools + */ + public List getTools() { + return new ArrayList<>(tools); + } + + /** + * Returns whether publishing checks should be skipped. + * + * @return {@code true} if publishing checks should be skipped, {@code false} otherwise + */ + public boolean isSkipPublishingChecks() { + return skipPublishingChecks; + } + + /** + * Sets whether publishing checks should be skipped or not. + * + * @param skipPublishingChecks {@code true} if publishing checks should be skipped, {@code false} otherwise + */ + @DataBoundSetter + public void setSkipPublishingChecks(final boolean skipPublishingChecks) { + this.skipPublishingChecks = skipPublishingChecks; + } + + /** + * Returns the encoding to use to read source files. + * + * @return the source code encoding + */ + @CheckForNull + public String getSourceCodeEncoding() { + return sourceCodeEncoding; + } + + /** + * Sets the encoding to use to read source files. + * + * @param sourceCodeEncoding + * the encoding, e.g. "ISO-8859-1" + */ + @DataBoundSetter + public void setSourceCodeEncoding(final String sourceCodeEncoding) { + this.sourceCodeEncoding = sourceCodeEncoding; + } + + /** + * Gets the paths to the directories that contain the source code. + * + * @return directories containing the source code + */ + public List getSourceDirectories() { + return new ArrayList<>(sourceDirectories); + } + + /** + * Sets the paths to the directories that contain the source code. If not relative and thus not part of the + * workspace then these directories need to be added in Jenkins global configuration to prevent accessing of + * forbidden resources. + * + * @param sourceCodeDirectories + * directories containing the source code + */ + @DataBoundSetter + public void setSourceDirectories(final List sourceCodeDirectories) { + sourceDirectories = new HashSet<>(sourceCodeDirectories); + } + + private Set getSourceDirectoriesPaths() { + Set paths = sourceDirectories.stream() + .map(SourceCodeDirectory::getPath) + .collect(Collectors.toSet()); + paths.add("src/main/java"); + return paths; + } + + /** + * Returns the retention strategy for source code files. + * + * @return the retention strategy for source code files + */ + public SourceCodeRetention getSourceCodeRetention() { + return sourceCodeRetention; + } + + /** + * Defines the retention strategy for source code files. + * + * @param sourceCodeRetention + * the retention strategy for source code files + */ + @DataBoundSetter + public void setSourceCodeRetention(final SourceCodeRetention sourceCodeRetention) { + this.sourceCodeRetention = sourceCodeRetention; + } + + @Override + public BuildStepMonitor getRequiredMonitorService() { + return BuildStepMonitor.NONE; + } + + @Override + public void perform(@NonNull final Run run, @NonNull final FilePath workspace, @NonNull final EnvVars env, + @NonNull final Launcher launcher, @NonNull final TaskListener listener) throws InterruptedException { + LogHandler logHandler = new LogHandler(listener, "Coverage"); + FilteredLog log = new FilteredLog("Errors while recording code coverage:"); + log.logInfo("Recording coverage results"); + + + + + logHandler.log(log); + } + + @Override + public Descriptor getDescriptor() { + return (Descriptor) super.getDescriptor(); + } + + /** + * Descriptor for this step: defines the context and the UI elements. + */ + @Extension + @Symbol("recordCoverage") + public static class Descriptor extends BuildStepDescriptor { + private static final JenkinsFacade JENKINS = new JenkinsFacade(); + private static final CharsetValidation CHARSET_VALIDATION = new CharsetValidation(); + + @NonNull + @Override + public String getDisplayName() { + return Messages.Recorder_Name(); + } + + @Override + public boolean isApplicable(final Class jobType) { + return true; + } + + /** + * Returns a model with all {@link SourceCodeRetention} strategies. + * + * @param project + * the project that is configured + * @return a model with all {@link SourceCodeRetention} strategies. + */ + @POST + public ListBoxModel doFillSourceCodeRetentionItems(@AncestorInPath final AbstractProject project) { + if (JENKINS.hasPermission(Item.CONFIGURE, project)) { + ListBoxModel options = new ListBoxModel(); + add(options, SourceCodeRetention.NEVER); + add(options, SourceCodeRetention.LAST_BUILD); + add(options, SourceCodeRetention.EVERY_BUILD); + return options; + } + return new ListBoxModel(); + } + + private void add(final ListBoxModel options, final SourceCodeRetention sourceCodeRetention) { + options.add(sourceCodeRetention.getDisplayName(), sourceCodeRetention.name()); + } + + /** + * Returns a model with all available charsets. + * + * @param project + * the project that is configured + * @return a model with all available charsets + */ + @POST + public ComboBoxModel doFillSourceCodeEncodingItems(@AncestorInPath final AbstractProject project) { + if (JENKINS.hasPermission(Item.CONFIGURE, project)) { + return CHARSET_VALIDATION.getAllCharsets(); + } + return new ComboBoxModel(); + } + + /** + * Performs on-the-fly validation on the character encoding. + * + * @param project + * the project that is configured + * @param sourceCodeEncoding + * the character encoding + * + * @return the validation result + */ + @POST + public FormValidation doCheckSourceCodeEncoding(@AncestorInPath final AbstractProject project, + @QueryParameter final String sourceCodeEncoding) { + if (!JENKINS.hasPermission(Item.CONFIGURE, project)) { + return FormValidation.ok(); + } + + return CHARSET_VALIDATION.validateCharset(sourceCodeEncoding); + } + } +} diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTool.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTool.java new file mode 100644 index 000000000..2f82c87f0 --- /dev/null +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTool.java @@ -0,0 +1,167 @@ +package io.jenkins.plugins.coverage.model; + +import java.io.Serializable; + +import org.apache.commons.lang3.StringUtils; + +import edu.hm.hafner.util.VisibleForTesting; + +import org.kohsuke.stapler.AncestorInPath; +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.DataBoundSetter; +import org.kohsuke.stapler.QueryParameter; +import org.kohsuke.stapler.verb.POST; +import hudson.Extension; +import hudson.model.AbstractDescribableImpl; +import hudson.model.AbstractProject; +import hudson.model.Descriptor; +import hudson.model.Item; +import hudson.util.FormValidation; + +import io.jenkins.plugins.util.JenkinsFacade; + +/** + * A coverage tool that can produce a {@link CoverageNode coverage tree} by parsing a given report file. + * + * @author Ullrich Hafner + */ +public class CoverageTool extends AbstractDescribableImpl implements Serializable { + private static final long serialVersionUID = -8612521458890553037L; + + private JenkinsFacade jenkins = new JenkinsFacade(); + + private String id = StringUtils.EMPTY; + private String name = StringUtils.EMPTY; + + @DataBoundConstructor + public CoverageTool() { + // empty for stapler + } + + @VisibleForTesting + void setJenkinsFacade(final JenkinsFacade jenkinsFacade) { + jenkins = jenkinsFacade; + } + + /** + * Called after de-serialization to retain backward compatibility. + * + * @return this + */ + protected Object readResolve() { + jenkins = new JenkinsFacade(); + + return this; + } + + /** + * Overrides the default ID of the results. The ID is used as URL of the results and as identifier in UI elements. + * If no ID is given, then the default ID is used. + * + * @param id + * the ID of the results + */ + @DataBoundSetter + public void setId(final String id) { + new ModelValidation().ensureValidId(id); + + this.id = id; + } + + public String getId() { + return id; + } + + /** + * Returns the actual ID of the tool. If no user defined ID is given, then the default ID is returned. + * + * @return the ID + * @see #setId(String) + */ + public String getActualId() { + return StringUtils.defaultIfBlank(getId(), getDescriptor().getId()); + } + + /** + * Overrides the name of the results. The name is used for all labels in the UI. If no name is given, then the + * default name is used. + * + * @param name + * the name of the results + */ + @DataBoundSetter + public void setName(final String name) { + this.name = name; + } + + public String getName() { + return name; + } + + /** + * Returns the actual name of the tool. If no user defined name is given, then the default name is returned. + * + * @return the name + * @see #setName(String) + */ + public String getActualName() { + return StringUtils.defaultIfBlank(getName(), getDescriptor().getDisplayName()); + } + + @Override + public CoverageToolDescriptor getDescriptor() { + return (CoverageToolDescriptor) jenkins.getDescriptorOrDie(getClass()); + } + + /** Descriptor for {@link CoverageTool}. **/ + @Extension + public static class CoverageToolDescriptor extends Descriptor { + private final JenkinsFacade JENKINS = new JenkinsFacade(); + + /** + * Creates a new instance of {@link CoverageToolDescriptor}. + */ + public CoverageToolDescriptor() { + super(); + } + + /** + * Performs on-the-fly validation of the ID. + * + * @param project + * the project that is configured + * @param id + * the ID of the tool + * + * @return the validation result + */ + @POST + public FormValidation doCheckId(@AncestorInPath final AbstractProject project, + @QueryParameter final String id) { + if (!new JenkinsFacade().hasPermission(Item.CONFIGURE, project)) { + return FormValidation.ok(); + } + + return new ModelValidation().validateId(id); + } + + /** + * Returns an optional help text that can provide useful hints on how to configure the coverage tool so that the + * report files could be parsed by Jenkins. This help can be a plain text message or an HTML snippet. + * + * @return the help + */ + public String getHelp() { + return StringUtils.EMPTY; + } + + /** + * Returns an optional URL to the homepage of the coverage tool. + * + * @return the help + */ + public String getUrl() { + return StringUtils.EMPTY; + } + } +} diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/ModelValidation.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/ModelValidation.java new file mode 100644 index 000000000..d75dc5988 --- /dev/null +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/ModelValidation.java @@ -0,0 +1,219 @@ +package io.jenkins.plugins.coverage.model; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.charset.IllegalCharsetNameException; +import java.nio.charset.UnsupportedCharsetException; +import java.util.Set; +import java.util.regex.Pattern; + +import org.apache.commons.lang3.StringUtils; + +import edu.hm.hafner.util.PathUtil; +import edu.hm.hafner.util.VisibleForTesting; +import edu.umd.cs.findbugs.annotations.CheckForNull; + +import org.kohsuke.stapler.AncestorInPath; +import org.kohsuke.stapler.QueryParameter; +import hudson.FilePath; +import hudson.model.AbstractProject; +import hudson.util.ComboBoxModel; +import hudson.util.FormValidation; + +import io.jenkins.plugins.util.JenkinsFacade; + +/** + * Validates all properties of a configuration of a static analysis tool in a job. + * + * @author Ullrich Hafner + */ +@SuppressWarnings("PMD.GodClass") +public class ModelValidation { + private static final Set ALL_CHARSETS = Charset.availableCharsets().keySet(); + private static final Pattern VALID_ID_PATTERN = Pattern.compile("\\p{Alnum}[\\p{Alnum}-_.]*"); + + @VisibleForTesting + static final String NO_REFERENCE_JOB = "-"; + + private final JenkinsFacade jenkins; + + /** Creates a new descriptor. */ + public ModelValidation() { + this(new JenkinsFacade()); + } + + @VisibleForTesting + ModelValidation(final JenkinsFacade jenkins) { + super(); + + this.jenkins = jenkins; + } + + /** + * Returns all available character set names. + * + * @return all available character set names + * @deprecated moved to Prism API Plugin + */ + @Deprecated + public ComboBoxModel getAllCharsets() { + return new ComboBoxModel(ALL_CHARSETS); + } + + /** + * Returns the default charset for the specified encoding string. If the default encoding is empty or {@code null}, + * or if the charset is not valid then the default encoding of the platform is returned. + * + * @param charset + * identifier of the character set + * + * @return the default charset for the specified encoding string + * @deprecated moved to Prism API Plugin + */ + @Deprecated + public Charset getCharset(@CheckForNull final String charset) { + try { + if (StringUtils.isNotBlank(charset)) { + return Charset.forName(charset); + } + } + catch (UnsupportedCharsetException | IllegalCharsetNameException exception) { + // ignore and return default + } + return Charset.defaultCharset(); + } + + /** + * Ensures that the specified ID is valid. + * + * @param id + * the custom ID of the tool + * + * @throws IllegalArgumentException + * if the ID is not valid + */ + public void ensureValidId(final String id) { + if (!isValidId(id)) { + throw new IllegalArgumentException("INVALID ID TODO"); + } + } + + /** + * Performs on-the-fly validation of the ID. + * + * @param id + * the custom ID of the tool + * + * @return the validation result + */ + public FormValidation validateId(final String id) { + if (isValidId(id)) { + return FormValidation.ok(); + } + return FormValidation.error("FIXME ERROR"); + } + + private boolean isValidId(final String id) { + return StringUtils.isEmpty(id) || VALID_ID_PATTERN.matcher(id).matches(); + } + + /** + * Performs on-the-fly validation of the character encoding. + * + * @param reportEncoding + * the character encoding + * + * @return the validation result + * @deprecated moved to Prism API Plugin + */ + @Deprecated + public FormValidation validateCharset(final String reportEncoding) { + try { + if (StringUtils.isBlank(reportEncoding) || Charset.isSupported(reportEncoding)) { + return FormValidation.ok(); + } + } + catch (IllegalCharsetNameException | UnsupportedCharsetException ignore) { + // throw a FormValidation error + } + return FormValidation.errorWithMarkup(createWrongEncodingErrorMessage()); + } + + @VisibleForTesting + static String createWrongEncodingErrorMessage() { + return "FIXME"; + } + + /** + * Performs on-the-fly validation on the ant pattern for input files. + * + * @param project + * the project that is configured + * @param pattern + * the file pattern + * + * @return the validation result + */ + public FormValidation doCheckPattern(final AbstractProject project, final String pattern) { + if (project != null) { // there is no workspace in pipelines + try { + FilePath workspace = project.getSomeWorkspace(); + if (workspace != null && workspace.exists()) { + return validatePatternInWorkspace(pattern, workspace); + } + } + catch (InterruptedException | IOException ignore) { + // ignore and return ok + } + } + + return FormValidation.ok(); + } + + private FormValidation validatePatternInWorkspace(final String pattern, final FilePath workspace) + throws IOException, InterruptedException { + String result = workspace.validateAntFileMask(pattern, FilePath.VALIDATE_ANT_FILE_MASK_BOUND); + if (result != null) { + return FormValidation.error(result); + } + return FormValidation.ok(); + } + + /** + * Performs on-the-fly validation on the source code directory. + * + * @param project + * the project that is configured + * @param sourceDirectory + * the file pattern + * + * @return the validation result + * @deprecated moved to Prism API Plugin + */ + @Deprecated + public FormValidation doCheckSourceDirectory(@AncestorInPath final AbstractProject project, + @QueryParameter final String sourceDirectory) { + if (project != null) { // there is no workspace in pipelines + try { + FilePath workspace = project.getSomeWorkspace(); + if (workspace != null && workspace.exists()) { + return validateRelativePath(sourceDirectory, workspace); + } + } + catch (InterruptedException | IOException ignore) { + // ignore and return ok + } + } + + return FormValidation.ok(); + } + + private FormValidation validateRelativePath( + @QueryParameter final String sourceDirectory, final FilePath workspace) throws IOException { + PathUtil pathUtil = new PathUtil(); + if (pathUtil.isAbsolute(sourceDirectory)) { + return FormValidation.ok(); + } + return workspace.validateRelativeDirectory(sourceDirectory); + } +} diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/config.jelly b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/config.jelly new file mode 100644 index 000000000..ccfe5aaf0 --- /dev/null +++ b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/config.jelly @@ -0,0 +1,40 @@ + + + +
+ + +
+ +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/config.properties b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/config.properties new file mode 100644 index 000000000..57d76ae65 --- /dev/null +++ b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/config.properties @@ -0,0 +1,14 @@ +sourceCodeRetention.title=Source Code Retention Strategy +parser.title=Code Coverage Tools +parser.add=Add Tool + +skipPublishingChecks.title=Skip publishing of checks to SCM provider platforms +failOnError.title=Fail the build if errors have been reported during the execution +aggregatingResults.title=Aggregate results of all tools into a single result + +threshold.100.percent=100% +threshold.0.percent=0% +description.health.limit=Configure the thresholds for the build health. If left empty then no health report is created. \ + If the actual coverage is between the provided thresholds then the build health is interpolated. +description.healthy=Report health as 100% when the coverage is less than this value (TODO: which). +description.unhealthy=Report health as 0% when the coverage is greater than this value (TODO: which). diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-aggregatingResults.html b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-aggregatingResults.html new file mode 100644 index 000000000..55e9eef75 --- /dev/null +++ b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-aggregatingResults.html @@ -0,0 +1,6 @@ +
+ By default, each static analysis result will be recorded as a separate result + that is presented as an individual Jenkins Action with separate UI and dashboard. If you rather prefer aggregation + of the results into a single result (i.e., single Jenkins Action), then activate this check box. You still can + see the distribution of issues grouped by static analysis tool in the UI. +
diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-failOnError.html b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-failOnError.html new file mode 100644 index 000000000..cb7750f50 --- /dev/null +++ b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-failOnError.html @@ -0,0 +1,5 @@ +
+ If there are errors while scanning the console log or files for issues (e.g., file pattern matches no files, source files + could not be copied, etc.) then the warning plugin will show these errors in a separate view but does not alter + the build state. If you would rather like to fail the build on such errors, please tick this checkbox. +
diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-healthy.html b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-healthy.html new file mode 100644 index 000000000..2d3dbbf93 --- /dev/null +++ b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-healthy.html @@ -0,0 +1,5 @@ +
+ The healthy threshold defines the limit of warnings for a healthy result: A build is considered as 100% healthy + when the number of issues is less than the specified threshold. Values less or equal zero are ignored. + So if you want to have a healthy build (i.e. 100%) only for zero warnings, then set this field to 1. +
diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-skipPublishingChecks.html b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-skipPublishingChecks.html new file mode 100644 index 000000000..869b42b98 --- /dev/null +++ b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-skipPublishingChecks.html @@ -0,0 +1,6 @@ +
+ If this option is unchecked, then the plugin automatically publishes the issues to corresponding SCM hosting platforms. + For example, if you are using this feature for a GitHub organization project, the warnings will be published to + GitHub through the Checks API. If this operation slows down your build or you don't want to publish the warnings to + SCM platforms, you can use this option to deactivate this feature. +
diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-sourceCodeEncoding.html b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-sourceCodeEncoding.html new file mode 100644 index 000000000..a7cb26ab1 --- /dev/null +++ b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-sourceCodeEncoding.html @@ -0,0 +1,6 @@ +
+ In order to correctly show all your affected source code files in the detail views, + the plugin must open these files with the correct character encoding (UTF-8, ISO-8859-1, etc.). + If you leave this field empty then the default encoding of the platform will be used. This might work but + is not recommended. +
diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-sourceDirectories.html b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-sourceDirectories.html new file mode 100644 index 000000000..ef2b04e12 --- /dev/null +++ b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-sourceDirectories.html @@ -0,0 +1,7 @@ +
+ Since the plugin also reads the affected source code files it needs to copy these files from the agent to the + controller. If these files are not part of the workspace (or checked out into a sub folder of the workspace) they + are not automatically found. So you can add one or more source code directories where the plugin tries to find + these files. Note, that due to security restrictions additional paths outside the workspace need to be registered + in Jenkins system configuration before they can be used here. +
diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-tools.html b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-tools.html new file mode 100644 index 000000000..e74c25299 --- /dev/null +++ b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-tools.html @@ -0,0 +1,9 @@ +
+ For each static analysis tool a dedicated parser or scanner will be used to read report files or produce issues in + any other way. If your tool is not yet supported you can define a new Groovy based parser in Jenkins + system configuration. You can reference this new parser afterwards when you select the tool 'Groovy Parser'. + Additionally, you provide a new parser within a new small plug-in. If the parser is useful for other teams + as well please share it and provide pull requests for the + Warnings Next Generation Plug-in and + the Analysis Parsers Library. +
diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageTool/config.jelly b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageTool/config.jelly new file mode 100644 index 000000000..cde4c43ce --- /dev/null +++ b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageTool/config.jelly @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageTool/config.properties b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageTool/config.properties new file mode 100644 index 000000000..316c9fb2e --- /dev/null +++ b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageTool/config.properties @@ -0,0 +1,4 @@ +title.name=Custom Name +description.name=Optional custom display name of the coverage report, overwrites the built-in name ''{0}''. +title.id=Custom ID +description.id=Optional custom ID (URL) of the coverage report, overwrites the built-in ID ''{0}''. diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageTool/help-id.html b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageTool/help-id.html new file mode 100644 index 000000000..280bf5d52 --- /dev/null +++ b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageTool/help-id.html @@ -0,0 +1,7 @@ +
+ The results of the selected tool are published using a unique ID (i.e. URL) which must not be already used by + another tool in this job. This ID is used as link to the results, so choose a short and meaningful name. + Allowed elements are characters, digits, dashes and underscores (more precisely, + the ID must match the regular expression \p{Alnum}[\p{Alnum}-_]*). + If you leave the ID field empty, then the built-in default ID of the registered tool will be used. +
diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageTool/help-name.html b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageTool/help-name.html new file mode 100644 index 000000000..6acc58fd7 --- /dev/null +++ b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageTool/help-name.html @@ -0,0 +1,4 @@ +
+ You can override the display name of the tool. This name is used in details views, trend captions, and hyper + links. If you leave the name field empty, then the built-in default name of the registered tool will be used. +
diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/Messages.properties b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/Messages.properties index d37256021..8aec305fa 100644 --- a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/Messages.properties +++ b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/Messages.properties @@ -1,3 +1,4 @@ +Recorder.Name=Record code coverage results Coverage.Not.Available=n/a Coverage.Link.Name=Coverage Report Coverage.Title=Coverage of ''{0}'' diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageRecorderTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageRecorderTest.java new file mode 100644 index 000000000..bd2d47512 --- /dev/null +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageRecorderTest.java @@ -0,0 +1,32 @@ +package io.jenkins.plugins.coverage.model; + +import org.junit.jupiter.api.Test; + +import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; +import org.jenkinsci.plugins.workflow.job.WorkflowJob; +import hudson.model.Result; +import hudson.model.Run; + +import io.jenkins.plugins.util.IntegrationTestWithJenkinsPerSuite; + +import static org.assertj.core.api.Assertions.*; + +/** + * Tests the class {@link CoverageRecorder}. + * + * @author Ullrich Hafner + */ +class CoverageRecorderTest extends IntegrationTestWithJenkinsPerSuite { + @Test + void pipelineForNoAdapter() { + WorkflowJob job = createPipeline(); + job.setDefinition(new CpsFlowDefinition( + "node {\n" + + " recordCoverage()\n" + + " }\n", true)); + + Run run = buildWithResult(job, Result.SUCCESS); + + assertThat(getConsoleLog(run)).isEqualTo("blub"); + } +} From cf6897399cfa8cecd29bb81fa6d21fd190be4a0a Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Sat, 1 Oct 2022 18:59:01 +0200 Subject: [PATCH 02/29] Make parsers selectable. --- .../plugins/coverage/model/CoverageTool.java | 57 +++++++++++++++++++ .../coverage/model/CoverageTool/config.jelly | 6 +- .../model/CoverageTool/config.properties | 1 + .../coverage/model/Messages.properties | 4 ++ 4 files changed, 67 insertions(+), 1 deletion(-) diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTool.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTool.java index 2f82c87f0..7e553187d 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTool.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTool.java @@ -11,13 +11,16 @@ import org.kohsuke.stapler.DataBoundSetter; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.verb.POST; +import org.jvnet.localizer.Localizable; import hudson.Extension; import hudson.model.AbstractDescribableImpl; import hudson.model.AbstractProject; import hudson.model.Descriptor; import hudson.model.Item; import hudson.util.FormValidation; +import hudson.util.ListBoxModel; +import io.jenkins.plugins.prism.SourceCodeRetention; import io.jenkins.plugins.util.JenkinsFacade; /** @@ -32,12 +35,27 @@ public class CoverageTool extends AbstractDescribableImpl implemen private String id = StringUtils.EMPTY; private String name = StringUtils.EMPTY; + private CoverageParser parser = CoverageParser.JACOCO; @DataBoundConstructor public CoverageTool() { // empty for stapler } + public CoverageParser getParser() { + return parser; + } + + /** + * Sets the parser to be used to read the input files. + * + * @param parser the parser to use + */ + @DataBoundSetter + public void setParser(final CoverageParser parser) { + this.parser = parser; + } + @VisibleForTesting void setJenkinsFacade(final JenkinsFacade jenkinsFacade) { jenkins = jenkinsFacade; @@ -125,6 +143,29 @@ public CoverageToolDescriptor() { super(); } + /** + * Returns a model with all {@link SourceCodeRetention} strategies. + * + * @param project + * the project that is configured + * @return a model with all {@link SourceCodeRetention} strategies. + */ + @POST + public ListBoxModel doFillParserItems(@AncestorInPath final AbstractProject project) { + if (JENKINS.hasPermission(Item.CONFIGURE, project)) { + ListBoxModel options = new ListBoxModel(); + add(options, CoverageParser.JACOCO); + add(options, CoverageParser.COBERTURA); + add(options, CoverageParser.PIT); + return options; + } + return new ListBoxModel(); + } + + private void add(final ListBoxModel options, final CoverageParser parser) { + options.add(parser.getDisplayName(), parser.name()); + } + /** * Performs on-the-fly validation of the ID. * @@ -164,4 +205,20 @@ public String getUrl() { return StringUtils.EMPTY; } } + + public enum CoverageParser { + COBERTURA(Messages._Parser_Cobertura()), + JACOCO(Messages._Parser_JaCoCo()), + PIT(Messages._Parser_PIT()); + + private final Localizable displayName; + + CoverageParser(final Localizable displayName) { + this.displayName = displayName; + } + + public String getDisplayName() { + return displayName.toString(); + } + } } diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageTool/config.jelly b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageTool/config.jelly index cde4c43ce..76681d89c 100644 --- a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageTool/config.jelly +++ b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageTool/config.jelly @@ -1,11 +1,15 @@ + + + + - + diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageTool/config.properties b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageTool/config.properties index 316c9fb2e..a36d229be 100644 --- a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageTool/config.properties +++ b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageTool/config.properties @@ -1,3 +1,4 @@ +parser.title=Coverage Parser title.name=Custom Name description.name=Optional custom display name of the coverage report, overwrites the built-in name ''{0}''. title.id=Custom ID diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/Messages.properties b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/Messages.properties index 8aec305fa..5033eac41 100644 --- a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/Messages.properties +++ b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/Messages.properties @@ -16,3 +16,7 @@ Column.DeltaLineCoverage=Line {0} Column.BranchCoverage=Branch Column.DeltaBranchCoverage=Branch {0} Column.LinesOfCode=LOC + +Parser.Cobertura=Cobertura +Parser.JaCoCo=JaCoCo +Parser.PIT=PIT Mutation Testing From c111f740fbb451124653c1449a16deabdd372c56 Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Sun, 9 Oct 2022 15:06:22 +0200 Subject: [PATCH 03/29] Reuse warnings plugin tools to scan the workspace for files and move new API to new package. --- plugin/pom.xml | 18 + .../coverage/metrics/AggregatedResult.java | 29 + .../{model => metrics}/CoverageRecorder.java | 101 +- .../{model => metrics}/CoverageTool.java | 67 +- .../plugins/coverage/metrics/FileFinder.java | 117 ++ .../coverage/metrics/FilesScanner.java | 132 ++ .../CoverageRecorder/config.jelly | 0 .../CoverageRecorder/config.properties | 0 .../help-aggregatingResults.html | 0 .../CoverageRecorder/help-failOnError.html | 0 .../CoverageRecorder/help-healthy.html | 0 .../help-skipPublishingChecks.html | 0 .../help-sourceCodeEncoding.html | 0 .../help-sourceDirectories.html | 0 .../CoverageRecorder/help-tools.html | 0 .../CoverageTool/config.jelly | 0 .../CoverageTool/config.properties | 0 .../CoverageTool/help-id.html | 0 .../CoverageTool/help-name.html | 0 .../coverage/metrics/Messages.properties | 5 + .../coverage/model/Messages.properties | 5 - .../metrics/CoverageRecorderTest.java | 77 ++ .../coverage/model/CoverageRecorderTest.java | 32 - .../plugins/coverage/metrics/jacoco.xml | 1179 +++++++++++++++++ 24 files changed, 1711 insertions(+), 51 deletions(-) create mode 100644 plugin/src/main/java/io/jenkins/plugins/coverage/metrics/AggregatedResult.java rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/CoverageRecorder.java (69%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/CoverageTool.java (73%) create mode 100644 plugin/src/main/java/io/jenkins/plugins/coverage/metrics/FileFinder.java create mode 100644 plugin/src/main/java/io/jenkins/plugins/coverage/metrics/FilesScanner.java rename plugin/src/main/resources/io/jenkins/plugins/coverage/{model => metrics}/CoverageRecorder/config.jelly (100%) rename plugin/src/main/resources/io/jenkins/plugins/coverage/{model => metrics}/CoverageRecorder/config.properties (100%) rename plugin/src/main/resources/io/jenkins/plugins/coverage/{model => metrics}/CoverageRecorder/help-aggregatingResults.html (100%) rename plugin/src/main/resources/io/jenkins/plugins/coverage/{model => metrics}/CoverageRecorder/help-failOnError.html (100%) rename plugin/src/main/resources/io/jenkins/plugins/coverage/{model => metrics}/CoverageRecorder/help-healthy.html (100%) rename plugin/src/main/resources/io/jenkins/plugins/coverage/{model => metrics}/CoverageRecorder/help-skipPublishingChecks.html (100%) rename plugin/src/main/resources/io/jenkins/plugins/coverage/{model => metrics}/CoverageRecorder/help-sourceCodeEncoding.html (100%) rename plugin/src/main/resources/io/jenkins/plugins/coverage/{model => metrics}/CoverageRecorder/help-sourceDirectories.html (100%) rename plugin/src/main/resources/io/jenkins/plugins/coverage/{model => metrics}/CoverageRecorder/help-tools.html (100%) rename plugin/src/main/resources/io/jenkins/plugins/coverage/{model => metrics}/CoverageTool/config.jelly (100%) rename plugin/src/main/resources/io/jenkins/plugins/coverage/{model => metrics}/CoverageTool/config.properties (100%) rename plugin/src/main/resources/io/jenkins/plugins/coverage/{model => metrics}/CoverageTool/help-id.html (100%) rename plugin/src/main/resources/io/jenkins/plugins/coverage/{model => metrics}/CoverageTool/help-name.html (100%) create mode 100644 plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/Messages.properties create mode 100644 plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CoverageRecorderTest.java delete mode 100644 plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageRecorderTest.java create mode 100644 plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/jacoco.xml diff --git a/plugin/pom.xml b/plugin/pom.xml index f7b7b994f..681e70e7c 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -23,6 +23,9 @@ -SNAPSHOT jenkinsci/code-coverage-api-plugin + 2.361.1 + 11 + 3.0.3 11.4 @@ -58,6 +61,11 @@ + + edu.hm.hafner + metric-model + 1.0.0-SNAPSHOT + io.jenkins.plugins ionicons-api @@ -142,6 +150,10 @@ prism-api ${prism-api.version} + + org.jenkins-ci.plugins.workflow + workflow-api + org.jsoup jsoup @@ -293,6 +305,12 @@ + + edu.hm.hafner + metric-model + 1.0.0-SNAPSHOT + compile + diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/AggregatedResult.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/AggregatedResult.java new file mode 100644 index 000000000..4cde4d7a6 --- /dev/null +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/AggregatedResult.java @@ -0,0 +1,29 @@ +package io.jenkins.plugins.coverage.metrics; + +import java.io.Serializable; + +import edu.hm.hafner.metric.Node; +import edu.hm.hafner.util.FilteredLog; + +/** + * FIXME: comment class. + * + * @author Ullrich Hafner + */ +public class AggregatedResult implements Serializable { + private final FilteredLog log; + private final Node root; + + public AggregatedResult(final FilteredLog log, final Node root) { + this.log = log; + this.root = root; + } + + public FilteredLog getLog() { + return log; + } + + public Node getRoot() { + return root; + } +} diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageRecorder.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageRecorder.java similarity index 69% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageRecorder.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageRecorder.java index fde5ef306..8553e75cb 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageRecorder.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageRecorder.java @@ -1,5 +1,6 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; +import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -24,6 +25,7 @@ import hudson.Launcher; import hudson.model.AbstractProject; import hudson.model.Item; +import hudson.model.Result; import hudson.model.Run; import hudson.model.TaskListener; import hudson.tasks.BuildStepDescriptor; @@ -35,9 +37,13 @@ import hudson.util.ListBoxModel; import jenkins.tasks.SimpleBuildStep; +import io.jenkins.plugins.coverage.metrics.CoverageTool.CoverageParser; +import io.jenkins.plugins.coverage.model.CoverageBuildAction; +import io.jenkins.plugins.coverage.model.CoverageNode; import io.jenkins.plugins.prism.CharsetValidation; import io.jenkins.plugins.prism.SourceCodeDirectory; import io.jenkins.plugins.prism.SourceCodeRetention; +import io.jenkins.plugins.util.EnvironmentResolver; import io.jenkins.plugins.util.JenkinsFacade; import io.jenkins.plugins.util.LogHandler; @@ -55,9 +61,11 @@ public class CoverageRecorder extends Recorder implements SimpleBuildStep { private String sourceCodeEncoding = StringUtils.EMPTY; private Set sourceDirectories = new HashSet<>(); private boolean skipPublishingChecks = false; - private SourceCodeRetention sourceCodeRetention = SourceCodeRetention.NEVER; + private SourceCodeRetention sourceCodeRetention = SourceCodeRetention.LAST_BUILD; private List tools = new ArrayList<>(); + private boolean failOnError; + private boolean enabledForFailure; private int healthy; private int unhealthy; @@ -110,6 +118,40 @@ public void setSkipPublishingChecks(final boolean skipPublishingChecks) { this.skipPublishingChecks = skipPublishingChecks; } + /** + * Determines whether to fail the build on errors during the step of recording issues. + * + * @param failOnError + * if {@code true} then the build will be failed on errors, {@code false} then errors are only reported in + * the UI + */ + @DataBoundSetter + @SuppressWarnings("unused") // Used by Stapler + public void setFailOnError(final boolean failOnError) { + this.failOnError = failOnError; + } + + @SuppressWarnings({"PMD.BooleanGetMethodName", "unused"}) + public boolean getFailOnError() { + return failOnError; + } + + /** + * Returns whether recording should be enabled for failed builds as well. + * + * @return {@code true} if recording should be enabled for failed builds as well, {@code false} if recording is + * enabled for successful or unstable builds only + */ + @SuppressWarnings("PMD.BooleanGetMethodName") + public boolean getEnabledForFailure() { + return enabledForFailure; + } + + @DataBoundSetter + public void setEnabledForFailure(final boolean enabledForFailure) { + this.enabledForFailure = enabledForFailure; + } + /** * Returns the encoding to use to read source files. * @@ -189,14 +231,59 @@ public BuildStepMonitor getRequiredMonitorService() { @Override public void perform(@NonNull final Run run, @NonNull final FilePath workspace, @NonNull final EnvVars env, @NonNull final Launcher launcher, @NonNull final TaskListener listener) throws InterruptedException { + Result overallResult = run.getResult(); LogHandler logHandler = new LogHandler(listener, "Coverage"); - FilteredLog log = new FilteredLog("Errors while recording code coverage:"); - log.logInfo("Recording coverage results"); - - + if (enabledForFailure || overallResult == null || overallResult.isBetterOrEqualTo(Result.UNSTABLE)) { + FilteredLog log = new FilteredLog("Errors while recording code coverage:"); + log.logInfo("Recording coverage results"); + + if (tools.isEmpty()) { + log.logError("No tools defined that will record the coverage files"); + run.setResult(Result.FAILURE); // use StageResultHandler of warnings plugin + logHandler.log(log); + } + else { + for (CoverageTool tool : tools) { + LogHandler toolHandler = new LogHandler(listener, tool.getActualName()); + CoverageParser parser = tool.getParser(); + if (StringUtils.isBlank(tool.getPattern())) { + toolHandler.log("Using default pattern '%s' since user defined pattern is not set", + parser.getDefaultPattern()); + } + + String expandedPattern = expandPattern(run, tool.getActualPattern()); + if (!expandedPattern.equals(tool.getActualPattern())) { + log.logInfo("Expanding pattern '%s' to '%s'", tool.getActualPattern(), expandedPattern); + } + + try { + AggregatedResult result = workspace.act( + new FilesScanner(expandedPattern, "UTF-8", false, parser)); + log.merge(result.getLog()); + } + catch (IOException exception) { + log.logException(exception, "Exception during parsing"); + } + + toolHandler.log(log); + } + } + } + else { + logHandler.log("Skipping execution of coverage recorder since overall result is '%s'", overallResult); + } + } + private String expandPattern(final Run run, final String actualPattern) { + try { + EnvironmentResolver environmentResolver = new EnvironmentResolver(); - logHandler.log(log); + return environmentResolver.expandEnvironmentVariables( + run.getEnvironment(TaskListener.NULL), actualPattern); + } + catch (IOException | InterruptedException ignore) { + return actualPattern; // fallback, no expansion + } } @Override diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTool.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageTool.java similarity index 73% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTool.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageTool.java index 7e553187d..82e7e535b 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTool.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageTool.java @@ -1,10 +1,16 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import java.io.Serializable; +import java.util.function.Supplier; import org.apache.commons.lang3.StringUtils; +import edu.hm.hafner.metric.parser.CoberturaParser; +import edu.hm.hafner.metric.parser.JacocoParser; +import edu.hm.hafner.metric.parser.PitestParser; +import edu.hm.hafner.metric.parser.XmlParser; import edu.hm.hafner.util.VisibleForTesting; +import edu.umd.cs.findbugs.annotations.CheckForNull; import org.kohsuke.stapler.AncestorInPath; import org.kohsuke.stapler.DataBoundConstructor; @@ -20,11 +26,13 @@ import hudson.util.FormValidation; import hudson.util.ListBoxModel; +import io.jenkins.plugins.coverage.model.FileCoverageNode; +import io.jenkins.plugins.coverage.model.ModelValidation; import io.jenkins.plugins.prism.SourceCodeRetention; import io.jenkins.plugins.util.JenkinsFacade; /** - * A coverage tool that can produce a {@link CoverageNode coverage tree} by parsing a given report file. + * A coverage tool that can produce a {@link FileCoverageNode coverage tree} by parsing a given report file. * * @author Ullrich Hafner */ @@ -35,6 +43,7 @@ public class CoverageTool extends AbstractDescribableImpl implemen private String id = StringUtils.EMPTY; private String name = StringUtils.EMPTY; + private String pattern = StringUtils.EMPTY; private CoverageParser parser = CoverageParser.JACOCO; @DataBoundConstructor @@ -123,9 +132,38 @@ public String getName() { * @see #setName(String) */ public String getActualName() { - return StringUtils.defaultIfBlank(getName(), getDescriptor().getDisplayName()); + return StringUtils.defaultIfBlank(getName(), getParser().getDisplayName()); } + /** + * Sets the Ant file-set pattern of files to work with. If the pattern is undefined then the console log is + * scanned. + * + * @param pattern + * the pattern to use + */ + @DataBoundSetter + public void setPattern(final String pattern) { + this.pattern = pattern; + } + + @CheckForNull + public String getPattern() { + return pattern; + } + + /** + * Returns the actual pattern to work with. If no user defined pattern is given, then the default pattern is + * returned. + * + * @return the name + * @see #setPattern(String) + */ + public String getActualPattern() { + return StringUtils.defaultIfBlank(pattern, parser.getDefaultPattern()); + } + + @Override public CoverageToolDescriptor getDescriptor() { return (CoverageToolDescriptor) jenkins.getDescriptorOrDie(getClass()); @@ -206,19 +244,34 @@ public String getUrl() { } } + /** + * Supported coverage parsers. + */ public enum CoverageParser { - COBERTURA(Messages._Parser_Cobertura()), - JACOCO(Messages._Parser_JaCoCo()), - PIT(Messages._Parser_PIT()); + COBERTURA(Messages._Parser_Cobertura(), "**/cobertura.xml", CoberturaParser::new), + JACOCO(Messages._Parser_JaCoCo(), "**/jacoco.xml", JacocoParser::new), + PIT(Messages._Parser_PIT(), "**/pit.xml", PitestParser::new); private final Localizable displayName; + private final String defaultPattern; + private final Supplier parserSupplier; - CoverageParser(final Localizable displayName) { + CoverageParser(final Localizable displayName, final String defaultPattern, final Supplier parserSupplier) { this.displayName = displayName; + this.defaultPattern = defaultPattern; + this.parserSupplier = parserSupplier; } public String getDisplayName() { return displayName.toString(); } + + public String getDefaultPattern() { + return defaultPattern; + } + + public XmlParser createParser() { + return parserSupplier.get(); + } } } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/FileFinder.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/FileFinder.java new file mode 100644 index 000000000..746f1f261 --- /dev/null +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/FileFinder.java @@ -0,0 +1,117 @@ +package io.jenkins.plugins.coverage.metrics; + +import java.io.File; +import java.io.IOException; + +import org.apache.commons.lang3.StringUtils; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.types.selectors.TypeSelector; +import org.apache.tools.ant.types.selectors.TypeSelector.FileType; + +import hudson.remoting.VirtualChannel; +import jenkins.MasterToSlaveFileCallable; + +/** + * Scans the workspace and finds all files matching a given ant pattern. + * + * @author Ullrich Hafner + */ +// FIXME: move to plugin-util +public class FileFinder extends MasterToSlaveFileCallable { + private static final long serialVersionUID = 2970029366847565970L; + + private final String includesPattern; + private final String excludesPattern; + private final boolean followSymlinks; + + /** + * Creates a new instance of {@link FileFinder}. + * + * @param includesPattern + * the ant file includes pattern to scan for + */ + public FileFinder(final String includesPattern) { + this(includesPattern, StringUtils.EMPTY); + } + + /** + * Creates a new instance of {@link FileFinder}. + * + * @param includesPattern + * the ant file includes pattern to scan for + * @param excludesPattern + * the ant file excludes pattern to scan for + */ + public FileFinder(final String includesPattern, final String excludesPattern) { + this(includesPattern, excludesPattern, false); + } + + /** + * Creates a new instance of {@link FileFinder}. + * + * @param includesPattern + * the ant file includes pattern to scan for + * @param excludesPattern + * the ant file excludes pattern to scan for + * @param followSymlinks + * if the scanner should traverse symbolic links + */ + public FileFinder(final String includesPattern, final String excludesPattern, final boolean followSymlinks) { + super(); + + this.includesPattern = includesPattern; + this.excludesPattern = excludesPattern; + this.followSymlinks = followSymlinks; + } + + /** + * Returns an array with the file names of the specified file pattern that have been found in the workspace. + * + * @param workspace + * root directory of the workspace + * @param channel + * not used + * + * @return the file names of all found files + * @throws IOException + * if the workspace could not be read + */ + @Override + public String[] invoke(final File workspace, final VirtualChannel channel) throws IOException { + return find(workspace); + } + + /** + * Returns an array with the file names of the specified file pattern that have been found in the workspace. + * + * @param workspace + * root directory of the workspace + * + * @return the file names of all found files + */ + public String[] find(final File workspace) { + try { + FileSet fileSet = new FileSet(); + Project antProject = new Project(); + fileSet.setProject(antProject); + fileSet.setDir(workspace); + fileSet.setIncludes(includesPattern); + TypeSelector selector = new TypeSelector(); + FileType fileType = new FileType(); + fileType.setValue(FileType.FILE); + selector.setType(fileType); + fileSet.addType(selector); + if (StringUtils.isNotBlank(excludesPattern)) { + fileSet.setExcludes(excludesPattern); + } + fileSet.setFollowSymlinks(followSymlinks); + + return fileSet.getDirectoryScanner(antProject).getIncludedFiles(); + } + catch (BuildException ignored) { + return new String[0]; // as fallback do not return any file + } + } +} diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/FilesScanner.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/FilesScanner.java new file mode 100644 index 000000000..eea079031 --- /dev/null +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/FilesScanner.java @@ -0,0 +1,132 @@ +package io.jenkins.plugins.coverage.metrics; + +import java.io.File; +import java.io.IOException; +import java.io.Serializable; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; + +import edu.hm.hafner.metric.ModuleNode; +import edu.hm.hafner.metric.Node; +import edu.hm.hafner.metric.parser.XmlParser; +import edu.hm.hafner.util.FilteredLog; + +import hudson.remoting.VirtualChannel; +import jenkins.MasterToSlaveFileCallable; + +import io.jenkins.plugins.coverage.metrics.CoverageTool.CoverageParser; + +/** + * Scans files that match a specified Ant files pattern for issues and aggregates the coverage files into a single {@link + * Node node} instance. This callable will be invoked on an agent so all fields and the returned node need to + * be {@link Serializable}. + * + * @author Ullrich Hafner + */ +public class FilesScanner extends MasterToSlaveFileCallable { + private static final long serialVersionUID = -4242755766101768715L; + + private final String filePattern; + private final CoverageParser parser; + private final String encoding; + private final boolean followSymbolicLinks; + + /** + * Creates a new instance of {@link FilesScanner}. + * + * @param filePattern + * ant file-set pattern to scan for files to parse + * @param encoding + * encoding of the files to parse + * @param followSymbolicLinks + * if the scanner should traverse symbolic links + * @param parser + * the parser to use + */ + public FilesScanner(final String filePattern, final String encoding, + final boolean followSymbolicLinks, final CoverageParser parser) { + super(); + + this.filePattern = filePattern; + this.parser = parser; + this.encoding = encoding; + this.followSymbolicLinks = followSymbolicLinks; + } + + @Override + public AggregatedResult invoke(final File workspace, final VirtualChannel channel) { + FilteredLog log = new FilteredLog("Errors while parsing with " + parser.getDisplayName()); + log.logInfo("Searching for all files in '%s' that match the pattern '%s'", + workspace.getAbsolutePath(), filePattern); + + String[] fileNames = new FileFinder(filePattern, StringUtils.EMPTY, followSymbolicLinks).find(workspace); + if (fileNames.length == 0) { + log.logError("No files found for pattern '%s'. Configuration error?", filePattern); + + return new AggregatedResult(log, new ModuleNode(filePattern)); + } + else { + log.logInfo("-> found %s", plural(fileNames.length, "file")); + + return new AggregatedResult(log, scanFiles(workspace, fileNames, log)); + } + + } + + private Node scanFiles(final File workspace, final String[] fileNames, final FilteredLog log) { + List nodes = new ArrayList<>(); + for (String fileName : fileNames) { + Path file = workspace.toPath().resolve(fileName); + + if (!Files.isReadable(file)) { + log.logError("Skipping file '%s' because Jenkins has no permission to read the file", fileName); + } + else if (isEmpty(file)) { + log.logError("Skipping file '%s' because it's empty", fileName); + } + else { + nodes.add(aggregateIssuesOfFile(file, log)); + } + } + return nodes.stream().reduce(Node::combineWith).orElse(new ModuleNode("Empty results for " + fileNames)); + } + + private boolean isEmpty(final Path file) { + try { + return Files.size(file) <= 0; + } + catch (IOException e) { + return true; + } + } + + private ModuleNode aggregateIssuesOfFile(final Path file, final FilteredLog log) { + try { + // FIXME: encoding? + XmlParser xmlParser = parser.createParser(); + ModuleNode node = xmlParser.parse(Files.newBufferedReader(file)); + log.logInfo("Successfully parsed file '%s'", file); + node.getMetricsDistribution().forEach((m, v) -> log.logInfo("%s: %s", m, v)); + return node; + } + catch (IOException exception) { + log.logException(exception, "Parsing of file '%s' failed due to an exception:", file); + return new ModuleNode(file.toString()); + } + } + + @SuppressWarnings("PMD.AvoidLiteralsInIfCondition") + private String plural(final int count, final String itemName) { + StringBuilder builder = new StringBuilder(itemName); + if (count != 1) { + builder.append('s'); + } + builder.insert(0, ' '); + builder.insert(0, count); + return builder.toString(); + } +} diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/config.jelly b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageRecorder/config.jelly similarity index 100% rename from plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/config.jelly rename to plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageRecorder/config.jelly diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/config.properties b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageRecorder/config.properties similarity index 100% rename from plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/config.properties rename to plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageRecorder/config.properties diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-aggregatingResults.html b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageRecorder/help-aggregatingResults.html similarity index 100% rename from plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-aggregatingResults.html rename to plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageRecorder/help-aggregatingResults.html diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-failOnError.html b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageRecorder/help-failOnError.html similarity index 100% rename from plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-failOnError.html rename to plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageRecorder/help-failOnError.html diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-healthy.html b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageRecorder/help-healthy.html similarity index 100% rename from plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-healthy.html rename to plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageRecorder/help-healthy.html diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-skipPublishingChecks.html b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageRecorder/help-skipPublishingChecks.html similarity index 100% rename from plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-skipPublishingChecks.html rename to plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageRecorder/help-skipPublishingChecks.html diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-sourceCodeEncoding.html b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageRecorder/help-sourceCodeEncoding.html similarity index 100% rename from plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-sourceCodeEncoding.html rename to plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageRecorder/help-sourceCodeEncoding.html diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-sourceDirectories.html b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageRecorder/help-sourceDirectories.html similarity index 100% rename from plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-sourceDirectories.html rename to plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageRecorder/help-sourceDirectories.html diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-tools.html b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageRecorder/help-tools.html similarity index 100% rename from plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageRecorder/help-tools.html rename to plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageRecorder/help-tools.html diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageTool/config.jelly b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageTool/config.jelly similarity index 100% rename from plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageTool/config.jelly rename to plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageTool/config.jelly diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageTool/config.properties b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageTool/config.properties similarity index 100% rename from plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageTool/config.properties rename to plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageTool/config.properties diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageTool/help-id.html b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageTool/help-id.html similarity index 100% rename from plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageTool/help-id.html rename to plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageTool/help-id.html diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageTool/help-name.html b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageTool/help-name.html similarity index 100% rename from plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageTool/help-name.html rename to plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageTool/help-name.html diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/Messages.properties b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/Messages.properties new file mode 100644 index 000000000..f8c65462b --- /dev/null +++ b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/Messages.properties @@ -0,0 +1,5 @@ +Recorder.Name=Record code coverage results + +Parser.Cobertura=Cobertura +Parser.JaCoCo=JaCoCo +Parser.PIT=PIT Mutation Testing diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/Messages.properties b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/Messages.properties index 5033eac41..d37256021 100644 --- a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/Messages.properties +++ b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/Messages.properties @@ -1,4 +1,3 @@ -Recorder.Name=Record code coverage results Coverage.Not.Available=n/a Coverage.Link.Name=Coverage Report Coverage.Title=Coverage of ''{0}'' @@ -16,7 +15,3 @@ Column.DeltaLineCoverage=Line {0} Column.BranchCoverage=Branch Column.DeltaBranchCoverage=Branch {0} Column.LinesOfCode=LOC - -Parser.Cobertura=Cobertura -Parser.JaCoCo=JaCoCo -Parser.PIT=PIT Mutation Testing diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CoverageRecorderTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CoverageRecorderTest.java new file mode 100644 index 000000000..447df7129 --- /dev/null +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CoverageRecorderTest.java @@ -0,0 +1,77 @@ +package io.jenkins.plugins.coverage.metrics; + +import org.junit.jupiter.api.Test; + +import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; +import org.jenkinsci.plugins.workflow.job.WorkflowJob; +import hudson.model.Result; +import hudson.model.Run; + +import io.jenkins.plugins.util.IntegrationTestWithJenkinsPerSuite; + +import static org.assertj.core.api.Assertions.*; + +/** + * Tests the class {@link CoverageRecorder}. + * + * @author Ullrich Hafner + */ +class CoverageRecorderTest extends IntegrationTestWithJenkinsPerSuite { + @Test + void shouldFailWithoutAdapter() { + WorkflowJob job = createPipeline(); + job.setDefinition(new CpsFlowDefinition( + "node {\n" + + " recordCoverage()\n" + + " }\n", true)); + + Run run = buildWithResult(job, Result.FAILURE); + + assertThat(getConsoleLog(run)).contains("[-ERROR-] No tools defined that will record the coverage files"); + } + + @Test + void shouldIgnoreEmptyListOfFiles() { + WorkflowJob job = createPipeline(); + job.setDefinition(new CpsFlowDefinition( + "node {\n" + + " recordCoverage tools: [[parser: 'JACOCO']]\n" + + " }\n", true)); + + Run run = buildWithResult(job, Result.SUCCESS); + + assertThat(getConsoleLog(run)) + .contains("[JaCoCo] Using default pattern '**/jacoco.xml' since user defined pattern is not set", + "[-ERROR-] No files found for pattern '**/jacoco.xml'. Configuration error?") + .containsPattern("Searching for all files in '.*' that match the pattern '\\*\\*/jacoco.xml'") + .doesNotContain("Expanding pattern"); + } + + @Test + void shouldParseFileWithJaCoCo() { + WorkflowJob job = createPipeline(); + copyFilesToWorkspace(job, "jacoco.xml"); + job.setDefinition(new CpsFlowDefinition( + "node {\n" + + " recordCoverage tools: [[parser: 'JACOCO']]\n" + + " }\n", true)); + + Run run = buildWithResult(job, Result.SUCCESS); + + assertThat(getConsoleLog(run)) + .contains("[JaCoCo] Using default pattern '**/jacoco.xml' since user defined pattern is not set", + "[JaCoCo] -> found 1 file", + "[JaCoCo] MODULE: 100.00% (1/1)", + "[JaCoCo] PACKAGE: 100.00% (1/1)", + "[JaCoCo] FILE: 70.00% (7/10)", + "[JaCoCo] CLASS: 83.33% (15/18)", + "[JaCoCo] METHOD: 95.10% (97/102)", + "[JaCoCo] INSTRUCTION: 93.33% (1260/1350)", + "[JaCoCo] LINE: 91.02% (294/323)", + "[JaCoCo] BRANCH: 93.97% (109/116)", + "[JaCoCo] COMPLEXITY: [COMPLEXITY]: 160") + .containsPattern("Searching for all files in '.*' that match the pattern '\\*\\*/jacoco.xml'") + .containsPattern("Successfully parsed file '.*/jacoco.xml'") + .doesNotContain("Expanding pattern"); + } +} diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageRecorderTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageRecorderTest.java deleted file mode 100644 index bd2d47512..000000000 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageRecorderTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.jenkins.plugins.coverage.model; - -import org.junit.jupiter.api.Test; - -import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; -import org.jenkinsci.plugins.workflow.job.WorkflowJob; -import hudson.model.Result; -import hudson.model.Run; - -import io.jenkins.plugins.util.IntegrationTestWithJenkinsPerSuite; - -import static org.assertj.core.api.Assertions.*; - -/** - * Tests the class {@link CoverageRecorder}. - * - * @author Ullrich Hafner - */ -class CoverageRecorderTest extends IntegrationTestWithJenkinsPerSuite { - @Test - void pipelineForNoAdapter() { - WorkflowJob job = createPipeline(); - job.setDefinition(new CpsFlowDefinition( - "node {\n" - + " recordCoverage()\n" - + " }\n", true)); - - Run run = buildWithResult(job, Result.SUCCESS); - - assertThat(getConsoleLog(run)).isEqualTo("blub"); - } -} diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/jacoco.xml b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/jacoco.xml new file mode 100644 index 000000000..49915a685 --- /dev/null +++ b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/jacoco.xml @@ -0,0 +1,1179 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 076631ef57a1b1baaa2843461d5db3f5fea7ab73 Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Sun, 16 Oct 2022 00:13:59 +0200 Subject: [PATCH 04/29] Fix most of the unit tests. Started to migrate one integration test. --- plugin/pom.xml | 26 +- .../plugins/coverage/CoverageProcessor.java | 5 +- .../coverage/metrics/CoverageRecorder.java | 28 +- .../coverage/metrics/CoverageTool.java | 4 +- .../coverage/metrics/FilesScanner.java | 7 +- .../model/ChangeCoverageTableModel.java | 60 +- .../coverage/model/CodeDeltaCalculator.java | 37 +- .../plugins/coverage/model/Coverage.java | 376 --------- .../coverage/model/CoverageBuildAction.java | 234 +++--- .../coverage/model/CoverageFormatter.java | 73 ++ .../coverage/model/CoverageJobAction.java | 3 +- .../plugins/coverage/model/CoverageLeaf.java | 72 -- .../coverage/model/CoverageMetric.java | 185 ----- .../plugins/coverage/model/CoverageNode.java | 780 ------------------ .../coverage/model/CoverageNodeConverter.java | 114 --- .../coverage/model/CoveragePercentage.java | 2 + .../coverage/model/CoverageReporter.java | 109 ++- .../coverage/model/CoverageTableModel.java | 99 ++- .../coverage/model/CoverageTreeCreator.java | 145 ++-- .../coverage/model/CoverageViewModel.java | 106 +-- .../coverage/model/CoverageXmlStream.java | 308 +------ .../coverage/model/FileChangesProcessor.java | 90 +- .../coverage/model/FileCoverageNode.java | 217 ----- .../coverage/model/FilePathValidator.java | 11 +- .../model/IndirectCoverageChangesTable.java | 57 +- .../coverage/model/MethodCoverageNode.java | 69 -- .../coverage/model/PackageCoverageNode.java | 30 - .../plugins/coverage/model/SafeFraction.java | 80 -- .../coverage/model/SourceViewModel.java | 4 +- .../charts/CoverageSeriesBuilder.java | 22 +- .../visualization/code/SourceCodeFacade.java | 24 +- .../visualization/code/SourceCodePainter.java | 6 +- .../dashboard/ChangeCoverage.java | 13 +- .../dashboard/ChangeCoverageDelta.java | 8 +- .../dashboard/CoverageColumn.java | 10 +- .../dashboard/CoverageColumnType.java | 7 +- .../dashboard/IndirectCoverageChanges.java | 13 +- .../dashboard/ProjectCoverage.java | 13 +- .../dashboard/ProjectCoverageDelta.java | 7 +- .../tree/TreeMapNodeConverter.java | 27 +- .../metrics/CoverageRecorderTest.java | 2 +- .../coverage/model/AbstractCoverageTest.java | 87 +- .../model/CodeDeltaCalculatorTest.java | 64 +- .../model/CoverageBuildActionTest.java | 170 ++-- .../coverage/model/CoverageJobActionTest.java | 12 +- .../coverage/model/CoverageLeafTest.java | 32 - .../coverage/model/CoverageMetricTest.java | 73 -- .../coverage/model/CoverageNodeTest.java | 562 ------------- .../model/CoveragePercentageTest.java | 5 +- .../coverage/model/CoveragePluginITest.java | 24 +- .../model/CoveragePluginSourceITest.java | 11 +- .../coverage/model/CoverageResultTest.java | 96 --- .../plugins/coverage/model/CoverageTest.java | 136 --- .../model/CoverageTreeCreatorTest.java | 96 +-- .../coverage/model/CoverageViewModelTest.java | 47 +- .../coverage/model/CoverageXmlStreamTest.java | 164 ---- .../DeclarativePipelineSupportITest.java | 5 +- ...DeltaComputationVsReferenceBuildITest.java | 24 +- .../DockerAndSourceCodeRenderingITest.java | 8 +- .../model/FileChangesProcessorTest.java | 97 +-- .../coverage/model/FileCoverageNodeTest.java | 92 --- .../coverage/model/FilePathValidatorTest.java | 65 +- .../coverage/model/GitForensicsITest.java | 57 +- .../plugins/coverage/model/JobDslITest.java | 4 +- .../model/MethodCoverageNodeTest.java | 68 -- .../model/MultipleInvocationsOfStepITest.java | 6 +- .../model/PackageCoverageNodeTest.java | 61 -- .../coverage/model/SafeFractionTest.java | 43 - .../model/testutil/CoverageStubs.java | 87 +- .../charts/CoverageSeriesBuilderTest.java | 15 +- .../code/SourceCodeFacadeTest.java | 41 +- .../dashboard/ChangeCoverageDeltaTest.java | 5 +- .../dashboard/ChangeCoverageTest.java | 5 +- .../dashboard/CoverageColumnTest.java | 13 +- .../dashboard/CoverageColumnTypeTest.java | 6 +- .../IndirectCoverageChangesTest.java | 5 +- .../dashboard/ProjectCoverageDeltaTest.java | 5 +- .../dashboard/ProjectCoverageTest.java | 5 +- .../tree/TreeMapNodeConverterTest.java | 18 +- .../visualization/code/SourcecodeTestCC.html | 84 +- 80 files changed, 1232 insertions(+), 4649 deletions(-) delete mode 100644 plugin/src/main/java/io/jenkins/plugins/coverage/model/Coverage.java create mode 100644 plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageFormatter.java delete mode 100644 plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageLeaf.java delete mode 100644 plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageMetric.java delete mode 100644 plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageNode.java delete mode 100644 plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageNodeConverter.java delete mode 100644 plugin/src/main/java/io/jenkins/plugins/coverage/model/FileCoverageNode.java delete mode 100644 plugin/src/main/java/io/jenkins/plugins/coverage/model/MethodCoverageNode.java delete mode 100644 plugin/src/main/java/io/jenkins/plugins/coverage/model/PackageCoverageNode.java delete mode 100644 plugin/src/main/java/io/jenkins/plugins/coverage/model/SafeFraction.java delete mode 100644 plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageLeafTest.java delete mode 100644 plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageMetricTest.java delete mode 100644 plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageNodeTest.java delete mode 100644 plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageResultTest.java delete mode 100644 plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTest.java delete mode 100644 plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageXmlStreamTest.java delete mode 100644 plugin/src/test/java/io/jenkins/plugins/coverage/model/FileCoverageNodeTest.java delete mode 100644 plugin/src/test/java/io/jenkins/plugins/coverage/model/MethodCoverageNodeTest.java delete mode 100644 plugin/src/test/java/io/jenkins/plugins/coverage/model/PackageCoverageNodeTest.java delete mode 100644 plugin/src/test/java/io/jenkins/plugins/coverage/model/SafeFractionTest.java diff --git a/plugin/pom.xml b/plugin/pom.xml index 681e70e7c..743d47969 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -23,8 +23,13 @@ -SNAPSHOT jenkinsci/code-coverage-api-plugin - 2.361.1 + 2.361 + 2.361.2 11 + 11 + 11 + 11 + 11 3.0.3 11.4 @@ -211,11 +216,6 @@ workflow-durable-task-step test - - org.jenkins-ci.plugins.workflow - workflow-api - test - org.jenkins-ci.plugins.workflow workflow-support @@ -305,12 +305,6 @@ - - edu.hm.hafner - metric-model - 1.0.0-SNAPSHOT - compile - @@ -334,6 +328,14 @@ true + + org.apache.maven.plugins + maven-compiler-plugin + + 10 + 10 + + diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/CoverageProcessor.java b/plugin/src/main/java/io/jenkins/plugins/coverage/CoverageProcessor.java index abf79b13c..f03e39d32 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/CoverageProcessor.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/CoverageProcessor.java @@ -44,7 +44,6 @@ import io.jenkins.plugins.coverage.detector.Detectable; import io.jenkins.plugins.coverage.detector.ReportDetector; import io.jenkins.plugins.coverage.exception.CoverageException; -import io.jenkins.plugins.coverage.model.CoverageReporter; import io.jenkins.plugins.coverage.source.SourceFileResolver; import io.jenkins.plugins.coverage.source.SourceFileResolver.SourceFileResolverLevel; import io.jenkins.plugins.coverage.targets.CoverageElement; @@ -128,7 +127,8 @@ public void performCoverageReport(final List reportAdapte HealthReport healthReport = processThresholds(results, globalThresholds, action); action.setHealthReport(healthReport); - // Transform the old model to the new model + // FIXME: Transform the old model to the new model + /* CoverageReporter coverageReporter = new CoverageReporter(); coverageReporter.run(coverageReport.getRoot(), run, workspace, listener, healthReport, scm, sourceDirectories, sourceCodeEncoding, mapSourceCodeRetention()); @@ -136,6 +136,7 @@ public void performCoverageReport(final List reportAdapte if (failBuildIfCoverageDecreasedInChangeRequest) { failBuildIfChangeRequestDecreasedCoverage(coverageReport, action); } + */ } private SourceCodeRetention mapSourceCodeRetention() { diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageRecorder.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageRecorder.java index 8553e75cb..f31078fe6 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageRecorder.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageRecorder.java @@ -9,6 +9,7 @@ import org.apache.commons.lang3.StringUtils; +import edu.hm.hafner.metric.Node; import edu.hm.hafner.util.FilteredLog; import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; @@ -39,7 +40,7 @@ import io.jenkins.plugins.coverage.metrics.CoverageTool.CoverageParser; import io.jenkins.plugins.coverage.model.CoverageBuildAction; -import io.jenkins.plugins.coverage.model.CoverageNode; +import io.jenkins.plugins.coverage.model.CoverageReporter; import io.jenkins.plugins.prism.CharsetValidation; import io.jenkins.plugins.prism.SourceCodeDirectory; import io.jenkins.plugins.prism.SourceCodeRetention; @@ -51,7 +52,7 @@ * A pipeline {@code Step} or Freestyle or Maven {@link Recorder} that reads and parses coverage results in a build and * adds the results to the persisted build results. *

- * Stores the created issues in a {@link CoverageNode}. This result is then attached to the {@link Run build} by + * Stores the created issues in a {@link Node}. This result is then attached to the {@link Run build} by * registering a {@link CoverageBuildAction}. *

* @@ -68,6 +69,7 @@ public class CoverageRecorder extends Recorder implements SimpleBuildStep { private boolean enabledForFailure; private int healthy; private int unhealthy; + private String scm; /** * Creates a new instance of {@link CoverageRecorder}. @@ -223,6 +225,24 @@ public void setSourceCodeRetention(final SourceCodeRetention sourceCodeRetention this.sourceCodeRetention = sourceCodeRetention; } + /** + * Sets the SCM that should be used to find the reference build for. The reference recorder will select the SCM + * based on a substring comparison, there is no need to specify the full name. + * + * @param scm + * the ID of the SCM to use (a substring of the full ID) + */ + @DataBoundSetter + public void setScm(final String scm) { + this.scm = scm; + } + + public String getScm() { + return scm; + } + + + @Override public BuildStepMonitor getRequiredMonitorService() { return BuildStepMonitor.NONE; @@ -260,6 +280,10 @@ public void perform(@NonNull final Run run, @NonNull final FilePath worksp AggregatedResult result = workspace.act( new FilesScanner(expandedPattern, "UTF-8", false, parser)); log.merge(result.getLog()); + + CoverageReporter reporter = new CoverageReporter(); + reporter.run(result.getRoot(), run, workspace, listener, getScm(), getSourceDirectoriesPaths(), + getSourceCodeEncoding(), getSourceCodeRetention()); } catch (IOException exception) { log.logException(exception, "Exception during parsing"); diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageTool.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageTool.java index 82e7e535b..05043a274 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageTool.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageTool.java @@ -5,6 +5,7 @@ import org.apache.commons.lang3.StringUtils; +import edu.hm.hafner.metric.Node; import edu.hm.hafner.metric.parser.CoberturaParser; import edu.hm.hafner.metric.parser.JacocoParser; import edu.hm.hafner.metric.parser.PitestParser; @@ -26,13 +27,12 @@ import hudson.util.FormValidation; import hudson.util.ListBoxModel; -import io.jenkins.plugins.coverage.model.FileCoverageNode; import io.jenkins.plugins.coverage.model.ModelValidation; import io.jenkins.plugins.prism.SourceCodeRetention; import io.jenkins.plugins.util.JenkinsFacade; /** - * A coverage tool that can produce a {@link FileCoverageNode coverage tree} by parsing a given report file. + * A coverage tool that can produce a {@link Node coverage tree} by parsing a given report file. * * @author Ullrich Hafner */ diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/FilesScanner.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/FilesScanner.java index eea079031..8d1f7dd4d 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/FilesScanner.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/FilesScanner.java @@ -6,6 +6,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import org.apache.commons.lang3.StringUtils; @@ -92,7 +93,9 @@ else if (isEmpty(file)) { nodes.add(aggregateIssuesOfFile(file, log)); } } - return nodes.stream().reduce(Node::combineWith).orElse(new ModuleNode("Empty results for " + fileNames)); + return nodes.stream() + .reduce(Node::combineWith) + .orElse(new ModuleNode("Empty results for " + Arrays.toString(fileNames))); } private boolean isEmpty(final Path file) { @@ -110,7 +113,7 @@ private ModuleNode aggregateIssuesOfFile(final Path file, final FilteredLog log) XmlParser xmlParser = parser.createParser(); ModuleNode node = xmlParser.parse(Files.newBufferedReader(file)); log.logInfo("Successfully parsed file '%s'", file); - node.getMetricsDistribution().forEach((m, v) -> log.logInfo("%s: %s", m, v)); + node.getMetricsDistribution().values().forEach(v -> log.logInfo("%s", v)); return node; } catch (IOException exception) { diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/ChangeCoverageTableModel.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/ChangeCoverageTableModel.java index 27e00b267..8d10ce0b4 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/ChangeCoverageTableModel.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/ChangeCoverageTableModel.java @@ -2,10 +2,13 @@ import java.util.List; import java.util.Locale; -import java.util.Optional; +import java.util.SortedSet; import java.util.stream.Collectors; -import org.apache.commons.lang3.math.Fraction; +import edu.hm.hafner.metric.Coverage; +import edu.hm.hafner.metric.FileNode; +import edu.hm.hafner.metric.Metric; +import edu.hm.hafner.metric.Node; import hudson.Functions; @@ -20,7 +23,7 @@ * @since 3.0.0 */ class ChangeCoverageTableModel extends CoverageTableModel { - private final CoverageNode changeRoot; + private final Node changeRoot; /** * Creates a change coverage table model. @@ -36,7 +39,7 @@ class ChangeCoverageTableModel extends CoverageTableModel { * @param colorProvider * The {@link ColorProvider} which provides the used colors */ - ChangeCoverageTableModel(final String id, final CoverageNode root, final CoverageNode changeRoot, + ChangeCoverageTableModel(final String id, final Node root, final Node changeRoot, final RowRenderer renderer, final ColorProvider colorProvider) { super(id, root, renderer, colorProvider); @@ -51,18 +54,18 @@ public TableConfiguration getTableConfiguration() { @Override public List getRows() { Locale browserLocale = Functions.getCurrentLocale(); - return changeRoot.getAllFileCoverageNodes().stream() + return changeRoot.getAllFileNodes().stream() .map(file -> new ChangeCoverageRow( getOriginalNode(file), file, browserLocale, getRenderer(), getColorProvider())) .collect(Collectors.toList()); } - private FileCoverageNode getOriginalNode(final FileCoverageNode fileNode) { - Optional reference = getRoot().getAllFileCoverageNodes().stream() + private FileNode getOriginalNode(final FileNode fileNode) { + return getRoot().getAllFileNodes().stream() .filter(node -> node.getPath().equals(fileNode.getPath()) && node.getName().equals(fileNode.getName())) - .findFirst(); - return reference.orElse(fileNode); // return this as fallback to prevent exceptions + .findFirst() + .orElse(fileNode); // return this as fallback to prevent exceptions } /** @@ -71,51 +74,36 @@ private FileCoverageNode getOriginalNode(final FileCoverageNode fileNode) { * @since 3.0.0 */ private static class ChangeCoverageRow extends CoverageRow { - private final FileCoverageNode changedFileNode; + private final FileNode originalFile; - ChangeCoverageRow(final FileCoverageNode root, final FileCoverageNode changedFileNode, + ChangeCoverageRow(final FileNode originalFile, final FileNode changedFileNode, final Locale browserLocale, final RowRenderer renderer, final ColorProvider colorProvider) { - super(root, browserLocale, renderer, colorProvider); + super(changedFileNode, browserLocale, renderer, colorProvider); - this.changedFileNode = changedFileNode; - } - - @Override - public DetailedCell getLineCoverage() { - Coverage coverage = changedFileNode.getCoverage(CoverageMetric.LINE); - return createColoredCoverageColumn(coverage, "The line change coverage"); - } - - @Override - public DetailedCell getBranchCoverage() { - Coverage coverage = changedFileNode.getCoverage(CoverageMetric.BRANCH); - return createColoredCoverageColumn(coverage, "The branch change coverage"); + this.originalFile = originalFile; } @Override public DetailedCell getLineCoverageDelta() { - return createColoredChangeCoverageDeltaColumn(CoverageMetric.LINE); + return createColoredChangeCoverageDeltaColumn(Metric.LINE); } @Override public DetailedCell getBranchCoverageDelta() { - return createColoredChangeCoverageDeltaColumn(CoverageMetric.BRANCH); + return createColoredChangeCoverageDeltaColumn(Metric.BRANCH); } @Override public int getLoc() { - return (int) changedFileNode.getChangedCodeLines().stream() - .filter(line -> changedFileNode.getCoveragePerLine().containsKey(line)) - .count(); + SortedSet coveredDelta = getFile().getCoveredLinesOfChangeSet(); + return coveredDelta.size(); } - private DetailedCell createColoredChangeCoverageDeltaColumn(final CoverageMetric coverageMetric) { - Coverage changeCoverage = changedFileNode.getCoverage(coverageMetric); + private DetailedCell createColoredChangeCoverageDeltaColumn(final Metric metric) { + Coverage changeCoverage = getFile().getTypedValue(metric, Coverage.nullObject(metric)); if (changeCoverage.isSet()) { - Fraction delta = changeCoverage.getCoveredFraction() - .subtract(getRoot().getCoverage(coverageMetric).getCoveredFraction()); - return createColoredCoverageDeltaColumn(CoveragePercentage.valueOf(delta), - "The change coverage within the file against the total file coverage"); + return createColoredCoverageDeltaColumn( + changeCoverage.delta(originalFile.getTypedValue(metric, Coverage.nullObject(metric)))); } return NO_COVERAGE; } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CodeDeltaCalculator.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CodeDeltaCalculator.java index 9a85b1f19..a62918d97 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CodeDeltaCalculator.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CodeDeltaCalculator.java @@ -12,6 +12,7 @@ import java.util.function.Function; import java.util.stream.Collectors; +import edu.hm.hafner.metric.Node; import edu.hm.hafner.util.FilteredLog; import one.util.streamex.StreamEx; @@ -32,7 +33,6 @@ * @author Florian Orendi */ public class CodeDeltaCalculator { - static final String AMBIGUOUS_PATHS_ERROR = "Failed to map SCM paths with coverage report paths due to ambiguous fully qualified names"; static final String AMBIGUOUS_OLD_PATHS_ERROR = @@ -108,24 +108,20 @@ public Set getCoverageRelevantChanges(final Delta delta) { * the coverage reporting tools - usually the fully qualified name of the file. * * @param changes - * The code changes + * the code changes * @param root - * The root of the coverage tree + * the root of the coverage tree * @param log - * The log + * logger * - * @return the create code changes mapping + * @return the created mapping of code changes * @throws CodeDeltaException * when creating the mapping failed due to ambiguous paths */ public Map mapScmChangesToReportPaths( - final Set changes, final CoverageNode root, final FilteredLog log) throws CodeDeltaException { - Set reportPaths = root.getAllFileCoverageNodes().stream() - .map(FileCoverageNode::getPath) - .collect(Collectors.toSet()); - Set scmPaths = changes.stream() - .map(FileChanges::getFileName) - .collect(Collectors.toSet()); + final Set changes, final Node root, final FilteredLog log) throws CodeDeltaException { + Set reportPaths = new HashSet<>(root.getFiles()); + Set scmPaths = changes.stream().map(FileChanges::getFileName).collect(Collectors.toSet()); Map pathMapping = getScmToReportPathMapping(scmPaths, reportPaths); verifyScmToReportPathMapping(pathMapping, log); @@ -157,19 +153,17 @@ public Map mapScmChangesToReportPaths( * @throws CodeDeltaException * if the SCM path mapping is ambiguous */ - public Map createOldPathMapping(final CoverageNode root, final CoverageNode referenceRoot, + public Map createOldPathMapping(final Node root, final Node referenceRoot, final Map changes, final FilteredLog log) throws CodeDeltaException { - Set oldReportPaths = referenceRoot.getAllFileCoverageNodes().stream() - .map(FileCoverageNode::getPath) - .collect(Collectors.toSet()); + Set oldReportPaths = new HashSet<>(referenceRoot.getFiles()); // mapping between reference and current file paths which initially contains the SCM paths with renamings Map oldPathMapping = changes.entrySet().stream() .filter(entry -> FileEditType.RENAME.equals(entry.getValue().getFileEditType())) .collect(Collectors.toMap(Entry::getKey, entry -> entry.getValue().getOldFileName())); // the SCM paths and the coverage report paths from the reference - Map oldScmToOldReportPathMapping = - getScmToReportPathMapping(oldPathMapping.values(), oldReportPaths); + Map oldScmToOldReportPathMapping + = getScmToReportPathMapping(oldPathMapping.values(), oldReportPaths); // replacing the old SCM paths with the old report paths Set newReportPathsWithRename = oldPathMapping.keySet(); @@ -182,10 +176,9 @@ public Map createOldPathMapping(final CoverageNode root, final C } // adding the paths, which exist in both trees and contain no changes, to the mapping - root.getAllFileCoverageNodes().stream() - .filter(node -> !oldPathMapping.containsKey(node.getPath()) && oldReportPaths.contains( - node.getPath())) - .forEach(node -> oldPathMapping.put(node.getPath(), node.getPath())); + root.getFiles().stream() + .filter(file -> !oldPathMapping.containsKey(file) && oldReportPaths.contains(file)) + .forEach(file -> oldPathMapping.put(file, file)); removeMissingReferences(oldPathMapping, log); verifyOldPathMapping(oldPathMapping, log); diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/Coverage.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/Coverage.java deleted file mode 100644 index 38bc8aff1..000000000 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/Coverage.java +++ /dev/null @@ -1,376 +0,0 @@ -package io.jenkins.plugins.coverage.model; - -import java.io.Serializable; -import java.util.Locale; - -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.math.Fraction; - -import edu.hm.hafner.util.VisibleForTesting; - -/** - * Value of a code coverage item. The code coverage is measured using the number of covered and missed items. The type - * of items (line, instruction, branch, file, etc.) is provided by the companion class {@link CoverageMetric}. - * - * @author Ullrich Hafner - */ -public final class Coverage implements Serializable { - private static final long serialVersionUID = -3802318446471137305L; - - /** Null object that indicates that the code coverage has not been measured. */ - public static final Coverage NO_COVERAGE = new Coverage(0, 0); - - /** - * Creates a new {@link Coverage} instance from the provided string representation. The string representation is - * expected to contain the number of covered items and the total number of items - separated by a slash, e.g. - * "100/345", or "0/0". Whitespace characters will be ignored. - * - * @param stringRepresentation - * string representation to convert from - * - * @return the created coverage - * @throws IllegalArgumentException - * if the string is not a valid Coverage instance - */ - public static Coverage valueOf(final String stringRepresentation) { - try { - String cleanedFormat = StringUtils.deleteWhitespace(stringRepresentation); - if (StringUtils.contains(cleanedFormat, "/")) { - String extractedCovered = StringUtils.substringBefore(cleanedFormat, "/"); - String extractedTotal = StringUtils.substringAfter(cleanedFormat, "/"); - - int covered = Integer.parseInt(extractedCovered); - int total = Integer.parseInt(extractedTotal); - if (total >= covered) { - return new CoverageBuilder().setCovered(covered).setMissed(total - covered).build(); - } - } - } - catch (NumberFormatException exception) { - // ignore and throw a specific exception - } - throw new IllegalArgumentException( - String.format("Cannot convert %s to a valid Coverage instance.", stringRepresentation)); - } - - private final int covered; - private final int missed; - - /** - * Creates a new code coverage with the specified values. - * - * @param covered - * the number of covered items - * @param missed - * the number of missed items - */ - private Coverage(final int covered, final int missed) { - this.covered = covered; - this.missed = missed; - } - - /** - * Returns the number of covered items. - * - * @return the number of covered items - */ - public int getCovered() { - return covered; - } - - /** - * Returns the covered percentage as a {@link Fraction} in the range of {@code [0, 1]}. - * - * @return the covered percentage - */ - public Fraction getCoveredFraction() { - if (getTotal() == 0) { - return Fraction.ZERO; - } - return Fraction.getFraction(covered, getTotal()); - } - - /** - * Returns the covered percentage as a {@link CoveragePercentage} in the range of {@code [0, 100]}. - * - * @return the covered percentage - */ - public CoveragePercentage getCoveredPercentage() { - if (getTotal() == 0) { - return CoveragePercentage.valueOf(0); - } - return CoveragePercentage.valueOf(Fraction.getFraction(covered, getTotal())); - } - - /** - * Returns the covered percentage as rounded integer value in the range of {@code [0, 100]}. - * - * @return the covered percentage - */ - // TODO: we should make the charts accept float values - public int getRoundedPercentage() { - if (getTotal() == 0) { - return 0; - } - return (int) Math.round(getCoveredPercentage().getDoubleValue()); - } - - /** - * Formats the covered percentage as String (with a precision of two digits after the comma). Uses - * {@code Locale.getDefault()} to format the percentage. - * - * @return the covered percentage - * @see #formatCoveredPercentage(Locale) - */ - public String formatCoveredPercentage() { - return formatCoveredPercentage(Locale.getDefault()); - } - - /** - * Formats the covered percentage as String (with a precision of two digits after the comma). - * - * @param locale - * the locale to use when formatting the percentage - * - * @return the covered percentage - */ - public String formatCoveredPercentage(final Locale locale) { - return printPercentage(locale, getCoveredPercentage()); - } - - /** - * Returns the number of missed items. - * - * @return the number of missed items - */ - public int getMissed() { - return missed; - } - - /** - * Returns the missed percentage as a {@link Fraction} in the range of {@code [0, 1]}. - * - * @return the missed percentage - */ - public Fraction getMissedFraction() { - if (getTotal() == 0) { - return Fraction.ZERO; - } - return Fraction.ONE.subtract(getCoveredFraction()); - } - - /** - * Returns the missed percentage as a {@link CoveragePercentage} in the range of {@code [0, 100]}. - * - * @return the missed percentage - */ - public CoveragePercentage getMissedPercentage() { - if (getTotal() == 0) { - return CoveragePercentage.valueOf(0); - } - return CoveragePercentage.valueOf(getMissedFraction()); - } - - /** - * Formats the missed percentage as formatted String (with a precision of two digits after the comma). Uses - * {@code Locale.getDefault()} to format the percentage. - * - * @return the missed percentage - */ - public String formatMissedPercentage() { - return formatMissedPercentage(Locale.getDefault()); - } - - /** - * Formats the missed percentage as formatted String (with a precision of two digits after the comma). - * - * @param locale - * the locale to use when formatting the percentage - * - * @return the missed percentage - */ - public String formatMissedPercentage(final Locale locale) { - return printPercentage(locale, getMissedPercentage()); - } - - private String printPercentage(final Locale locale, final CoveragePercentage coverage) { - if (isSet()) { - return coverage.formatPercentage(locale); - } - return Messages.Coverage_Not_Available(); - } - - /** - * Add the coverage details from the specified instance to the coverage details of this instance. - * - * @param additional - * the additional coverage details - * - * @return the sum of this and the additional coverage - */ - public Coverage add(final Coverage additional) { - return new CoverageBuilder().setCovered(covered + additional.getCovered()) - .setMissed(missed + additional.getMissed()) - .build(); - } - - @Override - public String toString() { - int total = getTotal(); - if (total > 0) { - return String.format("%s (%s)", formatCoveredPercentage(), getCoveredFraction()); - } - return Messages.Coverage_Not_Available(); - } - - public int getTotal() { - return missed + covered; - } - - public boolean isSet() { - return getTotal() > 0; - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - Coverage coverage = (Coverage) o; - - if (covered != coverage.covered) { - return false; - } - return missed == coverage.missed; - } - - @Override - public int hashCode() { - int result = covered; - result = 31 * result + missed; - return result; - } - - /** - * Returns a string representation for this {@link Coverage} that can be used to serialize this instance in a simple - * but still readable way. The serialization contains the number of covered items and the total number of items - - * separated by a slash, e.g. "100/345", or "0/0". - * - * @return a string representation for this {@link Coverage} - */ - public String serializeToString() { - return String.format("%d/%d", getCovered(), getTotal()); - } - - /** - * Builder to create an cache new {@link Coverage} instances. - */ - public static class CoverageBuilder { - @VisibleForTesting - static final int CACHE_SIZE = 16; - private static final Coverage[] CACHE = new Coverage[CACHE_SIZE * CACHE_SIZE]; - - static { - for (int covered = 0; covered < CACHE_SIZE; covered++) { - for (int missed = 0; missed < CACHE_SIZE; missed++) { - CACHE[getCacheIndex(covered, missed)] = new Coverage(covered, missed); - } - } - } - - private static int getCacheIndex(final int covered, final int missed) { - return covered * CACHE_SIZE + missed; - } - - /** Null object that indicates that the code coverage has not been measured. */ - public static final Coverage NO_COVERAGE = CACHE[0]; - - private int covered; - private boolean isCoveredSet; - private int missed; - private boolean isMissedSet; - private int total; - private boolean isTotalSet; - - /** - * Sets the number of total items. - * - * @param total - * the number of total items - * - * @return this - */ - public CoverageBuilder setTotal(final int total) { - this.total = total; - isTotalSet = true; - return this; - } - - /** - * Sets the number of covered items. - * - * @param covered - * the number of covered items - * - * @return this - */ - public CoverageBuilder setCovered(final int covered) { - this.covered = covered; - isCoveredSet = true; - return this; - } - - /** - * Sets the number of missed items. - * - * @param missed - * the number of missed items - * - * @return this - */ - public CoverageBuilder setMissed(final int missed) { - this.missed = missed; - isMissedSet = true; - return this; - } - - /** - * Creates the new {@link Coverage} instance. - * - * @return the new instance - */ - @SuppressWarnings("PMD.CyclomaticComplexity") - public Coverage build() { - if (isCoveredSet && isMissedSet && isTotalSet) { - throw new IllegalArgumentException( - "Setting all three values covered, missed, and total is not allowed, just select two of them."); - } - if (isTotalSet) { - if (isCoveredSet) { - return createOrGetCoverage(covered, total - covered); - } - else if (isMissedSet) { - return createOrGetCoverage(total - missed, missed); - } - } - else { - if (isCoveredSet && isMissedSet) { - return createOrGetCoverage(covered, missed); - } - } - throw new IllegalArgumentException("You must set exactly two properties."); - } - - @SuppressWarnings({"checkstyle:HiddenField", "ParameterHidesMemberVariable"}) - private Coverage createOrGetCoverage(final int covered, final int missed) { - if (covered < CACHE_SIZE && missed < CACHE_SIZE) { - return CACHE[getCacheIndex(covered, missed)]; - } - return new Coverage(covered, missed); - } - } -} diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageBuildAction.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageBuildAction.java index 9250052d7..7ace659bc 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageBuildAction.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageBuildAction.java @@ -1,18 +1,24 @@ package io.jenkins.plugins.coverage.model; -import java.util.Locale; +import java.util.Collection; import java.util.Map; -import java.util.Map.Entry; +import java.util.NavigableMap; import java.util.Optional; -import java.util.SortedMap; +import java.util.Set; import java.util.TreeMap; +import java.util.stream.Collectors; +import org.apache.commons.lang3.math.Fraction; + +import edu.hm.hafner.metric.Coverage; +import edu.hm.hafner.metric.FileNode; +import edu.hm.hafner.metric.Metric; +import edu.hm.hafner.metric.Node; +import edu.hm.hafner.metric.Value; import edu.hm.hafner.util.VisibleForTesting; import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; -import one.util.streamex.StreamEx; - import org.kohsuke.stapler.StaplerProxy; import hudson.Functions; import hudson.model.HealthReport; @@ -32,7 +38,7 @@ * @author Ullrich Hafner */ @SuppressWarnings("PMD.GodClass") -public class CoverageBuildAction extends BuildAction implements HealthReportingAction, StaplerProxy { +public class CoverageBuildAction extends BuildAction implements HealthReportingAction, StaplerProxy { /** Relative URL to the details of the code coverage results. */ public static final String DETAILS_URL = "coverage"; /** The coverage report icon. */ @@ -42,26 +48,26 @@ public class CoverageBuildAction extends BuildAction implements He private static final String NO_REFERENCE_BUILD = "-"; - private final HealthReport healthReport; + private static final CoverageFormatter FORMATTER = new CoverageFormatter(); - private final Coverage lineCoverage; - private final Coverage branchCoverage; + private final HealthReport healthReport; private final String referenceBuildId; - // since 3.0.0 + /** The coverages of the result. */ + private final NavigableMap coverage; + /** The delta of this build's coverages with respect to the reference build. */ - private SortedMap difference; + private final NavigableMap difference; + /** The coverages filtered by changed lines of the associated change request. */ - // FIXME: actually we need the coverages? - private SortedMap changeCoverage; + private final NavigableMap changeCoverage; + /** The delta of the coverages of the associated change request with respect to the reference build. */ - private SortedMap changeCoverageDifference; - /** The indirect coverage changes of the associated change request with respect to the reference build. */ - private SortedMap indirectCoverageChanges; + private final NavigableMap changeCoverageDifference; - @SuppressWarnings("unused") - private final transient SortedMap delta = new TreeMap<>(); // not used anymore + /** The indirect coverage changes of the associated change request with respect to the reference build. */ + private final NavigableMap indirectCoverageChanges; /** * Creates a new instance of {@link CoverageBuildAction}. @@ -73,7 +79,7 @@ public class CoverageBuildAction extends BuildAction implements He * @param healthReport * health report */ - public CoverageBuildAction(final Run owner, final CoverageNode result, final HealthReport healthReport) { + public CoverageBuildAction(final Run owner, final Node result, final HealthReport healthReport) { this(owner, result, healthReport, NO_REFERENCE_BUILD, new TreeMap<>(), new TreeMap<>(), new TreeMap<>(), new TreeMap<>()); } @@ -99,30 +105,28 @@ public CoverageBuildAction(final Run owner, final CoverageNode result, fin * the indirect coverage changes of the associated change request with respect to the reference build */ @SuppressWarnings("checkstyle:ParameterNumber") - public CoverageBuildAction(final Run owner, final CoverageNode result, + public CoverageBuildAction(final Run owner, final Node result, final HealthReport healthReport, final String referenceBuildId, - final SortedMap delta, - final SortedMap changeCoverage, - final SortedMap changeCoverageDifference, - final SortedMap indirectCoverageChanges) { + final NavigableMap delta, + final NavigableMap changeCoverage, + final NavigableMap changeCoverageDifference, + final NavigableMap indirectCoverageChanges) { this(owner, result, healthReport, referenceBuildId, delta, changeCoverage, changeCoverageDifference, indirectCoverageChanges, true); } @VisibleForTesting @SuppressWarnings("checkstyle:ParameterNumber") - CoverageBuildAction(final Run owner, final CoverageNode result, + CoverageBuildAction(final Run owner, final Node result, final HealthReport healthReport, final String referenceBuildId, - final SortedMap delta, - final SortedMap changeCoverage, - final SortedMap changeCoverageDifference, - final SortedMap indirectCoverageChanges, + final NavigableMap delta, + final NavigableMap changeCoverage, + final NavigableMap changeCoverageDifference, + final NavigableMap indirectCoverageChanges, final boolean canSerialize) { super(owner, result, canSerialize); - lineCoverage = result.getCoverage(CoverageMetric.LINE); - branchCoverage = result.getCoverage(CoverageMetric.BRANCH); - + coverage = result.getMetricsDistribution(); difference = delta; this.changeCoverage = changeCoverage; this.changeCoverageDifference = changeCoverageDifference; @@ -131,67 +135,36 @@ public CoverageBuildAction(final Run owner, final CoverageNode result, this.healthReport = healthReport; } - @Override - protected Object readResolve() { - if (difference == null) { - difference = StreamEx.of(delta.entrySet()) - .toSortedMap(Entry::getKey, e -> CoveragePercentage.valueOf(e.getValue())); - } - if (changeCoverage == null) { - changeCoverage = new TreeMap<>(); - } - if (changeCoverageDifference == null) { - changeCoverageDifference = new TreeMap<>(); - } - if (indirectCoverageChanges == null) { - indirectCoverageChanges = new TreeMap<>(); - } - return super.readResolve(); - } - public Coverage getLineCoverage() { - return lineCoverage; + return ((Coverage)getCoverage(Metric.LINE)); } public Coverage getBranchCoverage() { - return branchCoverage; + return ((Coverage)getCoverage(Metric.BRANCH)); } /** * Returns whether a {@link Coverage} for the specified metric exists. * - * @param coverageMetric + * @param metric * the coverage metric * * @return {@code true} if a coverage is available for the specified metric, {@code false} otherwise */ - public boolean hasCoverage(final CoverageMetric coverageMetric) { - if (coverageMetric.equals(CoverageMetric.LINE)) { - return lineCoverage.isSet(); - } - if (coverageMetric.equals(CoverageMetric.BRANCH)) { - return branchCoverage.isSet(); - } - - return getResult().getCoverage(coverageMetric).isSet(); + public boolean hasCoverage(final Metric metric) { + return coverage.containsKey(metric); } /** * Returns the {@link Coverage} for the specified metric. * - * @param coverageMetric + * @param metric * the coverage metric * * @return the coverage */ - public Coverage getCoverage(final CoverageMetric coverageMetric) { - if (coverageMetric.equals(CoverageMetric.LINE)) { - return lineCoverage; - } - if (coverageMetric.equals(CoverageMetric.BRANCH)) { - return branchCoverage; - } - return getResult().getCoverage(coverageMetric); + public Value getCoverage(final Metric metric) { + return coverage.get(metric); } /** @@ -200,32 +173,31 @@ public Coverage getCoverage(final CoverageMetric coverageMetric) { * @return {@code true} if the change coverage exist, else {@code false} */ public boolean hasChangeCoverage() { - return hasChangeCoverage(CoverageMetric.LINE) || hasChangeCoverage(CoverageMetric.BRANCH); + return hasChangeCoverage(Metric.LINE) || hasChangeCoverage(Metric.BRANCH); } /** - * Returns whether a change coverage exists for the passed {@link CoverageMetric}. + * Returns whether a change coverage exists for the passed {@link Metric}. * - * @param coverageMetric + * @param metric * The coverage metric * * @return {@code true} if the change coverage exist for the metric, else {@code false} */ - public boolean hasChangeCoverage(final CoverageMetric coverageMetric) { - return changeCoverage.containsKey(coverageMetric); + public boolean hasChangeCoverage(final Metric metric) { + return changeCoverage.containsKey(metric); } /** * Gets the {@link Coverage change coverage} for the passed metric. * - * @param coverageMetric + * @param metric * The coverage metric * * @return the change coverage */ - public Coverage getChangeCoverage(final CoverageMetric coverageMetric) { - // FIXME: is percentage sufficient? - return getResult().getChangeCoverageTree().getCoverage(coverageMetric); + public Value getChangeCoverage(final Metric metric) { + return changeCoverage.get(metric); } /** @@ -234,32 +206,31 @@ public Coverage getChangeCoverage(final CoverageMetric coverageMetric) { * @return {@code true} if indirect coverage changes exist, else {@code false} */ public boolean hasIndirectCoverageChanges() { - return hasIndirectCoverageChanges(CoverageMetric.LINE) || hasIndirectCoverageChanges(CoverageMetric.BRANCH); + return hasIndirectCoverageChanges(Metric.LINE) || hasIndirectCoverageChanges(Metric.BRANCH); } /** - * Returns whether indirect coverage changes exist for the passed {@link CoverageMetric}. + * Returns whether indirect coverage changes exist for the passed {@link Metric}. * - * @param coverageMetric + * @param metric * The coverage metric * * @return {@code true} if indirect coverage changes exist for the metric, else {@code false} */ - public boolean hasIndirectCoverageChanges(final CoverageMetric coverageMetric) { - return indirectCoverageChanges.containsKey(coverageMetric); + public boolean hasIndirectCoverageChanges(final Metric metric) { + return indirectCoverageChanges.containsKey(metric); } /** * Gets the {@link Coverage indirect coverage changes} for the passed metric. * - * @param coverageMetric + * @param metric * The coverage metric * * @return the indirect coverage changes */ - public Coverage getIndirectCoverageChanges(final CoverageMetric coverageMetric) { - // FIXME: is percentage sufficient? - return getResult().getIndirectCoverageChangesTree().getCoverage(coverageMetric); + public Value getIndirectCoverageChanges(final Metric metric) { + return indirectCoverageChanges.get(metric); } /** @@ -280,6 +251,7 @@ public Coverage getIndirectCoverageChanges(final CoverageMetric coverageMetric) * @return the reference build * @see #getReferenceBuild() */ + @SuppressWarnings("unused") // Called by jelly view public String getReferenceBuildLink() { return ReferenceBuild.getReferenceBuildLink(referenceBuildId); } @@ -291,7 +263,7 @@ public String getReferenceBuildLink() { * @return the delta for each available coverage metric */ @SuppressWarnings("unused") // Called by jelly view - public SortedMap getDifference() { + public NavigableMap getDelta() { return difference; } @@ -303,7 +275,7 @@ public SortedMap getDifference() { * * @return {@code true} if a delta is available for the specified metric */ - public boolean hasDelta(final CoverageMetric metric) { + public boolean hasDelta(final Metric metric) { return difference.containsKey(metric); } @@ -317,10 +289,9 @@ public boolean hasDelta(final CoverageMetric metric) { * @return the delta metric */ @SuppressWarnings("unused") // Called by jelly view - public String formatDelta(final CoverageMetric metric) { - Locale clientLocale = Functions.getCurrentLocale(); + public String formatDelta(final Metric metric) { if (hasDelta(metric)) { - return difference.get(metric).formatDeltaPercentage(clientLocale); + return FORMATTER.formatDelta(difference.get(metric), Functions.getCurrentLocale()); } return Messages.Coverage_Not_Available(); } @@ -335,7 +306,7 @@ public String formatDelta(final CoverageMetric metric) { * @return the change coverage metric */ @SuppressWarnings("unused") // Called by jelly view - public String formatChangeCoverage(final CoverageMetric metric) { + public String formatChangeCoverage(final Metric metric) { return formatCoverageForMetric(metric, changeCoverage); } @@ -349,7 +320,7 @@ public String formatChangeCoverage(final CoverageMetric metric) { * @return the formatted representation of the indirect coverage changes */ @SuppressWarnings("unused") // Called by jelly view - public String formatIndirectCoverageChanges(final CoverageMetric metric) { + public String formatIndirectCoverageChanges(final Metric metric) { return formatCoverageForMetric(metric, indirectCoverageChanges); } @@ -363,26 +334,25 @@ public String formatIndirectCoverageChanges(final CoverageMetric metric) { * * @return the formatted text representation of the coverage value corresponding to the passed metric */ - private String formatCoverageForMetric(final CoverageMetric metric, - final Map coverages) { + private String formatCoverageForMetric(final Metric metric, final Map coverages) { String coverage = Messages.Coverage_Not_Available(); if (coverages.containsKey(metric)) { - coverage = coverages.get(metric).formatPercentage(Functions.getCurrentLocale()); + coverage = FORMATTER.format(coverages.get(metric), Functions.getCurrentLocale()); } - return metric.getName() + ": " + coverage; + return metric + ": " + coverage; } /** * Returns the change coverage delta for the passed metric, i.e. the coverage results of the current build minus the * same results of the reference build. * - * @param coverageMetric + * @param Metric * The change coverage metric * * @return the delta for each available coverage metric */ - public CoveragePercentage getChangeCoverageDifference(final CoverageMetric coverageMetric) { - return changeCoverageDifference.get(coverageMetric); + public Fraction getChangeCoverageDifference(final Metric Metric) { + return changeCoverageDifference.get(Metric); } /** @@ -393,7 +363,7 @@ public CoveragePercentage getChangeCoverageDifference(final CoverageMetric cover * * @return {@code true} if a delta is available for the specified metric */ - public boolean hasChangeCoverageDifference(final CoverageMetric metric) { + public boolean hasChangeCoverageDifference(final Metric metric) { return changeCoverageDifference.containsKey(metric); } @@ -417,10 +387,9 @@ public boolean hasCodeChanges() { * @return the delta metric */ @SuppressWarnings("unused") // Called by jelly view - public String formatChangeCoverageDifference(final CoverageMetric metric) { - Locale clientLocale = Functions.getCurrentLocale(); + public String formatChangeCoverageDifference(final Metric metric) { if (hasChangeCoverage(metric)) { - return changeCoverageDifference.get(metric).formatDeltaPercentage(clientLocale); + return FORMATTER.formatDelta(changeCoverageDifference.get(metric), Functions.getCurrentLocale()); } return Messages.Coverage_Not_Available(); } @@ -434,8 +403,8 @@ public String formatChangeCoverageDifference(final CoverageMetric metric) { @SuppressWarnings("unused") // Called by jelly view public String formatChangeCoverageOverview() { if (hasChangeCoverage()) { - int fileAmount = getResult().getFileAmountWithChangedCoverage(); - long lineAmount = getResult().getLineAmountWithChangedCoverage(); + int fileAmount = getFileAmountWithChangedCoverage(); + long lineAmount = getLineAmountWithChangedCoverage(); return getFormattedChangesOverview(lineAmount, fileAmount); } return Messages.Coverage_Not_Available(); @@ -450,13 +419,49 @@ public String formatChangeCoverageOverview() { @SuppressWarnings("unused") // Called by jelly view public String formatIndirectCoverageChangesOverview() { if (hasIndirectCoverageChanges()) { - int fileAmount = getResult().getFileAmountWithIndirectCoverageChanges(); - long lineAmount = getResult().getLineAmountWithIndirectCoverageChanges(); + int fileAmount = getFileAmountWithIndirectCoverageChanges(); + long lineAmount = getLineAmountWithIndirectCoverageChanges(); return getFormattedChangesOverview(lineAmount, fileAmount); } return Messages.Coverage_Not_Available(); } + public int getFileAmountWithChangedCoverage() { + return extractFileNodesWithChangeCoverage().size(); + } + + public long getLineAmountWithChangedCoverage() { + return extractFileNodesWithChangeCoverage().stream() + .map(FileNode::getCoveredLinesOfChangeSet) + .mapToLong(Collection::size) + .sum(); + } + + private Set extractFileNodesWithChangeCoverage() { + CoverageTreeCreator treeCreator = new CoverageTreeCreator(); + return treeCreator.createChangeCoverageTree(getResult()).getAllFileNodes().stream() + .filter(FileNode::hasCoveredLinesInChangeSet) + .collect(Collectors.toSet()); + } + + public int getFileAmountWithIndirectCoverageChanges() { + return extractFileNodesWithIndirectCoverageChanges().size(); + } + + public long getLineAmountWithIndirectCoverageChanges() { + return extractFileNodesWithIndirectCoverageChanges().stream() + .map(node -> node.getIndirectCoverageChanges().values()) + .mapToLong(Collection::size) + .sum(); + } + + private Set extractFileNodesWithIndirectCoverageChanges() { + CoverageTreeCreator treeCreator = new CoverageTreeCreator(); + return treeCreator.createIndirectCoverageChangesTree(getResult()).getAllFileNodes().stream() + .filter(FileNode::hasIndirectCoverageChanges) + .collect(Collectors.toSet()); + } + /** * Gets a formatted String representation of an overview of how many lines in how many files changed. * @@ -491,12 +496,12 @@ private String getFormattedChangesOverview(final long lineAmount, final int file * @return the delta metric */ @SuppressWarnings("unused") // Called by jelly view - public String formatCoverage(final CoverageMetric metric) { - return metric.getName() + ": " + getCoverage(metric).formatCoveredPercentage(Functions.getCurrentLocale()); + public String formatCoverage(final Metric metric) { + return metric + ": " + FORMATTER.format(getCoverage(metric), Functions.getCurrentLocale()); } @Override - protected AbstractXmlStream createXmlStream() { + protected AbstractXmlStream createXmlStream() { return new CoverageXmlStream(); } @@ -537,5 +542,4 @@ public String getDisplayName() { public String getUrlName() { return DETAILS_URL; } - } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageFormatter.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageFormatter.java new file mode 100644 index 000000000..63626629b --- /dev/null +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageFormatter.java @@ -0,0 +1,73 @@ +package io.jenkins.plugins.coverage.model; + +import java.util.Locale; + +import org.apache.commons.lang3.math.Fraction; + +import edu.hm.hafner.metric.Coverage; +import edu.hm.hafner.metric.IntegerValue; +import edu.hm.hafner.metric.MutationValue; +import edu.hm.hafner.metric.Value; + +/** + * A formatter for coverages. + * + * @author Florian Orendi + */ +public final class CoverageFormatter { + private static final Fraction HUNDRED = Fraction.getFraction("100.0"); + private static final String NO_COVERAGE_AVAILABLE = "-"; + + /** + * Formats a percentage to plain text and rounds the value to two decimals. + * + * @param locale + * The used locale + * + * @return the formatted percentage as plain text + */ + public String formatPercentage(final Coverage coverage, final Locale locale) { + if (coverage.isSet()) { + return formatPercentage(coverage.getCoveredPercentage(), locale); + } + return NO_COVERAGE_AVAILABLE; + } + + /** + * Formats a percentage to plain text and rounds the value to two decimals. + * + * @param locale + * The used locale + * + * @return the formatted percentage as plain text + */ + private String formatPercentage(final Fraction fraction, final Locale locale) { + return String.format(locale, "%.2f%%", fraction.multiplyBy(HUNDRED).doubleValue()); + } + + /** + * Formats a delta percentage to its plain text representation with a leading sign and rounds the value to two + * decimals. + * + * @param locale + * The used locale + * + * @return the formatted delta percentage as plain text with a leading sign + */ + public String formatDelta(final Fraction fraction, final Locale locale) { + return String.format(locale, "%+.2f%%", fraction.multiplyBy(HUNDRED).doubleValue()); + } + + public String format(final Value value, final Locale locale) { + if (value instanceof Coverage) { + return formatPercentage((Coverage) value, locale); + } + if (value instanceof MutationValue) { + return formatPercentage(((MutationValue) value).getCoveredPercentage(), locale); + } + if (value instanceof IntegerValue) { + return String.valueOf(((IntegerValue) value).getValue()); + } + return value.toString(); + } +} diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageJobAction.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageJobAction.java index 20157307d..14eb0df1a 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageJobAction.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageJobAction.java @@ -3,6 +3,7 @@ import edu.hm.hafner.echarts.BuildResult; import edu.hm.hafner.echarts.ChartModelConfiguration; import edu.hm.hafner.echarts.LinesChartModel; +import edu.hm.hafner.metric.Node; import edu.umd.cs.findbugs.annotations.NonNull; import hudson.model.Job; @@ -13,7 +14,7 @@ /** * Project level action for the coverage results. A job action displays a link on the side panel of a job that refers to - * the last build that contains coverage results (i.e. a {@link CoverageBuildAction} with a {@link CoverageNode} + * the last build that contains coverage results (i.e. a {@link CoverageBuildAction} with a {@link Node} * instance). This action also is responsible to render the historical trend via its associated 'floatingBox.jelly' * view. * diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageLeaf.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageLeaf.java deleted file mode 100644 index 02aac73f7..000000000 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageLeaf.java +++ /dev/null @@ -1,72 +0,0 @@ -package io.jenkins.plugins.coverage.model; - -import java.io.Serializable; -import java.util.Objects; - -import io.jenkins.plugins.coverage.model.Coverage.CoverageBuilder; - -/** - * A leaf in the coverage hierarchy. A leaf is a non-divisible coverage metric like line or branch coverage. - * - * @author Ullrich Hafner - */ -public final class CoverageLeaf implements Serializable { - private static final long serialVersionUID = -1062406664372222691L; - - private final CoverageMetric metric; - private final Coverage coverage; - - /** - * Creates a new leaf with the given coverage for the specified metric. - * - * @param metric - * the coverage metric - * @param coverage - * the coverage of the element - */ - public CoverageLeaf(final CoverageMetric metric, final Coverage coverage) { - this.metric = metric; - this.coverage = coverage; - } - - public CoverageMetric getMetric() { - return metric; - } - - /** - * Returns the coverage for the specified metric. - * - * @param searchMetric - * the metric to get the coverage for - * - * @return coverage ratio - */ - public Coverage getCoverage(final CoverageMetric searchMetric) { - if (metric.equals(searchMetric)) { - return coverage; - } - return CoverageBuilder.NO_COVERAGE; - } - - @Override - public String toString() { - return String.format("[%s]: %s", metric, coverage); - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - CoverageLeaf that = (CoverageLeaf) o; - return Objects.equals(metric, that.metric) && Objects.equals(coverage, that.coverage); - } - - @Override - public int hashCode() { - return Objects.hash(metric, coverage); - } -} diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageMetric.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageMetric.java deleted file mode 100644 index a91ff919d..000000000 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageMetric.java +++ /dev/null @@ -1,185 +0,0 @@ -package io.jenkins.plugins.coverage.model; - -import java.io.Serializable; -import java.util.Arrays; -import java.util.List; -import java.util.Locale; - -import org.apache.commons.lang3.StringUtils; - -import edu.umd.cs.findbugs.annotations.CheckForNull; - -/** - * A coverage metric to identify the coverage result type. Note: this class has a natural ordering that is inconsistent - * with equals. - * - * @author Ullrich Hafner - */ -public class CoverageMetric implements Comparable, Serializable { - private static final long serialVersionUID = 3664773349525399092L; - private static final int DEFAULT_ORDER = 100; - - private enum MetricType { - LEAF, - COMPOSITE - } - - /** Module coverage. */ - public static final CoverageMetric MODULE = new CoverageMetric("Module", -10); - private static final CoverageMetric REPORT = new CoverageMetric("Report", -10); - /** Package or namespace coverage. */ - public static final CoverageMetric PACKAGE = new CoverageMetric("Package", 1); - /** File coverage. */ - public static final CoverageMetric FILE = new CoverageMetric("File", 2); - /** Class coverage. */ - public static final CoverageMetric CLASS = new CoverageMetric("Class", 3); - /** Method coverage. */ - public static final CoverageMetric METHOD = new CoverageMetric("Method", 4); - /** Instruction coverage. */ - public static final CoverageMetric INSTRUCTION = new CoverageMetric("Instruction", 11, MetricType.LEAF); - /** Line coverage. */ - public static final CoverageMetric LINE = new CoverageMetric("Line", 10, MetricType.LEAF); - /** Branch coverage. */ - public static final CoverageMetric BRANCH = new CoverageMetric("Branch", 12, MetricType.LEAF); - private static final CoverageMetric CONDITIONAL = new CoverageMetric("Conditional", 12, MetricType.LEAF); - - /** - * Creates a new {@link CoverageMetric} with the specified name. If the name is the same as the name of one of the - * predefined metrics, then the existing metric is returned. - * - * @param name - * the name of the metric - * - * @return the metric - */ - @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"}) - public static CoverageMetric valueOf(final String name) { - if (MODULE.equalsIgnoreCase(name) || REPORT.equalsIgnoreCase(name)) { - return MODULE; - } - if (PACKAGE.equalsIgnoreCase(name)) { - return PACKAGE; - } - if (FILE.equalsIgnoreCase(name)) { - return FILE; - } - if (CLASS.equalsIgnoreCase(name)) { - return CLASS; - } - if (METHOD.equalsIgnoreCase(name)) { - return METHOD; - } - if (INSTRUCTION.equalsIgnoreCase(name)) { - return INSTRUCTION; - } - if (LINE.equalsIgnoreCase(name)) { - return LINE; - } - if (BRANCH.equalsIgnoreCase(name) || CONDITIONAL.equalsIgnoreCase(name)) { - return BRANCH; - } - return new CoverageMetric(name, DEFAULT_ORDER); - } - - /** - * Provides all available values of {@link CoverageMetric}. - * - * @return the available metrics - */ - public static List getAvailableCoverageMetrics() { - return Arrays.asList(LINE, BRANCH, INSTRUCTION, METHOD, CLASS, FILE, PACKAGE, MODULE); - } - - /** - * Checks if this instance has a name that is equal to the specified name (ignoring case). - * - * @param searchName - * the coverage metric name to check - * - * @return {@code true} if this instance has the same name, {@code false} otherwise - */ - public boolean equalsIgnoreCase(final String searchName) { - return equalsIgnoreCase(getName(), searchName); - } - - /** - *

Compares two CharSequences, returning {@code true} if they represent - * equal sequences of characters, ignoring case.

- * - *

{@code null}s are handled without exceptions. Two {@code null} - * references are considered equal. The comparison is case insensitive.

- * - *
-     * StringUtils.equalsIgnoreCase(null, null)   = true
-     * StringUtils.equalsIgnoreCase(null, "abc")  = false
-     * StringUtils.equalsIgnoreCase("abc", null)  = false
-     * StringUtils.equalsIgnoreCase("abc", "abc") = true
-     * StringUtils.equalsIgnoreCase("abc", "ABC") = true
-     * 
- * - * @param a - * the first CharSequence, may be {@code null} - * @param b - * the second CharSequence, may be {@code null} - * - * @return {@code true} if the CharSequences are equal (case-insensitive), or both {@code null} - */ - public static boolean equalsIgnoreCase(@CheckForNull final String a, @CheckForNull final String b) { - return StringUtils.equals(normalize(a), normalize(b)); - } - - private static String normalize(@CheckForNull final String input) { - return StringUtils.defaultString(input).toUpperCase(Locale.ENGLISH); - } - - private final String name; - private final int order; - private final boolean leaf; - - private CoverageMetric(final String name, final int order) { - this(name, order, MetricType.COMPOSITE); - } - - private CoverageMetric(final String name, final int order, final MetricType type) { - this.name = name; - this.order = order; - this.leaf = type == MetricType.LEAF; - } - - public String getName() { - return name; - } - - public boolean isLeaf() { - return leaf; - } - - @Override - public String toString() { - return name; - } - - @Override - public int compareTo(final CoverageMetric other) { - return order - other.order; - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - CoverageMetric that = (CoverageMetric) o; - - return name.equals(that.name); - } - - @Override - public int hashCode() { - return name.hashCode(); - } -} diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageNode.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageNode.java deleted file mode 100644 index dde12a63e..000000000 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageNode.java +++ /dev/null @@ -1,780 +0,0 @@ -package io.jenkins.plugins.coverage.model; - -import java.io.ObjectStreamException; -import java.io.Serializable; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Deque; -import java.util.List; -import java.util.Locale; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.SortedMap; -import java.util.SortedSet; -import java.util.TreeMap; -import java.util.TreeSet; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.math.Fraction; - -import edu.hm.hafner.util.Ensure; -import edu.hm.hafner.util.VisibleForTesting; -import edu.umd.cs.findbugs.annotations.CheckForNull; - -import one.util.streamex.StreamEx; - -import io.jenkins.plugins.coverage.model.Coverage.CoverageBuilder; - -/** - * A hierarchical decomposition of coverage results. - * - * @author Ullrich Hafner - */ -@SuppressWarnings({"PMD.GodClass", "PMD.CyclomaticComplexity"}) -public class CoverageNode implements Serializable { - private static final long serialVersionUID = -6608885640271135273L; - - private static final Coverage COVERED_NODE = new Coverage.CoverageBuilder().setCovered(1).setMissed(0).build(); - private static final Coverage MISSED_NODE = new Coverage.CoverageBuilder().setCovered(0).setMissed(1).build(); - - /** Transient non static {@link CoverageTreeCreator} in order to be able to mock it for tests. */ - private transient CoverageTreeCreator coverageTreeCreator; - - static final String ROOT = "^"; - - private final CoverageMetric metric; - private final String name; - private final List children = new ArrayList<>(); - private final List leaves = new ArrayList<>(); - @CheckForNull - private CoverageNode parent; - - /** - * Creates a new coverage item node with the given name. - * - * @param metric - * the coverage metric this node belongs to - * @param name - * the human-readable name of the node - */ - public CoverageNode(final CoverageMetric metric, final String name) { - this(metric, name, new CoverageTreeCreator()); - } - - /** - * Creates a new coverage item node with the given name and a mocked {@link CoverageTreeCreator}. - * - * @param metric - * the coverage metric this node belongs to - * @param name - * the human-readable name of the node - * @param coverageTreeCreator - * the coverage tree creator - */ - @VisibleForTesting - public CoverageNode(final CoverageMetric metric, final String name, final CoverageTreeCreator coverageTreeCreator) { - this.metric = metric; - this.name = name; - this.coverageTreeCreator = coverageTreeCreator; - } - - /** - * Called after de-serialization to restore transient fields. - * - * @return this - * @throws ObjectStreamException - * if the operation failed - */ - protected Object readResolve() throws ObjectStreamException { - if (coverageTreeCreator == null) { - coverageTreeCreator = new CoverageTreeCreator(); - } - return this; - } - - /** - * Gets the parent node. - * - * @return the parent, if existent - * @throws IllegalStateException - * if no parent exists - */ - public CoverageNode getParent() { - if (parent == null) { - throw new IllegalStateException("Parent is not set"); - } - return parent; - } - - /** - * Returns the source code path of this node. - * - * @return the element type - */ - public String getPath() { - return StringUtils.EMPTY; - } - - protected String mergePath(final String localPath) { - // default packages are named '-' - if ("-".equals(localPath)) { - return StringUtils.EMPTY; - } - - if (hasParent()) { - String parentPath = getParent().getPath(); - - if (StringUtils.isBlank(parentPath)) { - return localPath; - } - if (StringUtils.isBlank(localPath)) { - return parentPath; - } - return parentPath + "/" + localPath; - } - - return localPath; - } - - /** - * Returns the type if the coverage metric for this node. - * - * @return the element type - */ - public CoverageMetric getMetric() { - return metric; - } - - /** - * Returns the available coverage metrics for the whole tree starting with this node. - * - * @return the elements in this tree - */ - public SortedSet getMetrics() { - SortedSet elements = children.stream() - .map(CoverageNode::getMetrics) - .flatMap(Collection::stream) - .collect(Collectors.toCollection(TreeSet::new)); - - elements.add(getMetric()); - leaves.stream().map(CoverageLeaf::getMetric).forEach(elements::add); - - return elements; - } - - /** - * Returns the most important coverage metrics. - * - * @return most important coverage metrics - */ - public Collection getImportantMetrics() { - List importantElements = new ArrayList<>(); - importantElements.add(CoverageMetric.LINE); - importantElements.add(CoverageMetric.BRANCH); - importantElements.retainAll(getMetrics()); - return importantElements; - } - - /** - * Returns a mapping of metric to coverage. The root of the tree will be skipped. - * - * @return a mapping of metric to coverage. - */ - public SortedMap getMetricsDistribution() { - return getMetrics().stream() - .collect(Collectors.toMap(Function.identity(), this::getCoverage, (o1, o2) -> o1, TreeMap::new)); - } - - /** - * Gets the coverage for each available metric as a fraction between 0 and 1. - * - * @return the coverage fractions mapped by their metric - */ - public SortedMap getMetricFractions() { - if (children.isEmpty() && leaves.isEmpty()) { - // prevents returning a module coverage of 0% if the tree has no leaves since the coverage is not existent - return new TreeMap<>(); - } - return getMetrics().stream() - .collect(Collectors.toMap(Function.identity(), - searchMetric -> getCoverage(searchMetric).getCoveredFraction(), (o1, o2) -> o1, - TreeMap::new)); - } - - /** - * Gets the coverage for each available metric as a percentage between 0 and 100. - * - * @return the coverage percentages mapped by their metric - */ - public SortedMap getMetricPercentages() { - return StreamEx.of(getMetricFractions().entrySet()) - .toSortedMap(Entry::getKey, e -> CoveragePercentage.valueOf(e.getValue())); - } - - public String getName() { - return name; - } - - public List getChildren() { - return children; - } - - public List getLeaves() { - return leaves; - } - - private void addAll(final List nodes) { - nodes.forEach(this::add); - } - - /** - * Appends the specified child element to the list of children. - * - * @param child - * the child to add - */ - public void add(final CoverageNode child) { - children.add(child); - child.setParent(this); - } - - /** - * Appends the specified leaf element to the list of leaves. - * - * @param leaf - * the leaf to add - */ - public void add(final CoverageLeaf leaf) { - leaves.add(leaf); - } - - /** - * Removes this node from the coverage tree. - */ - void remove() { - // TODO: remove this method when unique paths are handled correctly - if (hasParent()) { - getParent().children.remove(this); - clearEmptyPaths(getParent()); - } - } - - /** - * Clears an empty tree path from the bottom of the tree to the top, beginning with the passed node. - * - * @param node The {@link CoverageNode node} to begin from - */ - private void clearEmptyPaths(final CoverageNode node) { - // TODO: remove this method when unique paths are handled correctly - if (node != null && node.getChildren().isEmpty()) { - node.remove(); - } - } - - /** - * Returns whether this node is the root of the tree. - * - * @return {@code true} if this node is the root of the tree, {@code false} otherwise - */ - public boolean isRoot() { - return parent == null; - } - - /** - * Returns whether this node has a parent node. - * - * @return {@code true} if this node has a parent node, {@code false} if it is the root of the hierarchy - */ - public boolean hasParent() { - return !isRoot(); - } - - void setParent(final CoverageNode parent) { - this.parent = Objects.requireNonNull(parent); - } - - /** - * Returns the name of the parent element or "-" if there is no such element. - * - * @return the name of the parent element - */ - public String getParentName() { - if (parent == null) { - return ROOT; - } - CoverageMetric type = parent.getMetric(); - - List parentsOfSameType = new ArrayList<>(); - for (CoverageNode node = parent; node != null && node.getMetric().equals(type); node = node.parent) { - parentsOfSameType.add(0, node.getName()); - } - return String.join(".", parentsOfSameType); - } - - /** - * Prints the coverage for the specified element. Uses {@code Locale.getDefault()} to format the percentage. - * - * @param searchMetric - * the element to print the coverage for - * - * @return coverage ratio in a human-readable format - * @see #printCoverageFor(CoverageMetric, Locale) - */ - public String printCoverageFor(final CoverageMetric searchMetric) { - return printCoverageFor(searchMetric, Locale.getDefault()); - } - - /** - * Prints the coverage for the specified element. - * - * @param searchMetric - * the element to print the coverage for - * @param locale - * the locale to use when formatting the percentage - * - * @return coverage ratio in a human-readable format - */ - public String printCoverageFor(final CoverageMetric searchMetric, final Locale locale) { - return getCoverage(searchMetric).formatCoveredPercentage(locale); - } - - /** - * Returns the coverage for the specified metric. - * - * @param searchMetric - * the element to get the coverage for - * - * @return coverage ratio - */ - public Coverage getCoverage(final CoverageMetric searchMetric) { - if (searchMetric.isLeaf()) { - Coverage childrenCoverage = children.stream() - .map(node -> node.getCoverage(searchMetric)) - .reduce(CoverageBuilder.NO_COVERAGE, Coverage::add); - return leaves.stream() - .map(node -> node.getCoverage(searchMetric)) - .reduce(childrenCoverage, Coverage::add); - } - else { - Coverage childrenCoverage = children.stream() - .map(node -> node.getCoverage(searchMetric)) - .reduce(CoverageBuilder.NO_COVERAGE, Coverage::add); - - if (metric.equals(searchMetric)) { - if (getCoverage(CoverageMetric.LINE).getCovered() > 0) { - return childrenCoverage.add(COVERED_NODE); - } - else { - return childrenCoverage.add(MISSED_NODE); - } - } - return childrenCoverage; - } - } - - /** - * Computes the coverage delta between this node and the specified reference node as fractions between 0 and 1. - * - * @param reference - * the reference node - * - * @return the delta coverage for each available metric as fraction - */ - public SortedMap computeDelta(final CoverageNode reference) { - SortedMap deltaPercentages = new TreeMap<>(); - SortedMap metricPercentages = getMetricFractions(); - SortedMap referencePercentages = reference.getMetricFractions(); - metricPercentages.forEach((key, value) -> - deltaPercentages.put(key, - new SafeFraction(value).subtract(referencePercentages.getOrDefault(key, Fraction.ZERO)))); - return deltaPercentages; - } - - /** - * Computes the coverage delta between this node and the specified reference node as percentage between 0 and 100. - * - * @param reference - * the reference node - * - * @return the delta coverage for each available metric as percentage - */ - public SortedMap computeDeltaAsPercentage(final CoverageNode reference) { - return StreamEx.of(computeDelta(reference).entrySet()) - .toSortedMap(Entry::getKey, e -> CoveragePercentage.valueOf(e.getValue())); - } - - /** - * Returns recursively all nodes for the specified metric type. - * - * @param searchMetric - * the metric to look for - * - * @return all nodes for the given metric - */ - public List getAll(final CoverageMetric searchMetric) { - Ensure.that(searchMetric.isLeaf()) - .isFalse("Leaves like '%s' are not stored as inner nodes of the tree", searchMetric); - - List childNodes = children.stream() - .map(child -> child.getAll(searchMetric)) - .flatMap(List::stream).collect(Collectors.toList()); - if (metric.equals(searchMetric)) { - childNodes.add(this); - } - return childNodes; - } - - /** - * Returns recursively all nodes of the instance {@link FileCoverageNode}. - * - * @return all file coverage nodes - * @since 3.0.0 - */ - public List getAllFileCoverageNodes() { - List childNodes = children.stream() - .map(CoverageNode::getAllFileCoverageNodes) - .flatMap(List::stream) - .collect(Collectors.toList()); - if (this instanceof FileCoverageNode) { - childNodes.add((FileCoverageNode) this); - } - return childNodes; - } - - /** - * Finds the coverage metric with the given path starting from this node. - * - * @param searchMetric - * the coverage metric to search for - * @param searchPath - * the path of the node - * - * @return the result if found - */ - public Optional find(final CoverageMetric searchMetric, final String searchPath) { - if (matches(searchMetric, searchPath)) { - return Optional.of(this); - } - return children.stream() - .map(child -> child.find(searchMetric, searchPath)) - .flatMap(o -> o.map(Stream::of).orElseGet(Stream::empty)) - .findAny(); - } - - /** - * Finds the coverage metric with the given hash code starting from this node. - * - * @param searchMetric - * the coverage metric to search for - * @param searchNameHashCode - * the hash code of the node path - * - * @return the result if found - */ - public Optional findByHashCode(final CoverageMetric searchMetric, final int searchNameHashCode) { - if (matches(searchMetric, searchNameHashCode)) { - return Optional.of(this); - } - return children.stream() - .map(child -> child.findByHashCode(searchMetric, searchNameHashCode)) - .flatMap(o -> o.map(Stream::of).orElseGet(Stream::empty)) - .findAny(); - } - - /** - * Returns whether this node matches the specified coverage metric and name. - * - * @param searchMetric - * the coverage metric to search for - * @param searchPath - * the name of the node - * - * @return the result if found - */ - private boolean matches(final CoverageMetric searchMetric, final String searchPath) { - return metric.equals(searchMetric) && getPath().equals(searchPath); - } - - /** - * Returns whether this node matches the specified coverage metric and name. - * - * @param searchMetric - * the coverage metric to search for - * @param searchNameHashCode - * the hash code of the node name - * - * @return the result if found - */ - private boolean matches(final CoverageMetric searchMetric, final int searchNameHashCode) { - if (!metric.equals(searchMetric)) { - return false; - } - return getPath().hashCode() == searchNameHashCode; - } - - /** - * Splits flat packages into a package hierarchy. Changes the internal tree structure in place. - */ - public void splitPackages() { - if (CoverageMetric.MODULE.equals(metric)) { - List allPackages = children.stream() - .filter(child -> CoverageMetric.PACKAGE.equals(child.getMetric())) - .collect(Collectors.toList()); - if (!allPackages.isEmpty()) { - children.clear(); - for (CoverageNode packageNode : allPackages) { - String[] packageParts = packageNode.getName().split("\\."); - if (packageParts.length > 1) { - Deque packageLevels = new ArrayDeque<>(Arrays.asList(packageParts)); - insertPackage(packageNode, packageLevels); - } - else { - add(packageNode); - } - } - } - } - } - - /** - * Filters the package structure for only package nodes which contain file nodes. The filtered tree is required in - * order to calculate the package coverage. Note that packages without any files are fully removed. - * - * @return a filtered copy of this {@link CoverageNode} - */ - public CoverageNode filterPackageStructure() { - CoverageNode copy = copyTree(); - if (CoverageMetric.MODULE.equals(metric)) { - Set packagesWithFiles = copy.getAll(CoverageMetric.PACKAGE).stream() - .filter(node -> node.getChildren().stream() - .anyMatch(child -> child.getMetric().equals(CoverageMetric.FILE))) - .collect(Collectors.toSet()); - packagesWithFiles.forEach(node -> { - node.setParent(copy); - Set fileChildren = node.getChildren().stream() - .filter(child -> !child.getMetric().equals(CoverageMetric.PACKAGE)) - .collect(Collectors.toSet()); - node.children.clear(); - node.children.addAll(fileChildren); - }); - Set nonePackageChildren = copy.children.stream() - .filter(node -> !node.getMetric().equals(CoverageMetric.PACKAGE)) - .collect(Collectors.toSet()); - copy.children.clear(); - copy.children.addAll(nonePackageChildren); - copy.children.addAll(packagesWithFiles); - } - return copy; - } - - /** - * Checks whether the coverage tree contains a change coverage at all. The method checks if line or branch coverage - * are available since these are the basic metrics which are available if changes exist. - * - * @return {@code true} whether a change coverage exist, else {@code false} - */ - public boolean hasChangeCoverage() { - return hasChangeCoverage(CoverageMetric.LINE) || hasChangeCoverage(CoverageMetric.BRANCH); - } - - /** - * Checks whether the coverage tree contains a change coverage for the passed {@link CoverageMetric}. - * - * @param coverageMetric - * The coverage metric - * - * @return {@code true} whether a change coverage exist for the coverage metric, else {@code false} - */ - public boolean hasChangeCoverage(final CoverageMetric coverageMetric) { - return getChangeCoverageTree() - .getCoverage(coverageMetric) - .getTotal() > 0; - } - - /** - * Creates a filtered coverage tree which only contains nodes with code changes. The root of the tree is this. - * - * @return the filtered coverage tree - */ - public CoverageNode getChangeCoverageTree() { - return coverageTreeCreator.createChangeCoverageTree(this); - } - - public int getFileAmountWithChangedCoverage() { - return extractFileNodesWithChangeCoverage().size(); - } - - public long getLineAmountWithChangedCoverage() { - return extractFileNodesWithChangeCoverage().stream() - .map(node -> { // only mention lines with changes which affect coverage - SortedSet filtered = new TreeSet<>(node.getChangedCodeLines()); - return filtered.stream() - .filter(line -> node.getCoveragePerLine().containsKey(line)) - .collect(Collectors.toSet()); - }) - .mapToLong(Collection::size) - .sum(); - } - - private Set extractFileNodesWithChangeCoverage() { - return getChangeCoverageTree().getAllFileCoverageNodes().stream() - .filter(node -> node.getChangedCodeLines() - .stream() // only mention files with changes which affect coverage - .anyMatch(line -> node.getCoveragePerLine().containsKey(line))) - .collect(Collectors.toSet()); - } - - public int getFileAmountWithIndirectCoverageChanges() { - return extractFileNodesWithIndirectCoverageChanges().size(); - } - - public long getLineAmountWithIndirectCoverageChanges() { - return extractFileNodesWithIndirectCoverageChanges().stream() - .map(node -> node.getIndirectCoverageChanges().values()) - .mapToLong(Collection::size) - .sum(); - } - - private Set extractFileNodesWithIndirectCoverageChanges() { - return getIndirectCoverageChangesTree().getAllFileCoverageNodes().stream() - .filter(node -> !node.getIndirectCoverageChanges().isEmpty()) - .collect(Collectors.toSet()); - } - - /** - * Checks whether the coverage tree contains indirect coverage changes at all. The method checks if line or branch - * coverage are available since these are the basic metrics which are available if changes exist. - * - * @return {@code true} whether indirect coverage changes exist, else {@code false} - */ - public boolean hasIndirectCoverageChanges() { - return hasIndirectCoverageChanges(CoverageMetric.LINE) || hasIndirectCoverageChanges(CoverageMetric.BRANCH); - } - - /** - * Checks whether the coverage tree contains indirect coverage changes for the passed {@link CoverageMetric}. - * - * @param coverageMetric - * The coverage metric - * - * @return {@code true} whether indirect coverage changes exist for the coverage metric, else {@code false} - */ - public boolean hasIndirectCoverageChanges(final CoverageMetric coverageMetric) { - return getIndirectCoverageChangesTree() - .getCoverage(coverageMetric) - .getTotal() > 0; - } - - /** - * Creates a filtered coverage tree which only contains nodes with indirect coverage changes. The root of the tree - * is this. - * - * @return the filtered coverage tree - */ - public CoverageNode getIndirectCoverageChangesTree() { - return coverageTreeCreator.createIndirectCoverageChangesTree(this); - } - - /** - * Checks whether code any changes have been detected no matter if the code coverage is affected or not. - * - * @return {@code true} whether code changes have been detected - */ - public boolean hasCodeChanges() { - return getAllFileCoverageNodes().stream() - .anyMatch(fileNode -> !fileNode.getChangedCodeLines().isEmpty()); - } - - /** - * Creates a deep copy of the coverage tree with this as root node. - * - * @return the root node of the copied tree - */ - public CoverageNode copyTree() { - return copyTree(null); - } - - /** - * Recursively copies the coverage tree with the passed {@link CoverageNode} as root. - * - * @param copiedParent - * The root node - * - * @return the copied tree - */ - protected CoverageNode copyTree(@CheckForNull final CoverageNode copiedParent) { - CoverageNode copy = copyEmpty(); - if (copiedParent != null) { - copy.setParent(copiedParent); - } - - getChildren().stream() - .map(node -> node.copyTree(this)) - .forEach(copy::add); - getLeaves().forEach(copy::add); - - return copy; - } - - /** - * Creates a copied instance of this node that has no children, leaves, and parent yet. - * - * @return the new and empty node - */ - protected CoverageNode copyEmpty() { - return new CoverageNode(metric, name); - } - - private void insertPackage(final CoverageNode aPackage, final Deque packageLevels) { - String nextLevelName = packageLevels.pop(); - CoverageNode subPackage = createChild(nextLevelName); - if (packageLevels.isEmpty()) { - subPackage.addAll(aPackage.children); - } - else { - subPackage.insertPackage(aPackage, packageLevels); - } - } - - private CoverageNode createChild(final String childName) { - for (CoverageNode child : children) { - if (child.getName().equals(childName)) { - return child; - } - - } - CoverageNode newNode = new PackageCoverageNode(childName); - add(newNode); - return newNode; - } - - @Override - public String toString() { - return String.format("[%s] %s", metric, name); - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - CoverageNode that = (CoverageNode) o; - return Objects.equals(metric, that.metric) && Objects.equals(name, that.name) - && Objects.equals(children, that.children) && Objects.equals(leaves, that.leaves); - } - - @Override - public int hashCode() { - return Objects.hash(metric, name, children, leaves); - } -} diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageNodeConverter.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageNodeConverter.java deleted file mode 100644 index 2b92ed4ce..000000000 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageNodeConverter.java +++ /dev/null @@ -1,114 +0,0 @@ -package io.jenkins.plugins.coverage.model; - -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Optional; -import java.util.Set; -import java.util.SortedMap; -import java.util.TreeMap; - -import io.jenkins.plugins.coverage.targets.CoverageElement; -import io.jenkins.plugins.coverage.targets.CoveragePaint; -import io.jenkins.plugins.coverage.targets.CoverageResult; -import io.jenkins.plugins.coverage.targets.Ratio; - -/** - * Converts {@link CoverageResult} instances to corresponding {@link CoverageNode} instances. - * - * @author Ullrich Hafner - */ -public class CoverageNodeConverter { - - private final Map paintedFiles = new HashMap<>(); - - /** - * Converts a {@link CoverageResult} instance to the corresponding {@link CoverageNode} instance. - * - * @param result - * the result that should be converted - * - * @return the root node of the coverage tree - */ - public CoverageNode convert(final CoverageResult result) { - return createNode(result); - } - - private CoverageNode createNode(final CoverageResult result) { - CoverageElement element = result.getElement(); - CoverageMetric metric = CoverageMetric.valueOf(element.getName()); - if (result.getChildren().isEmpty()) { - CoverageNode coverageNode = createNode(metric, result); - for (Map.Entry coverage : result.getLocalResults().entrySet()) { - Ratio ratio = coverage.getValue(); - CoverageLeaf leaf = new CoverageLeaf(CoverageMetric.valueOf(coverage.getKey().getName()), - new Coverage.CoverageBuilder().setCovered((int) ratio.numerator) - .setMissed((int) (ratio.denominator - ratio.numerator)) - .build()); - coverageNode.add(leaf); - } - return coverageNode; - } - else { - CoverageNode coverageNode = createNode(metric, result); - for (String childKey : result.getChildren()) { - CoverageResult childResult = result.getChild(childKey); - coverageNode.add(convert(childResult)); - } - return coverageNode; - } - } - - private CoverageNode createNode(final CoverageMetric metric, final CoverageResult result) { - if (metric.equals(CoverageMetric.METHOD)) { - Optional line = result.getAdditionalProperty("lineNumber").stream().findAny(); - if (line.isPresent() && line.get().matches("\\d+")) { - return new MethodCoverageNode(result.getName(), Integer.parseInt(line.get())); - } - // fallback if method line has not been set properly since this is a temporary workaround - // until the adapter structure has been replaced - return new MethodCoverageNode(result.getName(), 0); - } - if (metric.equals(CoverageMetric.FILE)) { - FileCoverageNode fileCoverageNode = new FileCoverageNode(result.getName(), result.getRelativeSourcePath()); - attachCoverageLineMapping(result, fileCoverageNode); - return fileCoverageNode; - } - if (metric.equals(CoverageMetric.PACKAGE)) { - return new PackageCoverageNode(result.getName()); - } - return new CoverageNode(metric, result.getName()); - } - - private void attachCoverageLineMapping(final CoverageResult result, final FileCoverageNode node) { - CoveragePaint paint = result.getPaint(); - if (paint != null) { - paintedFiles.put(node, paint); - attachCoveragePerLine(node, paint); - } - } - - private void attachCoveragePerLine(final FileCoverageNode node, final CoveragePaint paint) { - int[] lines = paint.getAllLines(); - SortedMap coverageDetails = new TreeMap<>(); - for (int line : lines) { - if (paint.getBranchTotal(line) > 0) { - int covered = paint.getBranchCoverage(line); - int missed = paint.getBranchTotal(line) - covered; - coverageDetails.put(line, new Coverage.CoverageBuilder().setCovered(paint.getBranchCoverage(line)) - .setMissed(missed) - .build()); - } - else { - int covered = paint.getHits(line) > 0 ? 1 : 0; - coverageDetails.put(line, - new Coverage.CoverageBuilder().setCovered(covered).setMissed(1 - covered).build()); - } - } - node.setCoveragePerLine(coverageDetails); - } - - public Set> getPaintedFiles() { - return paintedFiles.entrySet(); - } -} diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoveragePercentage.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoveragePercentage.java index b4f504084..b1387a873 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoveragePercentage.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoveragePercentage.java @@ -7,6 +7,8 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.Fraction; +import edu.hm.hafner.metric.SafeFraction; + /** * Represents a coverage percentage value which can be used in order to show and serialize coverage values. The class * can also be used for transforming a coverage fraction into its percentage representation. The percentage is diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageReporter.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageReporter.java index bda488b01..f2fa6506d 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageReporter.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageReporter.java @@ -1,11 +1,18 @@ package io.jenkins.plugins.coverage.model; +import java.util.HashSet; import java.util.Map; +import java.util.Map.Entry; +import java.util.NavigableMap; import java.util.Optional; import java.util.Set; -import java.util.SortedMap; import java.util.TreeMap; +import org.apache.commons.lang3.math.Fraction; + +import edu.hm.hafner.metric.Metric; +import edu.hm.hafner.metric.Node; +import edu.hm.hafner.metric.Value; import edu.hm.hafner.util.FilteredLog; import edu.umd.cs.findbugs.annotations.CheckForNull; @@ -16,7 +23,7 @@ import io.jenkins.plugins.coverage.model.exception.CodeDeltaException; import io.jenkins.plugins.coverage.model.visualization.code.SourceCodePainter; -import io.jenkins.plugins.coverage.targets.CoverageResult; +import io.jenkins.plugins.coverage.targets.CoveragePaint; import io.jenkins.plugins.forensics.delta.model.Delta; import io.jenkins.plugins.forensics.delta.model.FileChanges; import io.jenkins.plugins.forensics.reference.ReferenceFinder; @@ -44,8 +51,6 @@ public class CoverageReporter { * the workspace on the agent that provides access to the source code files * @param listener * logger - * @param healthReport - * health report * @param scm * the SCM which is used for calculating the code delta to a reference build * @param sourceDirectories @@ -59,23 +64,35 @@ public class CoverageReporter { * if the build has been aborted */ @SuppressWarnings("checkstyle:ParameterNumber") - public void run(final CoverageResult rootResult, final Run build, final FilePath workspace, - final TaskListener listener, final HealthReport healthReport, final String scm, + public void run(final Node rootResult, final Run build, final FilePath workspace, + final TaskListener listener, final String scm, final Set sourceDirectories, final String sourceCodeEncoding, final SourceCodeRetention sourceCodeRetention) throws InterruptedException { LogHandler logHandler = new LogHandler(listener, "Coverage"); FilteredLog log = new FilteredLog("Errors while reporting code coverage results:"); - CoverageNodeConverter converter = new CoverageNodeConverter(); - CoverageNode rootNode = convertCoverageTree(converter, rootResult, log); + verifyPathUniqueness(rootResult, log); + + runWithModel(build, workspace, listener, scm, sourceDirectories, sourceCodeEncoding, + sourceCodeRetention, + logHandler, log, rootResult, new HashSet<>()); + } + private void runWithModel(final Run build, final FilePath workspace, final TaskListener listener, + final String scm, final Set sourceDirectories, + final String sourceCodeEncoding, final SourceCodeRetention sourceCodeRetention, final LogHandler logHandler, + final FilteredLog log, final Node rootNode, + final Set> paintedFiles) + throws InterruptedException { Optional possibleReferenceResult = getReferenceBuildAction(build, log); + HealthReport healthReport = new HealthReport(); // FIXME: currently empty + CoverageBuildAction action; if (possibleReferenceResult.isPresent()) { CoverageBuildAction referenceAction = possibleReferenceResult.get(); - CoverageNode referenceRoot = referenceAction.getResult(); + Node referenceRoot = referenceAction.getResult(); // calculate code delta log.logInfo("Calculating the code delta..."); @@ -120,25 +137,29 @@ public void run(final CoverageResult rootResult, final Run build, final Fi // filtered coverage trees CoverageTreeCreator coverageTreeCreator = new CoverageTreeCreator(); - CoverageNode changeCoverageRoot = coverageTreeCreator.createChangeCoverageTree(rootNode); - CoverageNode indirectCoverageChangesTree = coverageTreeCreator.createIndirectCoverageChangesTree(rootNode); + Node changeCoverageRoot = coverageTreeCreator.createChangeCoverageTree(rootNode); + Node indirectCoverageChangesTree = coverageTreeCreator.createIndirectCoverageChangesTree(rootNode); // coverage delta - SortedMap coverageDelta = - rootNode.computeDeltaAsPercentage(referenceRoot); - SortedMap changeCoverageDelta = - computeChangeCoverageDelta(rootNode, changeCoverageRoot); + NavigableMap coverageDelta = rootNode.computeDelta(referenceRoot); - if (rootNode.hasCodeChanges() && !rootNode.hasChangeCoverage()) { - log.logInfo("No detected code changes affect the code coverage"); + NavigableMap changeCoverageDelta; + if (hasChangeCoverage(changeCoverageRoot)) { + changeCoverageDelta = changeCoverageRoot.computeDelta(rootNode); + } + else { + changeCoverageDelta = new TreeMap<>(); + if (rootNode.hasCodeChanges()) { + log.logInfo("No detected code changes affect the code coverage"); + } } action = new CoverageBuildAction(build, rootNode, healthReport, referenceAction.getOwner().getExternalizableId(), coverageDelta, - changeCoverageRoot.getMetricPercentages(), + changeCoverageRoot.getMetricsDistribution(), changeCoverageDelta, - indirectCoverageChangesTree.getMetricPercentages()); + indirectCoverageChangesTree.getMetricsDistribution()); } else { action = new CoverageBuildAction(build, rootNode, healthReport); @@ -146,7 +167,7 @@ public void run(final CoverageResult rootResult, final Run build, final Fi log.logInfo("Executing source code painting..."); SourceCodePainter sourceCodePainter = new SourceCodePainter(build, workspace); - sourceCodePainter.processSourceCodePainting(converter.getPaintedFiles(), sourceDirectories, + sourceCodePainter.processSourceCodePainting(paintedFiles, sourceDirectories, sourceCodeEncoding, sourceCodeRetention, log); log.logInfo("Finished coverage processing - adding the action to the build..."); @@ -156,47 +177,15 @@ public void run(final CoverageResult rootResult, final Run build, final Fi build.addOrReplaceAction(action); } - /** - * Converts the passed coverage tree to the new model and verifies its path structure. - * - * @param converter - * The {@link CoverageNodeConverter converter} to be used - * @param rootResult - * The {@link CoverageResult root} of the coverage tree to be converted - * @param log - * The log - * - * @return the converted coverage tree which uses the new model - */ - private CoverageNode convertCoverageTree(final CoverageNodeConverter converter, - final CoverageResult rootResult, final FilteredLog log) { - rootResult.stripGroup(); - CoverageNode rootNode = converter.convert(rootResult); - rootNode.splitPackages(); - - log.logInfo("Verify uniqueness of file paths..."); - verifyPathUniqueness(rootNode, log); - - return rootNode; - } - - /** - * Computes the change coverage delta which represents the difference between the change coverage and the overall - * coverage per coverage metric. - * - * @param rootNode - * The root of the overall coverage tree - * @param changeCoverageRoot - * The root of the change coverage tree - * - * @return the delta per metric - */ - private SortedMap computeChangeCoverageDelta( - final CoverageNode rootNode, final CoverageNode changeCoverageRoot) { - if (rootNode.hasChangeCoverage()) { - return changeCoverageRoot.computeDeltaAsPercentage(rootNode); + private boolean hasChangeCoverage(final Node changeCoverageRoot) { + Optional lineCoverage = changeCoverageRoot.getValue(Metric.LINE); + if (lineCoverage.isPresent()) { + if (((edu.hm.hafner.metric.Coverage) lineCoverage.get()).isSet()) { + return true; + } } - return new TreeMap<>(); + Optional branchCoverage = changeCoverageRoot.getValue(Metric.BRANCH); + return branchCoverage.filter(value -> ((edu.hm.hafner.metric.Coverage) value).isSet()).isPresent(); } private Optional getReferenceBuildAction(final Run build, final FilteredLog log) { diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTableModel.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTableModel.java index 0da98b330..72a7aa41b 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTableModel.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTableModel.java @@ -6,6 +6,14 @@ import java.util.Locale; import java.util.stream.Collectors; +import org.apache.commons.lang3.math.Fraction; + +import edu.hm.hafner.metric.Coverage; +import edu.hm.hafner.metric.FileNode; +import edu.hm.hafner.metric.LinesOfCode; +import edu.hm.hafner.metric.Metric; +import edu.hm.hafner.metric.Node; + import hudson.Functions; import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider; @@ -34,11 +42,11 @@ class CoverageTableModel extends TableModel { */ private static final int TABLE_COVERAGE_COLOR_ALPHA = 80; - static final DetailedCell NO_COVERAGE = new DetailedCell<>(Messages.Coverage_Not_Available(), - NO_COVERAGE_SORT); + static final DetailedCell NO_COVERAGE + = new DetailedCell<>(Messages.Coverage_Not_Available(), NO_COVERAGE_SORT); private final ColorProvider colorProvider; - private final CoverageNode root; + private final Node root; private final RowRenderer renderer; private final String id; @@ -54,7 +62,7 @@ class CoverageTableModel extends TableModel { * @param colors * The {@link ColorProvider} which provides the used colors */ - CoverageTableModel(final String id, final CoverageNode root, final RowRenderer renderer, final ColorProvider colors) { + CoverageTableModel(final String id, final Node root, final RowRenderer renderer, final ColorProvider colors) { super(); this.id = id; @@ -140,12 +148,12 @@ public List getColumns() { @Override public List getRows() { Locale browserLocale = Functions.getCurrentLocale(); - return root.getAll(CoverageMetric.FILE).stream() + return root.getAllFileNodes().stream() .map(file -> new CoverageRow(file, browserLocale, renderer, colorProvider)) .collect(Collectors.toList()); } - protected CoverageNode getRoot() { + protected Node getRoot() { return root; } @@ -159,53 +167,54 @@ protected ColorProvider getColorProvider() { static class CoverageRow { private static final String COVERAGE_COLUMN_OUTER = "coverage-column-outer float-end"; private static final String COVERAGE_COLUMN_INNER = "coverage-column-inner"; - private final CoverageNode root; + private static final CoverageFormatter FORMATTER = new CoverageFormatter(); + + private final FileNode file; private final Locale browserLocale; private final RowRenderer renderer; private final ColorProvider colorProvider; - CoverageRow(final CoverageNode root, final Locale browserLocale, final RowRenderer renderer, final ColorProvider colors) { - this.root = root; + CoverageRow(final FileNode file, final Locale browserLocale, final RowRenderer renderer, final ColorProvider colors) { + this.file = file; this.browserLocale = browserLocale; this.renderer = renderer; this.colorProvider = colors; } public String getFileHash() { - return String.valueOf(root.getPath().hashCode()); + return String.valueOf(file.getPath().hashCode()); } public String getFileName() { - return renderer.renderFileName(root.getName(), root.getPath()); + return renderer.renderFileName(file.getName(), file.getPath()); } public String getPackageName() { - return root.getParentName(); + return file.getParentName(); } public DetailedCell getLineCoverage() { - Coverage coverage = root.getCoverage(CoverageMetric.LINE); - return createColoredCoverageColumn(coverage, "The total line coverage of the file"); + return createColoredCoverageColumn(getCoverageOfNode(Metric.LINE)); } public DetailedCell getBranchCoverage() { - Coverage coverage = root.getCoverage(CoverageMetric.BRANCH); - return createColoredCoverageColumn(coverage, "The total branch coverage of the file"); + return createColoredCoverageColumn(getCoverageOfNode(Metric.BRANCH)); + } + + Coverage getCoverageOfNode(final Metric metric) { + return file.getTypedValue(metric, Coverage.nullObject(metric)); } public DetailedCell getLineCoverageDelta() { - return createColoredFileCoverageDeltaColumn(CoverageMetric.LINE); + return createColoredFileCoverageDeltaColumn(Metric.LINE); } public DetailedCell getBranchCoverageDelta() { - return createColoredFileCoverageDeltaColumn(CoverageMetric.BRANCH); + return createColoredFileCoverageDeltaColumn(Metric.BRANCH); } public int getLoc() { - if (root instanceof FileCoverageNode) { // FIXME: Move LOC up in the hierarchy - return ((FileCoverageNode) root).getCoveragePerLine().size(); - } - return 0; + return file.getTypedValue(Metric.LOC, new LinesOfCode(0)).getValue(); } /** @@ -213,14 +222,11 @@ public int getLoc() { * * @param coverage * the coverage of the element - * @param tooltip - * the tooltip which describes the value - * * @return the new {@link DetailedCell} */ - protected DetailedCell createColoredCoverageColumn(final Coverage coverage, final String tooltip) { + protected DetailedCell createColoredCoverageColumn(final Coverage coverage) { if (coverage.isSet()) { - double percentage = coverage.getCoveredPercentage().getDoubleValue(); + double percentage = coverage.getCoveredPercentage().doubleValue() * 100.0; DisplayColors colors = CoverageLevel.getDisplayColorsOfCoverageLevel(percentage, colorProvider); String cell = div().withClasses(COVERAGE_COLUMN_OUTER).with( div().withClasses(COVERAGE_COLUMN_INNER) @@ -228,8 +234,7 @@ protected DetailedCell createColoredCoverageColumn(final Coverage coverage, f "background-image: linear-gradient(90deg, %s %f%%, transparent %f%%);", colors.getFillColorAsRGBAHex(TABLE_COVERAGE_COLOR_ALPHA), percentage, percentage)) - .withTitle(tooltip) - .withText(coverage.formatCoveredPercentage(browserLocale))) + .withText(FORMATTER.formatPercentage(coverage, browserLocale))) .render(); return new DetailedCell<>(cell, percentage); } @@ -239,50 +244,40 @@ protected DetailedCell createColoredCoverageColumn(final Coverage coverage, f /** * Creates a table cell which colorizes the tendency of the shown coverage delta. * - * @param coveragePercentage + * @param delta * The coverage delta as percentage - * @param tooltip - * The tooltip which describes the value * * @return the created {@link DetailedCell} */ - protected DetailedCell createColoredCoverageDeltaColumn( - final CoveragePercentage coveragePercentage, final String tooltip) { - double coverageValue = coveragePercentage.getDoubleValue(); - DisplayColors colors = CoverageChangeTendency.getDisplayColorsForTendency(coverageValue, colorProvider); + protected DetailedCell createColoredCoverageDeltaColumn(final Fraction delta) { + double percentage = delta.doubleValue() * 100.0; + DisplayColors colors = CoverageChangeTendency.getDisplayColorsForTendency(percentage, colorProvider); String cell = div().withClasses(COVERAGE_COLUMN_OUTER).with( div().withClasses(COVERAGE_COLUMN_INNER) .withStyle(String.format("background-color:%s;", colors.getFillColorAsRGBAHex( TABLE_COVERAGE_COLOR_ALPHA))) - .withText(coveragePercentage.formatDeltaPercentage(browserLocale)) - .withTitle(tooltip)) + .withText(FORMATTER.formatDelta(delta, browserLocale))) .render(); - return new DetailedCell<>(cell, coverageValue); + return new DetailedCell<>(cell, percentage); } - protected CoverageNode getRoot() { - return root; + protected FileNode getFile() { + return file; } /** * Creates a colored column for visualizing the file coverage delta against a reference for the passed - * {@link CoverageMetric}. + * {@link Metric}. * - * @param coverageMetric + * @param metric * The coverage metric * * @return the created {@link DetailedCell} * @since 3.0.0 */ - private DetailedCell createColoredFileCoverageDeltaColumn(final CoverageMetric coverageMetric) { - // this is only available for versions later than 3.0.0 which introduced FileCoverageNode - if (root instanceof FileCoverageNode) { - FileCoverageNode fileNode = (FileCoverageNode) root; - if (fileNode.hasFileCoverageDelta(coverageMetric)) { - CoveragePercentage delta = fileNode.getFileCoverageDeltaForMetric(coverageMetric); - return createColoredCoverageDeltaColumn(delta, - "The total file coverage delta against the reference build"); - } + private DetailedCell createColoredFileCoverageDeltaColumn(final Metric metric) { + if (file.hasChangeCoverage(metric)) { + return createColoredCoverageDeltaColumn(file.getChangeCoverage(metric)); } return NO_COVERAGE; } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTreeCreator.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTreeCreator.java index 0d6bb68ee..5cd8a537d 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTreeCreator.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTreeCreator.java @@ -3,10 +3,12 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Map.Entry; -import java.util.stream.Collectors; -import io.jenkins.plugins.coverage.model.Coverage.CoverageBuilder; +import edu.hm.hafner.metric.Coverage; +import edu.hm.hafner.metric.Coverage.CoverageBuilder; +import edu.hm.hafner.metric.FileNode; +import edu.hm.hafner.metric.Metric; +import edu.hm.hafner.metric.Node; /** * Creates coverage trees which represent different types of coverage. @@ -14,7 +16,6 @@ * @author Florian Orendi */ public class CoverageTreeCreator { - /** * Creates a coverage tree which represents the change coverage. * @@ -23,8 +24,8 @@ public class CoverageTreeCreator { * * @return the filtered tree */ - public CoverageNode createChangeCoverageTree(final CoverageNode coverageNode) { - CoverageNode copy = coverageNode.copyTree(); + public Node createChangeCoverageTree(final Node coverageNode) { + Node copy = coverageNode.copyTree(); boolean treeExists = calculateChangeCoverageTree(copy); if (treeExists) { @@ -45,8 +46,8 @@ public CoverageNode createChangeCoverageTree(final CoverageNode coverageNode) { * * @return the filtered tree */ - public CoverageNode createIndirectCoverageChangesTree(final CoverageNode coverageNode) { - CoverageNode copy = coverageNode.copyTree(); + public Node createIndirectCoverageChangesTree(final Node coverageNode) { + Node copy = coverageNode.copyTree(); boolean treeExists = calculateIndirectCoverageChangesTree(copy); if (treeExists) { @@ -63,22 +64,21 @@ public CoverageNode createIndirectCoverageChangesTree(final CoverageNode coverag * Recursively calculates a coverage tree which represents the change coverage. * * @param root - * The {@link CoverageNode root} of the tree + * The {@link Node root} of the tree * * @return {@code true} whether the tree has been calculated successfully, else {@link false} */ - private boolean calculateChangeCoverageTree(final CoverageNode root) { - if (root instanceof FileCoverageNode) { - FileCoverageNode fileNode = (FileCoverageNode) root; + private boolean calculateChangeCoverageTree(final Node root) { + if (root instanceof FileNode) { + FileNode fileNode = (FileNode) root; clearChildrenAndLeaves(fileNode); - // this is required since there might be changes which do not effect the code coverage -> ignore these files - return fileNode.getCoveragePerLine().keySet().stream() - .anyMatch(line -> fileNode.getChangedCodeLines().contains(line)); + // this is required since there might be changes which do not affect the code coverage -> ignore these files + return fileNode.hasCoveredLinesInChangeSet(); } - Iterator nodeIterator = root.getChildren().iterator(); + Iterator nodeIterator = root.getChildren().iterator(); boolean hasChanged = false; while (nodeIterator.hasNext()) { - CoverageNode child = nodeIterator.next(); + Node child = nodeIterator.next(); boolean childHasChanged = calculateChangeCoverageTree(child); if (!childHasChanged) { nodeIterator.remove(); @@ -92,19 +92,19 @@ private boolean calculateChangeCoverageTree(final CoverageNode root) { * Recursively calculates a coverage tree which represents the indirect coverage changes. * * @param root - * The {@link CoverageNode root} of the tree + * The {@link Node root} of the tree * * @return {@code true} whether the tree has been calculated successfully, else {@link false} */ - private boolean calculateIndirectCoverageChangesTree(final CoverageNode root) { - if (root instanceof FileCoverageNode) { + private boolean calculateIndirectCoverageChangesTree(final Node root) { + if (root instanceof FileNode) { clearChildrenAndLeaves(root); - return !((FileCoverageNode) root).getIndirectCoverageChanges().isEmpty(); + return ((FileNode) root).hasIndirectCoverageChanges(); } - Iterator nodeIterator = root.getChildren().iterator(); + Iterator nodeIterator = root.getChildren().iterator(); boolean hasChangedCoverage = false; while (nodeIterator.hasNext()) { - CoverageNode child = nodeIterator.next(); + Node child = nodeIterator.next(); boolean childHasChangedCoverage = calculateIndirectCoverageChangesTree(child); if (!childHasChangedCoverage) { nodeIterator.remove(); @@ -115,74 +115,66 @@ private boolean calculateIndirectCoverageChangesTree(final CoverageNode root) { } /** - * Attaches leaves to the passed {@link CoverageNode node} which represent its underlying change coverage. + * Attaches leaves to the given {@link Node node} which represent its underlying change coverage. * * @param node * The node which contains the change coverage */ - private void attachChangeCoverageLeaves(final CoverageNode node) { - node.getAllFileCoverageNodes() - .forEach(fileNode -> { - List changes = fileNode.getCoveragePerLine() - .entrySet().stream() - .filter(entry -> fileNode.getChangedCodeLines().contains(entry.getKey())) - .map(Entry::getValue) - .collect(Collectors.toList()); - createChangeCoverageLeaves(fileNode, changes); - }); + private void attachChangeCoverageLeaves(final Node node) { + node.getAllFileNodes() + .forEach(fileNode -> createChangeCoverageLeaves(fileNode, fileNode.getCoverageOfChangeSet())); } /** - * Attaches leaves to the passed {@link CoverageNode node} which represent its underlying indirect coverage + * Attaches leaves to the passed {@link Node node} which represent its underlying indirect coverage * changes. * * @param node * The node which contains indirect coverage changes */ - private void attachIndirectCoverageChangesLeaves(final CoverageNode node) { - node.getAllFileCoverageNodes().stream() - .filter(fileNode -> !fileNode.getIndirectCoverageChanges().isEmpty()) + private void attachIndirectCoverageChangesLeaves(final Node node) { + node.getAllFileNodes().stream() + .filter(FileNode::hasIndirectCoverageChanges) .forEach(this::createIndirectCoverageChangesLeaves); } /** - * Creates both a line and a branch change coverage leaf for the passed {@link FileCoverageNode node}. + * Creates both a line and a branch change coverage leaf for the given {@link FileNode node}. * * @param fileNode * The node the leaves are attached to * @param changes * The {@link Coverage} to be represented by the leaves */ - private void createChangeCoverageLeaves(final FileCoverageNode fileNode, final List changes) { - Coverage lineCoverage = CoverageBuilder.NO_COVERAGE; - Coverage branchCoverage = CoverageBuilder.NO_COVERAGE; + private void createChangeCoverageLeaves(final FileNode fileNode, final List changes) { + Coverage lineCoverage = Coverage.nullObject(Metric.LINE); + Coverage branchCoverage = Coverage.nullObject(Metric.BRANCH); + + CoverageBuilder builder = new CoverageBuilder(); for (Coverage change : changes) { int covered = change.getCovered() > 0 ? 1 : 0; if (change.getTotal() > 1) { - branchCoverage = branchCoverage.add(new Coverage.CoverageBuilder().setCovered(change.getCovered()) - .setMissed(change.getMissed()) - .build()); - lineCoverage = lineCoverage.add( - new Coverage.CoverageBuilder().setCovered(covered).setMissed(1 - covered).build()); + builder.setMetric(Metric.BRANCH).setCovered(change.getCovered()).setMissed(change.getMissed()); + branchCoverage = branchCoverage.add(builder.build()); + builder.setMetric(Metric.LINE).setCovered(covered).setMissed(1 - covered); + lineCoverage = lineCoverage.add(builder.build()); } else { int missed = change.getMissed() > 0 ? 1 : 0; - lineCoverage = lineCoverage.add( - new Coverage.CoverageBuilder().setCovered(covered).setMissed(missed).build()); + builder.setMetric(Metric.LINE).setCovered(covered).setMissed(missed); + lineCoverage = lineCoverage.add(builder.build()); } } if (lineCoverage.isSet()) { - CoverageLeaf lineCoverageLeaf = new CoverageLeaf(CoverageMetric.LINE, lineCoverage); - fileNode.add(lineCoverageLeaf); + fileNode.addValue(lineCoverage); } if (branchCoverage.isSet()) { - CoverageLeaf branchCoverageLeaf = new CoverageLeaf(CoverageMetric.BRANCH, branchCoverage); - fileNode.add(branchCoverageLeaf); + fileNode.addValue(branchCoverage); } } /** - * Creates both a line and a branch indirect coverage changes leaf for the passed {@link FileCoverageNode node}. The + * Creates both a line and a branch indirect coverage changes leaf for the passed {@link FileNode node}. The * leaves represent the delta for a file regarding the amount of lines / branches that got hit by tests. * * @param fileNode @@ -190,53 +182,54 @@ private void createChangeCoverageLeaves(final FileCoverageNode fileNode, final L */ @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.CognitiveComplexity"}) // there is no useful possibility for outsourcing code - private void createIndirectCoverageChangesLeaves(final FileCoverageNode fileNode) { - Coverage lineCoverage = CoverageBuilder.NO_COVERAGE; - Coverage branchCoverage = CoverageBuilder.NO_COVERAGE; + private void createIndirectCoverageChangesLeaves(final FileNode fileNode) { + Coverage lineCoverage = Coverage.nullObject(Metric.LINE); + Coverage branchCoverage = Coverage.nullObject(Metric.BRANCH); for (Map.Entry change : fileNode.getIndirectCoverageChanges().entrySet()) { int delta = change.getValue(); - Coverage currentCoverage = fileNode.getCoveragePerLine().get(change.getKey()); + Coverage currentLineCoverage = fileNode.getLineCoverage(change.getKey()); + Coverage currentBranchCoverage = fileNode.getBranchCoverage(change.getKey()); + CoverageBuilder builder = new CoverageBuilder(); if (delta > 0) { // the line is fully covered - even in case of branch coverage - if (delta == currentCoverage.getCovered()) { - lineCoverage = lineCoverage.add(new Coverage.CoverageBuilder().setCovered(1).setMissed(0).build()); + if (delta == currentLineCoverage.getCovered()) { + builder.setMetric(Metric.LINE).setCovered(1).setMissed(0); + lineCoverage = lineCoverage.add(builder.build()); } // the branch coverage increased for 'delta' hits - if (currentCoverage.getTotal() > 1) { - branchCoverage = branchCoverage.add( - new Coverage.CoverageBuilder().setCovered(delta).setMissed(0).build()); + if (currentBranchCoverage.getTotal() > 1) { + builder.setMetric(Metric.BRANCH).setCovered(delta).setMissed(0); + branchCoverage = branchCoverage.add(builder.build()); } } else if (delta < 0) { // the line is not covered anymore - if (currentCoverage.getCovered() == 0) { - lineCoverage = lineCoverage.add(new Coverage.CoverageBuilder().setCovered(0).setMissed(1).build()); + if (currentLineCoverage.getCovered() == 0) { + builder.setMetric(Metric.LINE).setCovered(0).setMissed(1); + lineCoverage = lineCoverage.add(builder.build()); } // the branch coverage is decreased by 'delta' hits - if (currentCoverage.getTotal() > 1) { - branchCoverage = branchCoverage.add( - new Coverage.CoverageBuilder().setCovered(0).setMissed(Math.abs(delta)).build()); + if (currentBranchCoverage.getTotal() > 1) { + builder.setMetric(Metric.BRANCH).setCovered(0).setMissed(Math.abs(delta)); } } } if (lineCoverage.isSet()) { - CoverageLeaf lineCoverageLeaf = new CoverageLeaf(CoverageMetric.LINE, lineCoverage); - fileNode.add(lineCoverageLeaf); + fileNode.addValue(lineCoverage); } if (branchCoverage.isSet()) { - CoverageLeaf branchCoverageLeaf = new CoverageLeaf(CoverageMetric.BRANCH, branchCoverage); - fileNode.add(branchCoverageLeaf); + fileNode.addValue(branchCoverage); } } /** - * Clears all leaves and children of the passed {@link CoverageNode}. + * Clears all leaves and children of the passed {@link Node}. * * @param coverageNode * The processed node */ - private void clearChildrenAndLeaves(final CoverageNode coverageNode) { - coverageNode.getChildren().clear(); - coverageNode.getLeaves().clear(); + // TODO: can't we create a new tree? + private void clearChildrenAndLeaves(final Node coverageNode) { + coverageNode.clear(); } } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageViewModel.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageViewModel.java index 0a11230be..fe9d58486 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageViewModel.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageViewModel.java @@ -5,10 +5,11 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.NavigableMap; import java.util.NoSuchElementException; import java.util.Optional; import java.util.Set; -import java.util.SortedMap; +import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -23,6 +24,11 @@ import edu.hm.hafner.echarts.JacksonFacade; import edu.hm.hafner.echarts.LinesChartModel; import edu.hm.hafner.echarts.TreeMapNode; +import edu.hm.hafner.metric.Coverage; +import edu.hm.hafner.metric.FileNode; +import edu.hm.hafner.metric.Metric; +import edu.hm.hafner.metric.Node; +import edu.hm.hafner.metric.Value; import edu.umd.cs.findbugs.annotations.CheckForNull; import org.kohsuke.stapler.StaplerRequest; @@ -63,11 +69,11 @@ public class CoverageViewModel extends DefaultAsyncTableContentProvider implemen private static final String INLINE_SUFFIX = "-inline"; private final Run owner; - private final CoverageNode node; + private final Node node; private final String id; - private final CoverageNode changeCoverageTreeRoot; - private final CoverageNode indirectCoverageChangesTreeRoot; + private final Node changeCoverageTreeRoot; + private final Node indirectCoverageChangesTreeRoot; private ColorProvider colorProvider = ColorProviderFactory.createDefaultColorProvider(); @@ -79,7 +85,7 @@ public class CoverageViewModel extends DefaultAsyncTableContentProvider implemen * @param node * the coverage node to be shown */ - public CoverageViewModel(final Run owner, final CoverageNode node) { + public CoverageViewModel(final Run owner, final Node node) { super(); this.owner = owner; @@ -87,8 +93,9 @@ public CoverageViewModel(final Run owner, final CoverageNode node) { this.id = "coverage"; // TODO: this needs to be a parameter // initialize filtered coverage trees so that they will not be calculated multiple times - this.changeCoverageTreeRoot = node.getChangeCoverageTree(); - this.indirectCoverageChangesTreeRoot = node.getIndirectCoverageChangesTree(); + CoverageTreeCreator treeCreator = new CoverageTreeCreator(); + this.changeCoverageTreeRoot = treeCreator.createChangeCoverageTree(node); + this.indirectCoverageChangesTreeRoot = treeCreator.createIndirectCoverageChangesTree(node); } public String getId() { @@ -99,7 +106,7 @@ public String getId() { return owner; } - public CoverageNode getNode() { + public Node getNode() { return node; } @@ -150,15 +157,10 @@ private ColorProvider createColorProvider(final String json) { } } + // FIXME: check why this model works on a filtered view @JavaScriptMethod public CoverageOverview getOverview() { - return new CoverageOverview(getNode().filterPackageStructure()); - } - - // FIXME: currently not in use anymore - @JavaScriptMethod - public CoverageOverview getChangeCoverageOverview() { - return new CoverageOverview(changeCoverageTreeRoot.filterPackageStructure()); + return new CoverageOverview(node); } /** @@ -199,7 +201,7 @@ private Optional getLatestAction() { @JavaScriptMethod @SuppressWarnings("unused") public TreeMapNode getCoverageTree(final String coverageMetric) { - CoverageMetric metric = getCoverageMetricFromText(coverageMetric); + Metric metric = getCoverageMetricFromText(coverageMetric); return TREE_MAP_NODE_CONVERTER.toTeeChartModel(getNode(), metric, colorProvider); } @@ -215,7 +217,7 @@ public TreeMapNode getCoverageTree(final String coverageMetric) { @JavaScriptMethod @SuppressWarnings("unused") public TreeMapNode getChangeCoverageTree(final String coverageMetric) { - CoverageMetric metric = getCoverageMetricFromText(coverageMetric); + Metric metric = getCoverageMetricFromText(coverageMetric); return TREE_MAP_NODE_CONVERTER.toTeeChartModel(changeCoverageTreeRoot, metric, colorProvider); } @@ -231,12 +233,12 @@ public TreeMapNode getChangeCoverageTree(final String coverageMetric) { @JavaScriptMethod @SuppressWarnings("unused") public TreeMapNode getCoverageChangesTree(final String coverageMetric) { - CoverageMetric metric = getCoverageMetricFromText(coverageMetric); + Metric metric = getCoverageMetricFromText(coverageMetric); return TREE_MAP_NODE_CONVERTER.toTeeChartModel(indirectCoverageChangesTreeRoot, metric, colorProvider); } /** - * Gets the {@link CoverageMetric} from a String representation used in the frontend. Only 'Line' and 'Branch' is + * Gets the {@link Metric} from a String representation used in the frontend. Only 'Line' and 'Branch' is * possible. 'Line' is used as a default. * * @param text @@ -244,11 +246,11 @@ public TreeMapNode getCoverageChangesTree(final String coverageMetric) { * * @return the coverage metric */ - private CoverageMetric getCoverageMetricFromText(final String text) { + private Metric getCoverageMetricFromText(final String text) { if ("Branch".equals(text)) { - return CoverageMetric.BRANCH; + return Metric.BRANCH; } - return CoverageMetric.LINE; + return Metric.LINE; } /** @@ -317,11 +319,11 @@ public String getUrlForBuild(final String selectedBuildDisplayName, final String */ @JavaScriptMethod public String getSourceCode(final String fileHash, final String tableId) { - Optional targetResult - = getNode().findByHashCode(CoverageMetric.FILE, Integer.parseInt(fileHash)); + Optional targetResult + = getNode().findByHashCode(Metric.FILE, Integer.parseInt(fileHash)); if (targetResult.isPresent()) { try { - CoverageNode fileNode = targetResult.get(); + Node fileNode = targetResult.get(); return readSourceCode(fileNode, tableId); } catch (IOException | InterruptedException exception) { @@ -332,7 +334,7 @@ public String getSourceCode(final String fileHash, final String tableId) { } /** - * Reads the sourcecode corresponding to the passed {@link CoverageNode node} and filters the code dependent on the + * Reads the sourcecode corresponding to the passed {@link Node node} and filters the code dependent on the * table ID. * * @param sourceNode @@ -346,7 +348,7 @@ public String getSourceCode(final String fileHash, final String tableId) { * @throws InterruptedException * if reading failed */ - private String readSourceCode(final CoverageNode sourceNode, final String tableId) + private String readSourceCode(final Node sourceNode, final String tableId) throws IOException, InterruptedException { String content = ""; File rootDir = getOwner().getRootDir(); @@ -357,9 +359,9 @@ private String readSourceCode(final CoverageNode sourceNode, final String tableI content = new TextFile(getFileForBuildsWithOldVersion(rootDir, sourceNode.getName())).read(); // fallback with sources persisted using the < 2.1.0 serialization } - if (!content.isEmpty() && sourceNode instanceof FileCoverageNode) { + if (!content.isEmpty() && sourceNode instanceof FileNode) { + FileNode fileNode = (FileNode) sourceNode; String cleanTableId = StringUtils.removeEnd(tableId, INLINE_SUFFIX); - FileCoverageNode fileNode = (FileCoverageNode) sourceNode; if (CHANGE_COVERAGE_TABLE_ID.equals(cleanTableId)) { return SOURCE_CODE_FACADE.calculateChangeCoverageSourceCode(content, fileNode); } @@ -390,7 +392,7 @@ public boolean hasSourceCode() { * @return {@code true} whether change coverage exists, else {@code false} */ public boolean hasChangeCoverage() { - return getNode().hasChangeCoverage(); + return getNode().getAllFileNodes().stream().anyMatch(FileNode::hasCodeChanges); } /** @@ -399,18 +401,18 @@ public boolean hasChangeCoverage() { * @return {@code true} whether indirect coverage changes exist, else {@code false} */ public boolean hasIndirectCoverageChanges() { - return getNode().hasIndirectCoverageChanges(); + return getNode().getAllFileNodes().stream().anyMatch(FileNode::hasIndirectCoverageChanges); } /** * Returns whether the source file is available in Jenkins build folder. * * @param coverageNode - * The {@link CoverageNode} which is checked if there is a source file available + * The {@link Node} which is checked if there is a source file available * * @return {@code true} if the source file is available, {@code false} otherwise */ - public boolean isSourceFileAvailable(final CoverageNode coverageNode) { + public boolean isSourceFileAvailable(final Node coverageNode) { return isSourceFileInNewFormatAvailable(coverageNode) || isSourceFileInOldFormatAvailable(coverageNode); } @@ -419,11 +421,11 @@ public boolean isSourceFileAvailable(final CoverageNode coverageNode) { * less than 2.1.0. * * @param coverageNode - * The {@link CoverageNode} which is checked if there is a source file available + * The {@link Node} which is checked if there is a source file available * * @return {@code true} if the source file is available, {@code false} otherwise */ - public boolean isSourceFileInOldFormatAvailable(final CoverageNode coverageNode) { + public boolean isSourceFileInOldFormatAvailable(final Node coverageNode) { return isSourceFileInOldFormatAvailable(getOwner().getRootDir(), coverageNode.getName()); } @@ -454,11 +456,11 @@ private static String sanitizeFilename(final String inputName) { * greater or equal than 2.1.0. * * @param coverageNode - * The {@link CoverageNode} which is checked if there is a source file available + * The {@link Node} which is checked if there is a source file available * * @return {@code true} if the source file is available, {@code false} otherwise */ - public boolean isSourceFileInNewFormatAvailable(final CoverageNode coverageNode) { + public boolean isSourceFileInNewFormatAvailable(final Node coverageNode) { return isSourceFileInNewFormatAvailable(getOwner().getRootDir(), id, coverageNode.getPath()); } @@ -483,8 +485,8 @@ static boolean isSourceFileInNewFormatAvailable(final File rootDir, final String public SourceViewModel getDynamic(final String link, final StaplerRequest request, final StaplerResponse response) { if (StringUtils.isNotEmpty(link)) { try { - Optional targetResult - = getNode().findByHashCode(CoverageMetric.FILE, Integer.parseInt(link)); + Optional targetResult + = getNode().findByHashCode(Metric.FILE, Integer.parseInt(link)); if (targetResult.isPresent()) { return new SourceViewModel(getOwner(), targetResult.get()); } @@ -499,17 +501,18 @@ public SourceViewModel getDynamic(final String link, final StaplerRequest reques /** * UI model for the coverage overview bar chart. Shows the coverage results for the different coverage metrics. */ + // FIXME: In the new model we do not only have coverages public static class CoverageOverview { - private final CoverageNode coverage; + private final Node coverage; - CoverageOverview(final CoverageNode coverage) { + CoverageOverview(final Node coverage) { this.coverage = coverage; } public List getMetrics() { return getMetricsDistribution().keySet().stream() .skip(1) // ignore the root of the tree as the coverage is always 1 of 1 - .map(CoverageMetric::getName) + .map(Metric::name) // FIXME: we need to create localized names .collect(Collectors.toList()); } @@ -518,9 +521,7 @@ public List getCovered() { } public List getCoveredPercentages() { - return streamCoverages().map(Coverage::getCoveredFraction) - .map(Fraction::doubleValue) - .collect(Collectors.toList()); + return getPercentages(Coverage::getCoveredPercentage); } public List getMissed() { @@ -528,16 +529,25 @@ public List getMissed() { } public List getMissedPercentages() { - return streamCoverages().map(Coverage::getMissedFraction) + return getPercentages(Coverage::getMissedPercentage); + } + + private List getPercentages(final Function displayType) { + return streamCoverages().map(displayType) .map(Fraction::doubleValue) + .map(p -> p * 100.0) .collect(Collectors.toList()); } private Stream streamCoverages() { - return getMetricsDistribution().values().stream().skip(1); + return getMetricsDistribution().values() + .stream() + .skip(1) + .filter(v -> v instanceof Coverage) + .map(Coverage.class::cast); } - private SortedMap getMetricsDistribution() { + private NavigableMap getMetricsDistribution() { return coverage.getMetricsDistribution(); } } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageXmlStream.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageXmlStream.java index ed5147010..826ba2389 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageXmlStream.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageXmlStream.java @@ -1,300 +1,64 @@ package io.jenkins.plugins.coverage.model; -import java.util.AbstractMap.SimpleEntry; -import java.util.Arrays; -import java.util.Map; -import java.util.Map.Entry; -import java.util.NavigableMap; -import java.util.NavigableSet; -import java.util.Set; -import java.util.SortedMap; -import java.util.TreeMap; -import java.util.TreeSet; -import java.util.function.Function; -import java.util.stream.Collector; -import java.util.stream.Collectors; - -import org.apache.commons.lang3.StringUtils; - -import com.thoughtworks.xstream.converters.Converter; -import com.thoughtworks.xstream.converters.MarshallingContext; -import com.thoughtworks.xstream.converters.UnmarshallingContext; -import com.thoughtworks.xstream.io.HierarchicalStreamReader; -import com.thoughtworks.xstream.io.HierarchicalStreamWriter; +import edu.hm.hafner.metric.ClassNode; +import edu.hm.hafner.metric.ContainerNode; +import edu.hm.hafner.metric.Coverage; +import edu.hm.hafner.metric.CyclomaticComplexity; +import edu.hm.hafner.metric.FileNode; +import edu.hm.hafner.metric.LinesOfCode; +import edu.hm.hafner.metric.MethodNode; +import edu.hm.hafner.metric.Metric; +import edu.hm.hafner.metric.ModuleNode; +import edu.hm.hafner.metric.MutationValue; +import edu.hm.hafner.metric.Node; +import edu.hm.hafner.metric.PackageNode; import hudson.util.XStream2; import io.jenkins.plugins.util.AbstractXmlStream; /** - * Configures the XML stream for the coverage tree, which consists of {@link CoverageNode}s. + * Configures the XML stream for the coverage tree, which consists of {@link Node}s. */ -class CoverageXmlStream extends AbstractXmlStream { - private static final Collector ARRAY_JOINER = Collectors.joining(", ", "[", "]"); - - private static String[] toArray(final String value) { - String cleanInput = StringUtils.removeEnd(StringUtils.removeStart(StringUtils.deleteWhitespace(value), "["), "]"); - - return StringUtils.split(cleanInput, ","); - } - +class CoverageXmlStream extends AbstractXmlStream { /** - * Creates an XML stream for {@link CoverageNode}. + * Creates an XML stream for {@link Node}. */ CoverageXmlStream() { - super(CoverageNode.class); + super(Node.class); } @Override protected void configureXStream(final XStream2 xStream) { - xStream.alias("node", CoverageNode.class); - xStream.alias("package", PackageCoverageNode.class); - xStream.alias("file", FileCoverageNode.class); - xStream.alias("method", MethodCoverageNode.class); - xStream.alias("leaf", CoverageLeaf.class); + xStream.alias("container", ContainerNode.class); + xStream.alias("module", ModuleNode.class); + xStream.alias("package", PackageNode.class); + xStream.alias("file", FileNode.class); + xStream.alias("class", ClassNode.class); + xStream.alias("method", MethodNode.class); xStream.alias("coverage", Coverage.class); + xStream.alias("mutation", MutationValue.class); + xStream.alias("complexity", CyclomaticComplexity.class); + xStream.alias("loc", LinesOfCode.class); xStream.alias("percentage", CoveragePercentage.class); - xStream.addImmutableType(CoverageMetric.class, false); + xStream.addImmutableType(Metric.class, false); xStream.addImmutableType(Coverage.class, false); + xStream.addImmutableType(LinesOfCode.class, false); + xStream.addImmutableType(CyclomaticComplexity.class, false); + /* FIXME: restore converters xStream.addImmutableType(CoveragePercentageConverter.class, false); xStream.registerConverter(new CoverageMetricConverter()); xStream.registerConverter(new CoverageConverter(xStream)); xStream.registerConverter(new CoveragePercentageConverter()); - xStream.registerLocalConverter(FileCoverageNode.class, "coveragePerLine", new LineMapConverter()); - xStream.registerLocalConverter(FileCoverageNode.class, "fileCoverageDelta", new MetricPercentageMapConverter()); - xStream.registerLocalConverter(FileCoverageNode.class, "indirectCoverageChanges", new HitsMapConverter()); - xStream.registerLocalConverter(FileCoverageNode.class, "changedCodeLines", new IntegerSetConverter()); + xStream.registerLocalConverter(FileNode.class, "coveragePerLine", new LineMapConverter()); + xStream.registerLocalConverter(FileNode.class, "fileCoverageDelta", new MetricPercentageMapConverter()); + xStream.registerLocalConverter(FileNode.class, "indirectCoverageChanges", new HitsMapConverter()); + xStream.registerLocalConverter(FileNode.class, "changedCodeLines", new IntegerSetConverter()); + */ } @Override - protected CoverageNode createDefaultValue() { - return new CoverageNode(CoverageMetric.MODULE, "Empty"); - } - - /** - * {@link Converter} for {@link CoverageMetric} instances so that only the string name will be serialized. After - * reading the values back from the stream, the string representation will be converted to an actual instance - * again. - */ - private static final class CoverageMetricConverter implements Converter { - @SuppressWarnings("PMD.NullAssignment") - @Override - public void marshal(final Object source, final HierarchicalStreamWriter writer, - final MarshallingContext context) { - writer.setValue(source instanceof CoverageMetric ? ((CoverageMetric) source).getName() : null); - } - - @Override - public Object unmarshal(final HierarchicalStreamReader reader, final UnmarshallingContext context) { - return CoverageMetric.valueOf(reader.getValue()); - } - - @Override - public boolean canConvert(final Class type) { - return type == CoverageMetric.class; - } - } - - /** - * {@link Converter} for {@link Coverage} instances so that only the values will be serialized. After reading the - * values back from the stream, the string representation will be converted to an actual instance again. - */ - private static final class CoverageConverter extends XStream2.PassthruConverter { - CoverageConverter(final XStream2 xStream) { - super(xStream); - } - - @SuppressWarnings("PMD.NullAssignment") - @Override - public void marshal(final Object source, final HierarchicalStreamWriter writer, - final MarshallingContext context) { - writer.setValue(source instanceof Coverage ? ((Coverage) source).serializeToString() : null); - } - - @Override - public Object unmarshal(final HierarchicalStreamReader reader, final UnmarshallingContext context) { - String value = reader.getValue(); - if (StringUtils.contains(value, '/')) { - return Coverage.valueOf(value); - } - return super.unmarshal(reader, context); // old versions before 3.0.0 - } - - @Override - protected void callback(final Coverage obj, final UnmarshallingContext context) { - // nothing to do - } - - @Override - public boolean canConvert(final Class type) { - return type == Coverage.class; - } - } - - /** - * {@link Converter} for {@link CoveragePercentage} instances so that only the values will be serialized. After - * reading the values back from the stream, the string representation will be converted to an actual instance - * again. - */ - private static final class CoveragePercentageConverter implements Converter { - @SuppressWarnings("PMD.NullAssignment") - @Override - public void marshal(final Object source, final HierarchicalStreamWriter writer, - final MarshallingContext context) { - writer.setValue( - source instanceof CoveragePercentage ? ((CoveragePercentage) source).serializeToString() : null); - } - - @Override - public CoveragePercentage unmarshal(final HierarchicalStreamReader reader, final UnmarshallingContext context) { - return CoveragePercentage.valueOf(reader.getValue()); - } - - @Override - public boolean canConvert(final Class type) { - return type == CoveragePercentage.class; - } - } - - /** - * {@link Converter} for a {@link TreeSet} of integers that serializes just the values. After - * reading the values back from the stream, the string representation will be converted to an actual instance - * again. - */ - static final class IntegerSetConverter implements Converter { - @SuppressWarnings({"PMD.NullAssignment", "unchecked"}) - @Override - public void marshal(final Object source, final HierarchicalStreamWriter writer, - final MarshallingContext context) { - writer.setValue(source instanceof TreeSet ? marshal((TreeSet) source) : null); - } - - String marshal(final Set lines) { - return lines.stream().map(String::valueOf).collect(ARRAY_JOINER); - } - - @Override - public NavigableSet unmarshal(final HierarchicalStreamReader reader, final UnmarshallingContext context) { - return unmarshal(reader.getValue()); - } - - NavigableSet unmarshal(final String value) { - return Arrays.stream(toArray(value)).map(Integer::valueOf).collect(Collectors.toCollection(TreeSet::new)); - } - - @Override - public boolean canConvert(final Class type) { - return type == TreeSet.class; - } - } - - /** - * {@link Converter} base class for {@link TreeMap} instance. Stores the mappings in a condensed format - * {@code key1: value1, key2: value2, ...}. - * - * @param - * the type of keys maintained by this map - * @param - * the type of mapped values - */ - abstract static class TreeMapConverter, V> implements Converter { - @Override - @SuppressWarnings({"PMD.NullAssignment", "unchecked"}) - public void marshal(final Object source, final HierarchicalStreamWriter writer, - final MarshallingContext context) { - writer.setValue(source instanceof NavigableMap ? marshal((NavigableMap) source) : null); - } - - String marshal(final SortedMap source) { - return source.entrySet() - .stream() - .map(createMapEntry()) - .collect(ARRAY_JOINER); - } - - @Override - public boolean canConvert(final Class type) { - return type == TreeMap.class; - } - - @Override - public NavigableMap unmarshal(final HierarchicalStreamReader reader, final UnmarshallingContext context) { - return unmarshal(reader.getValue()); - } - - NavigableMap unmarshal(final String value) { - NavigableMap map = new TreeMap<>(); - - for (String marshalledValue : toArray(value)) { - if (StringUtils.contains(marshalledValue, ":")) { - try { - Entry entry = createMapping( - StringUtils.substringBefore(marshalledValue, ':'), - StringUtils.substringAfter(marshalledValue, ':')); - map.put(entry.getKey(), entry.getValue()); - } - catch (IllegalArgumentException exception) { - // ignore - } - } - } - return map; - } - - protected abstract Function, String> createMapEntry(); - - protected abstract Map.Entry createMapping(String key, String value); - - protected SimpleEntry entry(final K key, final V value) { - return new SimpleEntry<>(key, value); - } - } - - /** - * {@link Converter} for a {@link SortedMap} of coverages per line. Stores the mapping in the condensed format - * {@code key1: covered1/missed1, key2: covered2/missed2, ...}. - */ - static final class LineMapConverter extends TreeMapConverter { - @Override - protected Function, String> createMapEntry() { - return e -> String.format("%d: %s", e.getKey(), e.getValue().serializeToString()); - } - - @Override - protected Entry createMapping(final String key, final String value) { - return entry(Integer.valueOf(key), Coverage.valueOf(value)); - } - } - - /** - * {@link Converter} for a {@link SortedMap} of coverage percentages per metric. Stores the mapping in the condensed - * format {@code metric1: numerator1/denominator1, metric2: numerator2/denominator2, ...}. - */ - static final class MetricPercentageMapConverter extends TreeMapConverter { - @Override - protected Function, String> createMapEntry() { - return e -> String.format("%s: %s", e.getKey().getName(), e.getValue().serializeToString()); - } - - @Override - protected Entry createMapping(final String key, final String value) { - return entry(CoverageMetric.valueOf(key), CoveragePercentage.valueOf(value)); - } - } - - /** - * {@link Converter} for a {@link SortedMap} of coverage hits per line. Stores the mapping in the condensed - * format {@code line1: hits1, line2: hits2, ...}. - */ - static final class HitsMapConverter extends TreeMapConverter { - @Override - protected Function, String> createMapEntry() { - return e -> String.format("%d: %d", e.getKey(), e.getValue()); - } - - @Override - protected Entry createMapping(final String key, final String value) { - return entry(Integer.valueOf(key), Integer.valueOf(value)); - } + protected Node createDefaultValue() { + return new ModuleNode("Empty"); } } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/FileChangesProcessor.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/FileChangesProcessor.java index 2e549bf2e..6ba686ef3 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/FileChangesProcessor.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/FileChangesProcessor.java @@ -6,6 +6,7 @@ import java.util.Comparator; import java.util.List; import java.util.Map; +import java.util.NavigableMap; import java.util.Optional; import java.util.Set; import java.util.SortedMap; @@ -14,20 +15,21 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; -import org.apache.commons.lang3.math.Fraction; +import edu.hm.hafner.metric.Coverage; +import edu.hm.hafner.metric.FileNode; +import edu.hm.hafner.metric.Node; import io.jenkins.plugins.forensics.delta.model.Change; import io.jenkins.plugins.forensics.delta.model.ChangeEditType; import io.jenkins.plugins.forensics.delta.model.FileChanges; /** - * Calculates and attaches values to the {@link FileCoverageNode nodes} of the coverage tree which represent the changes + * Calculates and attaches values to the {@link FileNode nodes} of the coverage tree which represent the changes * concerning code and coverage. * * @author Florian Orendi */ public class FileChangesProcessor { - /** * Attaches the changed code lines to the file nodes of the coverage tree. * @@ -36,30 +38,28 @@ public class FileChangesProcessor { * @param codeChanges * The code changes to be attached */ - public void attachChangedCodeLines(final CoverageNode coverageNode, final Map codeChanges) { - Map nodePathMapping = coverageNode.getAllFileCoverageNodes().stream() - .collect(Collectors.toMap(FileCoverageNode::getPath, Function.identity())); + public void attachChangedCodeLines(final Node coverageNode, final Map codeChanges) { + Map nodePathMapping = coverageNode.getAllFileNodes().stream() + .collect(Collectors.toMap(Node::getPath, Function.identity())); codeChanges.forEach((path, fileChange) -> { if (nodePathMapping.containsKey(path)) { - CoverageNode changedNode = nodePathMapping.get(path); - if (changedNode instanceof FileCoverageNode) { - attachChanges((FileCoverageNode) changedNode, fileChange.getChangesByType(ChangeEditType.INSERT)); - attachChanges((FileCoverageNode) changedNode, fileChange.getChangesByType(ChangeEditType.REPLACE)); - } + FileNode changedNode = nodePathMapping.get(path); + attachChanges(changedNode, fileChange.getChangesByType(ChangeEditType.INSERT)); + attachChanges(changedNode, fileChange.getChangesByType(ChangeEditType.REPLACE)); } }); } /** - * Attaches a set of changes to a specific {@link FileCoverageNode node}. + * Attaches a set of changes to a specific {@link FileNode node}. * * @param changedNode * The node which contains code changes * @param relevantChanges * The relevant changes */ - private void attachChanges(final FileCoverageNode changedNode, final Set relevantChanges) { + private void attachChanges(final FileNode changedNode, final Set relevantChanges) { for (Change change : relevantChanges) { for (int i = change.getFromLine(); i <= change.getToLine(); i++) { changedNode.addChangedCodeLine(i); @@ -78,10 +78,10 @@ private void attachChanges(final FileCoverageNode changedNode, final Set * @param oldPathMapping * A mapping between the report paths of the current and the reference coverage tree */ - public void attachFileCoverageDeltas(final CoverageNode root, final CoverageNode referenceNode, + public void attachFileCoverageDeltas(final Node root, final Node referenceNode, final Map oldPathMapping) { - Map fileNodes = getFileNodeMappingWithReferencePaths(root, oldPathMapping); - Map referenceFileNodes = getReferenceFileNodeMapping(fileNodes, referenceNode); + Map fileNodes = getFileNodeMappingWithReferencePaths(root, oldPathMapping); + Map referenceFileNodes = getReferenceFileNodeMapping(fileNodes, referenceNode); fileNodes.entrySet().stream() .filter(entry -> referenceFileNodes.containsKey(entry.getKey())) .forEach(entry -> attachFileCoverageDelta(entry.getValue(), referenceFileNodes.get(entry.getKey()))); @@ -91,18 +91,12 @@ public void attachFileCoverageDeltas(final CoverageNode root, final CoverageNode * Attaches the delta between the total coverage of a file against the same file from the reference build. * * @param fileNode - * The {@link FileCoverageNode node} which represents the total coverage of a file + * The {@link FileNode node} which represents the total coverage of a file * @param referenceNode - * The {@link FileCoverageNode reference node} which represents the coverage of the reference file + * The {@link FileNode reference node} which represents the coverage of the reference file */ - private void attachFileCoverageDelta(final FileCoverageNode fileNode, final FileCoverageNode referenceNode) { - SortedMap referenceCoverage = referenceNode.getMetricFractions(); - fileNode.getMetricFractions().forEach((metric, value) -> { - if (referenceCoverage.containsKey(metric)) { - Fraction delta = value.subtract(referenceCoverage.get(metric)); - fileNode.putFileCoverageDelta(metric, CoveragePercentage.valueOf(delta)); - } - }); + private void attachFileCoverageDelta(final FileNode fileNode, final FileNode referenceNode) { + fileNode.computeDelta(referenceNode); } /** @@ -117,14 +111,14 @@ private void attachFileCoverageDelta(final FileCoverageNode fileNode, final File * @param oldPathMapping * A mapping between the report paths of the current and the reference coverage tree */ - public void attachIndirectCoveragesChanges(final CoverageNode root, final CoverageNode referenceNode, + public void attachIndirectCoveragesChanges(final Node root, final Node referenceNode, final Map codeChanges, final Map oldPathMapping) { - Map fileNodes = getFileNodeMappingWithReferencePaths(root, oldPathMapping); - Map referenceFileNodes = getReferenceFileNodeMapping(fileNodes, referenceNode); + Map fileNodes = getFileNodeMappingWithReferencePaths(root, oldPathMapping); + Map referenceFileNodes = getReferenceFileNodeMapping(fileNodes, referenceNode); - for (Map.Entry entry : fileNodes.entrySet()) { + for (Map.Entry entry : fileNodes.entrySet()) { String referencePath = entry.getKey(); - FileCoverageNode fileNode = entry.getValue(); + FileNode fileNode = entry.getValue(); Optional> referenceCoveragePerLine = getReferenceCoveragePerLine(referenceFileNodes, referencePath); if (referenceCoveragePerLine.isPresent()) { @@ -147,15 +141,15 @@ public void attachIndirectCoveragesChanges(final CoverageNode root, final Covera * @param referenceCoverageMapping * A mapping which contains the coverage per line of the reference file */ - private void attachIndirectCoverageChangeForFile(final FileCoverageNode fileNode, + private void attachIndirectCoverageChangeForFile(final FileNode fileNode, final SortedMap referenceCoverageMapping) { - fileNode.getCoveragePerLine().forEach((line, coverage) -> { - if (!fileNode.getChangedCodeLines().contains(line) && referenceCoverageMapping.containsKey(line)) { + fileNode.getLineNumberToLineCoverage().forEach((line, coverage) -> { + if (!fileNode.getChangedLines().contains(line) && referenceCoverageMapping.containsKey(line)) { Coverage referenceCoverage = referenceCoverageMapping.get(line); int covered = coverage.getCovered(); int referenceCovered = referenceCoverage.getCovered(); if (covered != referenceCovered) { - fileNode.putIndirectCoverageChange(line, covered - referenceCovered); + fileNode.addIndirectCoverageChange(line, covered - referenceCovered); } } }); @@ -173,9 +167,9 @@ private void attachIndirectCoverageChangeForFile(final FileCoverageNode fileNode * @return an Optional of the coverage mapping if existent, else an empty Optional */ private Optional> getReferenceCoveragePerLine( - final Map references, final String fullyQualifiedName) { + final Map references, final String fullyQualifiedName) { if (references.containsKey(fullyQualifiedName)) { - SortedMap coveragePerLine = references.get(fullyQualifiedName).getCoveragePerLine(); + NavigableMap coveragePerLine = references.get(fullyQualifiedName).getLineNumberToLineCoverage(); if (coveragePerLine != null && !coveragePerLine.isEmpty()) { return Optional.of(coveragePerLine); } @@ -273,20 +267,20 @@ private List> transformCoveragePerLine( } /** - * Gets all {@link FileCoverageNode file nodes} from the actual build which also exist within the reference build - * and maps them by their fully qualified name from the reference. + * Gets all {@link FileNode file nodes} from the currently running build which also exist within the + * reference build and maps them by their fully qualified name from the reference. * * @param root - * The root node of the currently build coverage tree + * the root node of the coverage tree of the currently running build * @param oldPathMapping - * A mapping between the report fully qualified names of the current and the reference coverage tree + * a mapping between the report fully qualified names of the current and the reference coverage tree * * @return the created node mapping whose keys are fully qualified names from the reference and which values are the * corresponding nodes from the actual build */ - private Map getFileNodeMappingWithReferencePaths( - final CoverageNode root, final Map oldPathMapping) { - return root.getAllFileCoverageNodes().stream() + private Map getFileNodeMappingWithReferencePaths( + final Node root, final Map oldPathMapping) { + return root.getAllFileNodes().stream() .filter(node -> oldPathMapping.containsKey(node.getPath())) .collect(Collectors.toMap(node -> oldPathMapping.get(node.getPath()), Function.identity())); } @@ -302,11 +296,11 @@ private Map getFileNodeMappingWithReferencePaths( * * @return the created node mapping */ - private Map getReferenceFileNodeMapping( - final Map nodeMapping, final CoverageNode referenceNode) { - return referenceNode.getAllFileCoverageNodes().stream() + private Map getReferenceFileNodeMapping( + final Map nodeMapping, final Node referenceNode) { + return referenceNode.getAllFileNodes().stream() .filter(reference -> nodeMapping.containsKey(reference.getPath())) - .collect(Collectors.toMap(FileCoverageNode::getPath, Function.identity())); + .collect(Collectors.toMap(FileNode::getPath, Function.identity())); } /** diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/FileCoverageNode.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/FileCoverageNode.java deleted file mode 100644 index 1307f74bd..000000000 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/FileCoverageNode.java +++ /dev/null @@ -1,217 +0,0 @@ -package io.jenkins.plugins.coverage.model; - -import java.io.ObjectStreamException; -import java.util.Objects; -import java.util.SortedMap; -import java.util.SortedSet; -import java.util.TreeMap; -import java.util.TreeSet; - -import org.apache.commons.lang3.StringUtils; - -import edu.umd.cs.findbugs.annotations.CheckForNull; - -/** - * A {@link CoverageNode} for a specific file. It stores the actual file name along the coverage information. - * - * @author Ullrich Hafner - */ -public class FileCoverageNode extends CoverageNode { - private static final long serialVersionUID = -3795695377267542624L; - - private final String sourcePath; - - /** - * The {@link Coverage} represents both line and branch coverage per line since it can be differentiated by the - * total number of covered and missed cases and saves disk space. - */ - private SortedMap coveragePerLine = new TreeMap<>(); // since 3.0.0 - private SortedMap fileCoverageDelta = new TreeMap<>(); // since 3.0.0 - private SortedMap indirectCoverageChanges = new TreeMap<>(); // since 3.0.0 - private SortedSet changedCodeLines = new TreeSet<>(); // since 3.0.0 - - /** - * Creates a new {@link FileCoverageNode} with the given name. - * - * @param name - * the human-readable name of the node - * @param sourcePath - * optional path to the source code of this node - */ - public FileCoverageNode(final String name, @CheckForNull final String sourcePath) { - super(CoverageMetric.FILE, name); - this.sourcePath = StringUtils.defaultString(sourcePath); - } - - @Override - public String getPath() { - return mergePath(sourcePath); - } - - /** - * Called after de-serialization to retain backward compatibility. - * - * @return this - * @throws ObjectStreamException - * if the operation failed - */ - @Override - protected Object readResolve() throws ObjectStreamException { - super.readResolve(); - if (fileCoverageDelta == null) { - fileCoverageDelta = new TreeMap<>(); - } - if (indirectCoverageChanges == null) { - indirectCoverageChanges = new TreeMap<>(); - } - if (changedCodeLines == null) { - changedCodeLines = new TreeSet<>(); - } - if (coveragePerLine == null) { - coveragePerLine = new TreeMap<>(); - } - return this; - } - - /** - * Checks whether the file coverage delta exists for the passed {@link CoverageMetric}. - * - * @param coverageMetric - * The coverage metric - * - * @return {@code true} whether the coverage delta exists, else {@code false} - */ - public boolean hasFileCoverageDelta(final CoverageMetric coverageMetric) { - return fileCoverageDelta.containsKey(coverageMetric); - } - - /** - * Gets the file coverage delta for the passed {@link CoverageMetric}. - * - * @param coverageMetric - * The coverage metric - * - * @return the file coverage delta as percentage - */ - public CoveragePercentage getFileCoverageDeltaForMetric(final CoverageMetric coverageMetric) { - return fileCoverageDelta.get(coverageMetric); - } - - /** - * Adds a code line that has been changed. - * - * @param line - * The changed code line - */ - public void addChangedCodeLine(final int line) { - changedCodeLines.add(line); - } - - /** - * Adds a {@link CoveragePercentage file coverage delta} of this file against a reference for the passed {@link - * CoverageMetric}. - * - * @param coverageMetric - * The coverage metric - * @param delta - * The coverage delta as percentage - */ - public void putFileCoverageDelta(final CoverageMetric coverageMetric, final CoveragePercentage delta) { - fileCoverageDelta.put(coverageMetric, delta); - } - - /** - * Adds the {@link Coverage} for a specific line of code. - * - * @param line - * The line - * @param coverage - * The coverage - */ - public void putCoveragePerLine(final int line, final Coverage coverage) { - coveragePerLine.put(line, coverage); - } - - /** - * Adds an indirect coverage change for a specific line. - * - * @param line - * The line with the coverage change - * @param hitsDelta - * The delta of the coverage hits before and after the code changes - */ - public void putIndirectCoverageChange(final int line, final int hitsDelta) { - indirectCoverageChanges.put(line, hitsDelta); - } - - public void setFileCoverageDelta(final SortedMap fileCoverageDelta) { - this.fileCoverageDelta = fileCoverageDelta; - } - - public void setChangedCodeLines(final SortedSet changes) { - changedCodeLines = changes; - } - - public SortedSet getChangedCodeLines() { - return changedCodeLines; - } - - public void setCoveragePerLine(final SortedMap coverage) { - coveragePerLine = coverage; - } - - public SortedMap getCoveragePerLine() { - return coveragePerLine; - } - - public SortedMap getIndirectCoverageChanges() { - return indirectCoverageChanges; - } - - public void setIndirectCoverageChanges(final SortedMap changes) { - indirectCoverageChanges = changes; - } - - @Override - protected FileCoverageNode copyTree(@CheckForNull final CoverageNode copiedParent) { - CoverageNode copy = super.copyTree(copiedParent); - - FileCoverageNode fileCoverageNode = (FileCoverageNode) copy; - fileCoverageNode.setCoveragePerLine(new TreeMap<>(coveragePerLine)); - fileCoverageNode.setChangedCodeLines(new TreeSet<>(changedCodeLines)); - fileCoverageNode.setIndirectCoverageChanges(new TreeMap<>(indirectCoverageChanges)); - fileCoverageNode.setFileCoverageDelta(new TreeMap<>(fileCoverageDelta)); - - return fileCoverageNode; - } - - @Override - protected CoverageNode copyEmpty() { - return new FileCoverageNode(getName(), sourcePath); - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - if (!super.equals(o)) { - return false; - } - FileCoverageNode that = (FileCoverageNode) o; - return Objects.equals(sourcePath, that.sourcePath) - && Objects.equals(fileCoverageDelta, that.fileCoverageDelta) - && Objects.equals(coveragePerLine, that.coveragePerLine) - && Objects.equals(changedCodeLines, that.changedCodeLines) - && Objects.equals(indirectCoverageChanges, that.indirectCoverageChanges); - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), sourcePath, fileCoverageDelta, - coveragePerLine, changedCodeLines, indirectCoverageChanges); - } -} diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/FilePathValidator.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/FilePathValidator.java index 3b11f1845..1d3da6796 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/FilePathValidator.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/FilePathValidator.java @@ -3,6 +3,7 @@ import java.util.List; import java.util.stream.Collectors; +import edu.hm.hafner.metric.Node; import edu.hm.hafner.util.FilteredLog; import one.util.streamex.StreamEx; @@ -13,7 +14,6 @@ * @author Florian Orendi */ class FilePathValidator { - static final String AMBIGUOUS_FILES_MESSAGE = "There are ambiguous file paths which might lead to faulty coverage reports:"; static final String REMOVED_MESSAGE = @@ -37,16 +37,11 @@ private FilePathValidator() { * @param log * The log */ - static void verifyPathUniqueness(final CoverageNode root, final FilteredLog log) { - List duplicates = StreamEx.of(root.getAllFileCoverageNodes()) - .map(FileCoverageNode::getPath) + static void verifyPathUniqueness(final Node root, final FilteredLog log) { + List duplicates = StreamEx.of(root.getFiles()) .distinct(2) .collect(Collectors.toList()); if (!duplicates.isEmpty()) { - root.getAllFileCoverageNodes().stream() - .filter(node -> duplicates.contains(node.getPath())) - .forEach(CoverageNode::remove); - String message = AMBIGUOUS_FILES_MESSAGE + System.lineSeparator() + String.join("," + System.lineSeparator(), duplicates); log.logError(message); diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/IndirectCoverageChangesTable.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/IndirectCoverageChangesTable.java index ce6a3d99b..6a49ad471 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/IndirectCoverageChangesTable.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/IndirectCoverageChangesTable.java @@ -2,10 +2,12 @@ import java.util.List; import java.util.Locale; -import java.util.Optional; import java.util.stream.Collectors; -import org.apache.commons.lang3.math.Fraction; +import edu.hm.hafner.metric.Coverage; +import edu.hm.hafner.metric.FileNode; +import edu.hm.hafner.metric.Metric; +import edu.hm.hafner.metric.Node; import hudson.Functions; @@ -17,8 +19,9 @@ * * @since 3.0.0 */ +// FIXME: create a base class for changes table, there is too much duplication class IndirectCoverageChangesTable extends CoverageTableModel { - private final CoverageNode changeRoot; + private final Node changeRoot; /** * Creates an indirect coverage changes table model. @@ -34,7 +37,7 @@ class IndirectCoverageChangesTable extends CoverageTableModel { * @param colorProvider * The {@link ColorProvider} which provides the used colors */ - IndirectCoverageChangesTable(final String id, final CoverageNode root, final CoverageNode changeRoot, + IndirectCoverageChangesTable(final String id, final Node root, final Node changeRoot, final RowRenderer renderer, final ColorProvider colorProvider) { super(id, root, renderer, colorProvider); @@ -44,18 +47,18 @@ class IndirectCoverageChangesTable extends CoverageTableModel { @Override public List getRows() { Locale browserLocale = Functions.getCurrentLocale(); - return changeRoot.getAllFileCoverageNodes().stream() + return changeRoot.getAllFileNodes().stream() .map(file -> new IndirectCoverageChangesRow( getOriginalNode(file), file, browserLocale, getRenderer(), getColorProvider())) .collect(Collectors.toList()); } - private FileCoverageNode getOriginalNode(final FileCoverageNode fileNode) { - Optional reference = getRoot().getAllFileCoverageNodes().stream() + private FileNode getOriginalNode(final FileNode fileNode) { + return getRoot().getAllFileNodes().stream() .filter(node -> node.getPath().equals(fileNode.getPath()) && node.getName().equals(fileNode.getName())) - .findFirst(); - return reference.orElse(fileNode); // return this as fallback to prevent exceptions + .findFirst() + .orElse(fileNode); // return this as fallback to prevent exceptions } /** @@ -64,50 +67,36 @@ private FileCoverageNode getOriginalNode(final FileCoverageNode fileNode) { * @since 3.0.0 */ private static class IndirectCoverageChangesRow extends CoverageRow { - private final FileCoverageNode changedFileNode; + private final FileNode originalFile; - IndirectCoverageChangesRow(final FileCoverageNode root, final FileCoverageNode changedFileNode, + IndirectCoverageChangesRow(final FileNode originalFile, final FileNode changedFileNode, final Locale browserLocale, final RowRenderer renderer, final ColorProvider colorProvider) { - super(root, browserLocale, renderer, colorProvider); + super(changedFileNode, browserLocale, renderer, colorProvider); - this.changedFileNode = changedFileNode; - } - - @Override - public DetailedCell getLineCoverage() { - Coverage coverage = changedFileNode.getCoverage(CoverageMetric.LINE); - return createColoredCoverageColumn(coverage, "The indirect line coverage changes"); - } - - @Override - public DetailedCell getBranchCoverage() { - Coverage coverage = changedFileNode.getCoverage(CoverageMetric.BRANCH); - return createColoredCoverageColumn(coverage, "The indirect branch coverage changes"); + this.originalFile = originalFile; } @Override public DetailedCell getLineCoverageDelta() { - return createColoredChangeCoverageDeltaColumn(CoverageMetric.LINE); + return createColoredChangeCoverageDeltaColumn(Metric.LINE); } @Override public DetailedCell getBranchCoverageDelta() { - return createColoredChangeCoverageDeltaColumn(CoverageMetric.BRANCH); + return createColoredChangeCoverageDeltaColumn(Metric.BRANCH); } @Override public int getLoc() { - return changedFileNode.getIndirectCoverageChanges().size(); + return originalFile.getIndirectCoverageChanges().size(); } private DetailedCell createColoredChangeCoverageDeltaColumn( - final CoverageMetric coverageMetric) { - Coverage changeCoverage = changedFileNode.getCoverage(coverageMetric); + final Metric metric) { + Coverage changeCoverage = getFile().getTypedValue(metric, Coverage.nullObject(metric)); if (changeCoverage.isSet()) { - Fraction delta = changeCoverage.getCoveredFraction() - .subtract(getRoot().getCoverage(coverageMetric).getCoveredFraction()); - return createColoredCoverageDeltaColumn(CoveragePercentage.valueOf(delta), - "The indirect coverage changes within the file against the total file coverage"); + return createColoredCoverageDeltaColumn( + changeCoverage.delta(originalFile.getTypedValue(metric, Coverage.nullObject(metric)))); } return NO_COVERAGE; } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/MethodCoverageNode.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/MethodCoverageNode.java deleted file mode 100644 index 624052481..000000000 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/MethodCoverageNode.java +++ /dev/null @@ -1,69 +0,0 @@ -package io.jenkins.plugins.coverage.model; - -import java.util.Objects; - -/** - * A {@link CoverageNode} for a specific method. - * - * @author Florian Orendi - */ -public class MethodCoverageNode extends CoverageNode { - - private static final long serialVersionUID = -5765205034179396434L; - - /** - * The line number where the code of method begins (not including the method head). - */ - private final int lineNumber; - - /** - * Creates a new coverage item node with the given name. - * - * @param name - * The human-readable name of the node - * @param lineNumber - * The line number where the method begins (not including the method head) - */ - public MethodCoverageNode(final String name, final int lineNumber) { - super(CoverageMetric.METHOD, name); - this.lineNumber = lineNumber; - } - - /** - * Checks whether the line number is valid. - * - * @return {@code true} if the line number is valid, else {@code false} - */ - public boolean hasValidLineNumber() { - return lineNumber > 0; - } - - public int getLineNumber() { - return lineNumber; - } - - @Override - protected CoverageNode copyEmpty() { - return new MethodCoverageNode(getName(), getLineNumber()); - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - if (!super.equals(o)) { - return false; - } - MethodCoverageNode that = (MethodCoverageNode) o; - return lineNumber == that.lineNumber; - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), lineNumber); - } -} diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/PackageCoverageNode.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/PackageCoverageNode.java deleted file mode 100644 index 3a8a73a7c..000000000 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/PackageCoverageNode.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.jenkins.plugins.coverage.model; - -/** - * A {@link CoverageNode} for a specific package. It converts a package structure to a corresponding path structure. - * - * @author Ullrich Hafner - */ -public class PackageCoverageNode extends CoverageNode { - private static final long serialVersionUID = 8236436628673022634L; - - /** - * Creates a new coverage item node with the given name. - * - * @param name - * the human-readable name of the node - */ - public PackageCoverageNode(final String name) { - super(CoverageMetric.PACKAGE, name); - } - - @Override - public String getPath() { - return mergePath(getName().replaceAll("\\.", "/")); - } - - @Override - protected CoverageNode copyEmpty() { - return new PackageCoverageNode(getName()); - } -} diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/SafeFraction.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/SafeFraction.java deleted file mode 100644 index 596c6317d..000000000 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/SafeFraction.java +++ /dev/null @@ -1,80 +0,0 @@ -package io.jenkins.plugins.coverage.model; - -import org.apache.commons.lang3.math.Fraction; - -/** - * A small wrapper for {@link Fraction} instances that avoids an arithmetic overflow by using double based operations in - * case of an exception. - * - * @author Ullrich Hafner - */ -public class SafeFraction { - private final Fraction fraction; - - /** - * Creates a new fraction instance that wraps the specified fraction with safe operations. - * - * @param fraction - * the fraction to wrap - */ - public SafeFraction(final Fraction fraction) { - this.fraction = fraction; - } - - /** - * Multiplies the value of this fraction by another, returning the result in reduced form. Since there might be an - * arithmetic exception due to an overflow, this method will handle this situation by calculating the multiplication - * based on the double values of the fractions. - * - * @param multiplicator - * the fraction to multiply by - * - * @return a {@code Fraction} instance with the resulting values - */ - public Fraction multiplyBy(final Fraction multiplicator) { - try { - return fraction.multiplyBy(multiplicator); - } - catch (ArithmeticException exception) { - return Fraction.getFraction(fraction.doubleValue() * multiplicator.doubleValue()); - } - } - - /** - * Subtracts the value of another fraction from the value of this one, returning the result in reduced form. Since - * there might be an arithmetic exception due to an overflow, this method will handle this situation by calculating - * the subtraction based on the double values of the fractions. - * - * @param subtrahend - * the fraction to subtract - * - * @return a {@code Fraction} instance with the resulting values - */ - public Fraction subtract(final Fraction subtrahend) { - try { - return fraction.subtract(subtrahend); - } - catch (ArithmeticException exception) { - return Fraction.getFraction(fraction.doubleValue() - subtrahend.doubleValue()); - } - } - - /** - * Adds the value of another fraction to the value of this one, returning the result in reduced form. Since - * there might be an arithmetic exception due to an overflow, this method will handle this situation by calculating - * the addition based on the double values of the fractions. - * - * @param summand - * the fraction to add - * - * @return a {@code Fraction} instance with the resulting values - */ - public Fraction add(final Fraction summand) { - try { - return fraction.add(summand); - } - catch (ArithmeticException exception) { - return Fraction.getFraction(fraction.doubleValue() + summand.doubleValue()); - } - } -} diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/SourceViewModel.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/SourceViewModel.java index 6cc868db6..3d36402e0 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/SourceViewModel.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/SourceViewModel.java @@ -5,6 +5,8 @@ import org.apache.commons.lang3.exception.ExceptionUtils; +import edu.hm.hafner.metric.Node; + import hudson.model.Run; import hudson.util.TextFile; @@ -25,7 +27,7 @@ public class SourceViewModel extends CoverageViewModel { * @param fileNode * the selected file node of the coverage tree */ - public SourceViewModel(final Run owner, final CoverageNode fileNode) { + public SourceViewModel(final Run owner, final Node fileNode) { super(owner, fileNode); } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/charts/CoverageSeriesBuilder.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/charts/CoverageSeriesBuilder.java index f958a1862..0ea757cd8 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/charts/CoverageSeriesBuilder.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/charts/CoverageSeriesBuilder.java @@ -3,7 +3,10 @@ import java.util.HashMap; import java.util.Map; +import org.apache.commons.lang3.math.Fraction; + import edu.hm.hafner.echarts.SeriesBuilder; +import edu.hm.hafner.metric.Coverage; import io.jenkins.plugins.coverage.model.CoverageBuildAction; @@ -20,9 +23,22 @@ public class CoverageSeriesBuilder extends SeriesBuilder { protected Map computeSeries(final CoverageBuildAction coverageBuildAction) { Map series = new HashMap<>(); - series.put(LINE_COVERAGE, coverageBuildAction.getLineCoverage().getRoundedPercentage()); - series.put(BRANCH_COVERAGE, coverageBuildAction.getBranchCoverage().getRoundedPercentage()); - + series.put(LINE_COVERAGE, getRoundedPercentage(coverageBuildAction.getLineCoverage())); + series.put(BRANCH_COVERAGE, getRoundedPercentage(coverageBuildAction.getBranchCoverage())); return series; } + + /** + * Returns the covered percentage as rounded integer value in the range of {@code [0, 100]}. + * + * @return the covered percentage + */ + // TODO: we should make the charts accept float values + public int getRoundedPercentage(final Coverage coverage) { + if (coverage.isSet()) { + return (int) Math.round(coverage.getCoveredPercentage() + .multiplyBy(Fraction.getFraction(100, 1)).doubleValue()); + } + return 0; + } } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodeFacade.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodeFacade.java index e61739c8b..5c17bbb4a 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodeFacade.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodeFacade.java @@ -29,6 +29,8 @@ import org.jsoup.parser.Parser; import org.jsoup.select.Elements; +import edu.hm.hafner.metric.FileNode; +import edu.hm.hafner.metric.Node; import edu.hm.hafner.util.FilteredLog; import hudson.FilePath; @@ -37,8 +39,6 @@ import hudson.util.TextFile; import jenkins.MasterToSlaveFileCallable; -import io.jenkins.plugins.coverage.model.CoverageNode; -import io.jenkins.plugins.coverage.model.FileCoverageNode; import io.jenkins.plugins.coverage.targets.CoveragePaint; import io.jenkins.plugins.prism.CharsetValidation; import io.jenkins.plugins.prism.FilePermissionEnforcer; @@ -165,14 +165,12 @@ public boolean hasStoredSourceCode(final File buildResults, final String id) { * @param content * The original HTML content * @param fileNode - * The {@link FileCoverageNode node} which represents the coverage of the file + * The {@link FileNode node} which represents the coverage of the file * * @return the filtered HTML sourcecode view */ - public String calculateChangeCoverageSourceCode(final String content, final FileCoverageNode fileNode) { - Set lines = fileNode.getChangedCodeLines().stream() - .filter(line -> fileNode.getCoveragePerLine().containsKey(line)) - .collect(Collectors.toSet()); + public String calculateChangeCoverageSourceCode(final String content, final FileNode fileNode) { + Set lines = fileNode.getCoveredLines(); Set linesAsText = lines.stream().map(String::valueOf).collect(Collectors.toSet()); Document doc = Jsoup.parse(content, Parser.xmlParser()); int maxLine = Integer.parseInt(Objects.requireNonNull( @@ -204,11 +202,11 @@ else if (!linesAsText.contains(line)) { * @param content * The original HTML content * @param fileNode - * The {@link FileCoverageNode node} which represents the coverage of the file + * The {@link FileNode node} which represents the coverage of the file * * @return the filtered HTML sourcecode view */ - public String calculateIndirectCoverageChangesSourceCode(final String content, final FileCoverageNode fileNode) { + public String calculateIndirectCoverageChangesSourceCode(final String content, final FileNode fileNode) { Map lines = fileNode.getIndirectCoverageChanges(); Map indirectCoverageChangesAsText = lines.entrySet().stream() .collect(Collectors @@ -353,7 +351,7 @@ private static String sanitizeFilename(final String inputName) { * @param directory * the subdirectory where the source files will be stored in */ - AgentCoveragePainter(final Set> paintedFiles, + AgentCoveragePainter(final Set> paintedFiles, final Set permittedSourceDirectories, final Set requestedSourceDirectories, final String sourceCodeEncoding, final String directory) { super(); @@ -567,15 +565,15 @@ private void deleteFolder(final File folder, final FilteredLog log) { private static class PaintedNode implements Serializable { private static final long serialVersionUID = -6044649044983631852L; - private final CoverageNode node; + private final Node node; private final CoveragePaint paint; - PaintedNode(final CoverageNode node, final CoveragePaint paint) { + PaintedNode(final Node node, final CoveragePaint paint) { this.node = node; this.paint = paint; } - public CoverageNode getNode() { + public Node getNode() { return node; } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodePainter.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodePainter.java index 66799e2fb..0f8a8c41a 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodePainter.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodePainter.java @@ -5,13 +5,13 @@ import java.util.Set; import java.util.stream.Collectors; +import edu.hm.hafner.metric.Node; import edu.hm.hafner.util.FilteredLog; import edu.umd.cs.findbugs.annotations.NonNull; import hudson.FilePath; import hudson.model.Run; -import io.jenkins.plugins.coverage.model.CoverageNode; import io.jenkins.plugins.coverage.model.visualization.code.SourceCodeFacade.AgentCoveragePainter; import io.jenkins.plugins.coverage.targets.CoveragePaint; import io.jenkins.plugins.prism.PermittedSourceCodeDirectory; @@ -56,7 +56,7 @@ public SourceCodePainter(@NonNull final Run build, @NonNull final FilePath * @throws InterruptedException * if the painting process has been interrupted */ - public void processSourceCodePainting(final Set> paintedFiles, + public void processSourceCodePainting(final Set> paintedFiles, final Set sourceDirectories, final String sourceCodeEncoding, final SourceCodeRetention sourceCodeRetention, final FilteredLog log) throws InterruptedException { @@ -72,7 +72,7 @@ public void processSourceCodePainting(final Set> paintedFiles, + private void paintFilesOnAgent(final Set> paintedFiles, final Set requestedSourceDirectories, final String sourceCodeEncoding, final FilteredLog log) throws InterruptedException { try { diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ChangeCoverage.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ChangeCoverage.java index 71f97acbd..e75a60815 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ChangeCoverage.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ChangeCoverage.java @@ -3,8 +3,11 @@ import java.util.Locale; import java.util.Optional; +import edu.hm.hafner.metric.Coverage; +import edu.hm.hafner.metric.Metric; +import edu.hm.hafner.metric.Value; + import io.jenkins.plugins.coverage.model.CoverageBuildAction; -import io.jenkins.plugins.coverage.model.CoverageMetric; import io.jenkins.plugins.coverage.model.CoveragePercentage; import io.jenkins.plugins.coverage.model.Messages; import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors; @@ -16,7 +19,6 @@ * @author Florian Orendi */ public class ChangeCoverage extends CoverageColumnType { - /** * Creates a column type to be used for representing the change coverage. */ @@ -25,9 +27,12 @@ public ChangeCoverage() { } @Override - public Optional getCoverage(final CoverageBuildAction action, final CoverageMetric metric) { + public Optional getCoverage(final CoverageBuildAction action, final Metric metric) { if (action.hasChangeCoverage(metric)) { - return Optional.of(action.getChangeCoverage(metric).getCoveredPercentage()); + Value changeCoverage = action.getChangeCoverage(metric); + if (changeCoverage instanceof Coverage) { + return Optional.of(CoveragePercentage.valueOf(((Coverage) changeCoverage).getCoveredPercentage())); + } } return Optional.empty(); } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ChangeCoverageDelta.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ChangeCoverageDelta.java index d089a94b4..a02f02f99 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ChangeCoverageDelta.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ChangeCoverageDelta.java @@ -3,8 +3,9 @@ import java.util.Locale; import java.util.Optional; +import edu.hm.hafner.metric.Metric; + import io.jenkins.plugins.coverage.model.CoverageBuildAction; -import io.jenkins.plugins.coverage.model.CoverageMetric; import io.jenkins.plugins.coverage.model.CoveragePercentage; import io.jenkins.plugins.coverage.model.Messages; import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors; @@ -16,7 +17,6 @@ * @author Florian Orendi */ public class ChangeCoverageDelta extends CoverageColumnType { - /** * Creates a column type to be used for representing the change coverage delta. */ @@ -25,9 +25,9 @@ public ChangeCoverageDelta() { } @Override - public Optional getCoverage(final CoverageBuildAction action, final CoverageMetric metric) { + public Optional getCoverage(final CoverageBuildAction action, final Metric metric) { if (action.hasChangeCoverageDifference(metric)) { - return Optional.of(action.getChangeCoverageDifference(metric)); + return Optional.of(CoveragePercentage.valueOf(action.getChangeCoverageDifference(metric))); } return Optional.empty(); } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumn.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumn.java index 47055e37d..5e1d04181 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumn.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumn.java @@ -3,6 +3,7 @@ import java.util.Optional; import java.util.regex.Pattern; +import edu.hm.hafner.metric.Metric; import edu.umd.cs.findbugs.annotations.NonNull; import org.kohsuke.stapler.DataBoundConstructor; @@ -18,7 +19,6 @@ import jenkins.model.Jenkins; import io.jenkins.plugins.coverage.model.CoverageBuildAction; -import io.jenkins.plugins.coverage.model.CoverageMetric; import io.jenkins.plugins.coverage.model.CoveragePercentage; import io.jenkins.plugins.coverage.model.Messages; import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider; @@ -34,7 +34,7 @@ public class CoverageColumn extends ListViewColumn { private CoverageColumnType selectedCoverageColumnType = new ProjectCoverage(); private String columnName = Messages.Coverage_Column(); - private String coverageMetric = CoverageMetric.LINE.getName(); + private String coverageMetric = Metric.LINE.name(); private String coverageType = selectedCoverageColumnType.getDisplayName(); /** @@ -133,7 +133,7 @@ public String getCoverageText(final Job job) { public Optional getCoverageValue(final Job job) { if (hasCoverageAction(job)) { CoverageBuildAction action = job.getLastCompletedBuild().getAction(CoverageBuildAction.class); - return selectedCoverageColumnType.getCoverage(action, CoverageMetric.valueOf(coverageMetric)); + return selectedCoverageColumnType.getCoverage(action, Metric.valueOf(coverageMetric)); } return Optional.empty(); } @@ -243,8 +243,8 @@ public ListBoxModel doFillCoverageTypeItems() { public ListBoxModel doFillCoverageMetricItems() { ListBoxModel model = new ListBoxModel(); if (new JenkinsFacade().hasPermission(Jenkins.READ)) { - for (CoverageMetric coverageMetric : CoverageMetric.getAvailableCoverageMetrics()) { - model.add(coverageMetric.getName()); + for (Metric coverageMetric : Metric.values()) { + model.add(coverageMetric.name()); } } return model; diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumnType.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumnType.java index ffc4f1095..4f8adccc6 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumnType.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumnType.java @@ -5,10 +5,11 @@ import java.util.Locale; import java.util.Optional; +import edu.hm.hafner.metric.Metric; + import org.jvnet.localizer.Localizable; import io.jenkins.plugins.coverage.model.CoverageBuildAction; -import io.jenkins.plugins.coverage.model.CoverageMetric; import io.jenkins.plugins.coverage.model.CoveragePercentage; import io.jenkins.plugins.coverage.model.Messages; import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider; @@ -52,11 +53,11 @@ protected ColorProvider getColorProvider() { * @param action * The {@link CoverageBuildAction action} which contains the coverage * @param metric - * The {@link CoverageMetric coverage metric} + * The {@link Metric coverage metric} * * @return the coverage as optional or an empty optional if no coverage has been found */ - public abstract Optional getCoverage(CoverageBuildAction action, CoverageMetric metric); + public abstract Optional getCoverage(CoverageBuildAction action, Metric metric); /** * Gets the {@link DisplayColors display colors} which are used for visualizing the passed coverage. diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/IndirectCoverageChanges.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/IndirectCoverageChanges.java index 5356ddda0..6a0bcfe23 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/IndirectCoverageChanges.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/IndirectCoverageChanges.java @@ -3,8 +3,11 @@ import java.util.Locale; import java.util.Optional; +import edu.hm.hafner.metric.Coverage; +import edu.hm.hafner.metric.Metric; +import edu.hm.hafner.metric.Value; + import io.jenkins.plugins.coverage.model.CoverageBuildAction; -import io.jenkins.plugins.coverage.model.CoverageMetric; import io.jenkins.plugins.coverage.model.CoveragePercentage; import io.jenkins.plugins.coverage.model.Messages; import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors; @@ -16,7 +19,6 @@ * @author Florian Orendi */ public class IndirectCoverageChanges extends CoverageColumnType { - /** * Creates a column type to be used for representing the indirect coverage changes. */ @@ -25,9 +27,12 @@ public IndirectCoverageChanges() { } @Override - public Optional getCoverage(final CoverageBuildAction action, final CoverageMetric metric) { + public Optional getCoverage(final CoverageBuildAction action, final Metric metric) { if (action.hasIndirectCoverageChanges(metric)) { - return Optional.of(action.getIndirectCoverageChanges(metric).getCoveredPercentage()); + Value changeCoverage = action.getIndirectCoverageChanges(metric); + if (changeCoverage instanceof Coverage) { + return Optional.of(CoveragePercentage.valueOf(((Coverage) changeCoverage).getCoveredPercentage())); + } } return Optional.empty(); } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ProjectCoverage.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ProjectCoverage.java index d58f5d74b..953b30190 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ProjectCoverage.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ProjectCoverage.java @@ -3,8 +3,11 @@ import java.util.Locale; import java.util.Optional; +import edu.hm.hafner.metric.Coverage; +import edu.hm.hafner.metric.Metric; +import edu.hm.hafner.metric.Value; + import io.jenkins.plugins.coverage.model.CoverageBuildAction; -import io.jenkins.plugins.coverage.model.CoverageMetric; import io.jenkins.plugins.coverage.model.CoveragePercentage; import io.jenkins.plugins.coverage.model.Messages; import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors; @@ -16,7 +19,6 @@ * @author Florian Orendi */ public class ProjectCoverage extends CoverageColumnType { - /** * Creates a column type to be used for representing the project coverage. */ @@ -25,9 +27,12 @@ public ProjectCoverage() { } @Override - public Optional getCoverage(final CoverageBuildAction action, final CoverageMetric metric) { + public Optional getCoverage(final CoverageBuildAction action, final Metric metric) { if (action.hasCoverage(metric)) { - return Optional.of(action.getCoverage(metric).getCoveredPercentage()); + Value coverage = action.getCoverage(metric); + if (coverage instanceof Coverage) { + return Optional.of(CoveragePercentage.valueOf(((Coverage)coverage).getCoveredPercentage())); + } } return Optional.empty(); } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ProjectCoverageDelta.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ProjectCoverageDelta.java index 18fdd5ed5..4667fcafc 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ProjectCoverageDelta.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ProjectCoverageDelta.java @@ -3,8 +3,9 @@ import java.util.Locale; import java.util.Optional; +import edu.hm.hafner.metric.Metric; + import io.jenkins.plugins.coverage.model.CoverageBuildAction; -import io.jenkins.plugins.coverage.model.CoverageMetric; import io.jenkins.plugins.coverage.model.CoveragePercentage; import io.jenkins.plugins.coverage.model.Messages; import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors; @@ -25,9 +26,9 @@ public ProjectCoverageDelta() { } @Override - public Optional getCoverage(final CoverageBuildAction action, final CoverageMetric metric) { + public Optional getCoverage(final CoverageBuildAction action, final Metric metric) { if (action.hasDelta(metric)) { - return Optional.of(action.getDifference().get(metric)); + return Optional.of(CoveragePercentage.valueOf(action.getDelta().get(metric))); } return Optional.empty(); } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/tree/TreeMapNodeConverter.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/tree/TreeMapNodeConverter.java index d265dfe03..b9ca5fb7e 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/tree/TreeMapNodeConverter.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/tree/TreeMapNodeConverter.java @@ -3,25 +3,24 @@ import edu.hm.hafner.echarts.ItemStyle; import edu.hm.hafner.echarts.Label; import edu.hm.hafner.echarts.TreeMapNode; +import edu.hm.hafner.metric.Coverage; +import edu.hm.hafner.metric.FileNode; +import edu.hm.hafner.metric.Metric; +import edu.hm.hafner.metric.Node; -import io.jenkins.plugins.coverage.model.Coverage; -import io.jenkins.plugins.coverage.model.CoverageMetric; -import io.jenkins.plugins.coverage.model.CoverageNode; -import io.jenkins.plugins.coverage.model.FileCoverageNode; import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider; import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors; import io.jenkins.plugins.coverage.model.visualization.colorization.CoverageLevel; /** - * Converts a tree of {@link CoverageNode coverage nodes} to a corresponding tree of + * Converts a tree of {@link Node coverage nodes} to a corresponding tree of * {@link TreeMapNode ECharts tree map nodes}. * * @author Ullrich Hafner */ public class TreeMapNodeConverter { - /** - * Converts a coverage tree of {@link CoverageNode} to a ECharts tree map of {@link TreeMapNode}. + * Converts a coverage tree of {@link Node nodes} to an ECharts tree map of {@link TreeMapNode}. * * @param node * The root node of the tree to be converted @@ -32,8 +31,7 @@ public class TreeMapNodeConverter { * * @return the converted tree map representation */ - public TreeMapNode toTeeChartModel(final CoverageNode node, final CoverageMetric metric, - final ColorProvider colorProvider) { + public TreeMapNode toTeeChartModel(final Node node, final Metric metric, final ColorProvider colorProvider) { TreeMapNode root = toTreeMapNode(node, metric, colorProvider); for (TreeMapNode child : root.getChildren()) { child.collapseEmptyPackages(); @@ -42,11 +40,11 @@ public TreeMapNode toTeeChartModel(final CoverageNode node, final CoverageMetric return root; } - private TreeMapNode toTreeMapNode(final CoverageNode node, final CoverageMetric metric, + private TreeMapNode toTreeMapNode(final Node node, final Metric metric, final ColorProvider colorProvider) { - Coverage coverage = node.getCoverage(metric); + Coverage coverage = (Coverage)node.getValue(metric).orElse(Coverage.nullObject(metric)); - double coveragePercentage = coverage.getCoveredPercentage().getDoubleValue(); + double coveragePercentage = coverage.getCoveredPercentage().doubleValue() * 100.0; DisplayColors colors = CoverageLevel.getDisplayColorsOfCoverageLevel(coveragePercentage, colorProvider); String lineColor = colors.getLineColorAsRGBHex(); @@ -55,10 +53,9 @@ private TreeMapNode toTreeMapNode(final CoverageNode node, final CoverageMetric Label label = new Label(true, lineColor); Label upperLabel = new Label(true, lineColor); - if (node instanceof FileCoverageNode) { + if (node instanceof FileNode) { ItemStyle style = new ItemStyle(fillColor); - return new TreeMapNode(node.getName(), style, label, upperLabel, coverage.getTotal(), - coverage.getCovered()); + return new TreeMapNode(node.getName(), style, label, upperLabel, coverage.getTotal(), coverage.getCovered()); } ItemStyle packageStyle = new ItemStyle(fillColor, fillColor, 4); diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CoverageRecorderTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CoverageRecorderTest.java index 447df7129..c4d836a8f 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CoverageRecorderTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CoverageRecorderTest.java @@ -69,7 +69,7 @@ void shouldParseFileWithJaCoCo() { "[JaCoCo] INSTRUCTION: 93.33% (1260/1350)", "[JaCoCo] LINE: 91.02% (294/323)", "[JaCoCo] BRANCH: 93.97% (109/116)", - "[JaCoCo] COMPLEXITY: [COMPLEXITY]: 160") + "[JaCoCo] COMPLEXITY: 160") .containsPattern("Searching for all files in '.*' that match the pattern '\\*\\*/jacoco.xml'") .containsPattern("Successfully parsed file '.*/jacoco.xml'") .doesNotContain("Expanding pattern"); diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageTest.java index ebdb73f3f..2de0cb227 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageTest.java @@ -1,15 +1,15 @@ package io.jenkins.plugins.coverage.model; +import java.io.IOException; +import java.nio.file.Files; + import org.junitpioneer.jupiter.DefaultLocale; +import edu.hm.hafner.metric.Metric; +import edu.hm.hafner.metric.Node; +import edu.hm.hafner.metric.parser.JacocoParser; import edu.hm.hafner.util.ResourceTest; - -import io.jenkins.plugins.coverage.adapter.CoberturaReportAdapter; -import io.jenkins.plugins.coverage.adapter.JacocoReportAdapter; -import io.jenkins.plugins.coverage.adapter.JacocoReportAdapter.JacocoReportAdapterDescriptor; -import io.jenkins.plugins.coverage.exception.CoverageException; -import io.jenkins.plugins.coverage.targets.CoverageElementRegister; -import io.jenkins.plugins.coverage.targets.CoverageResult; +import edu.hm.hafner.util.SecureXmlParserFactory.ParsingException; /** * Base class for coverage tests that work on real coverage reports. @@ -18,74 +18,29 @@ */ @DefaultLocale("en") public abstract class AbstractCoverageTest extends ResourceTest { - static final CoverageMetric MODULE = CoverageMetric.MODULE; - static final CoverageMetric PACKAGE = CoverageMetric.PACKAGE; - static final CoverageMetric FILE = CoverageMetric.FILE; - static final CoverageMetric CLASS = CoverageMetric.CLASS; - static final CoverageMetric METHOD = CoverageMetric.METHOD; - static final CoverageMetric LINE = CoverageMetric.LINE; - static final CoverageMetric INSTRUCTION = CoverageMetric.INSTRUCTION; - static final CoverageMetric BRANCH = CoverageMetric.BRANCH; - - /** - * Reads the {@link CoverageResult} from a coverage report. - * - * @param fileName - * The name of the coverage report file - * - * @return the parsed coverage results - */ - public CoverageResult readResult(final String fileName) { - try { - JacocoReportAdapter parser = new JacocoReportAdapter("unused"); - CoverageElementRegister.addCoverageElements(new JacocoReportAdapterDescriptor().getCoverageElements()); - CoverageResult result = parser.getResult(getResourceAsFile(fileName).toFile()); - result.stripGroup(); - return result; - } - catch (CoverageException exception) { - throw new AssertionError(exception); - } - } + static final Metric MODULE = Metric.MODULE; + static final Metric PACKAGE = Metric.PACKAGE; + static final Metric FILE = Metric.FILE; + static final Metric CLASS = Metric.CLASS; + static final Metric METHOD = Metric.METHOD; + static final Metric LINE = Metric.LINE; + static final Metric INSTRUCTION = Metric.INSTRUCTION; + static final Metric BRANCH = Metric.BRANCH; /** - * Reads the {@link CoverageResult} from a coverage report. + * Reads and parses a JaCoCo coverage report. * * @param fileName - * The name of the coverage report file + * the name of the coverage report file * - * @return the parsed coverage results + * @return the parsed coverage tree */ - public CoverageResult readCoberturaResult(final String fileName) { + public Node readJacocoResult(final String fileName) { try { - CoberturaReportAdapter parser = new CoberturaReportAdapter("unused"); - CoverageElementRegister.addCoverageElements(new CoberturaReportAdapter.CoberturaReportAdapterDescriptor().getCoverageElements()); - CoverageResult result = parser.getResult(getResourceAsFile(fileName).toFile()); - result.stripGroup(); - return result; + return new JacocoParser().parse(Files.newBufferedReader(getResourceAsFile(fileName))); } - catch (CoverageException exception) { + catch (ParsingException | IOException exception) { throw new AssertionError(exception); } } - - /** - * Reads the {@link CoverageNode} from a coverage report. - * - * @param fileName - * The name of the coverage report file - * - * @return the parsed coverage tree - */ - public CoverageNode readNode(final String fileName) { - return new CoverageNodeConverter().convert(readResult(fileName)); - } - - protected CoverageResult readReport(final String fileName) throws CoverageException { - JacocoReportAdapter parser = new JacocoReportAdapter("Hello"); - CoverageElementRegister.addCoverageElements(new JacocoReportAdapterDescriptor().getCoverageElements()); - CoverageResult result = parser.getResult(getResourceAsFile(fileName).toFile()); - result.stripGroup(); - return result; - } } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CodeDeltaCalculatorTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CodeDeltaCalculatorTest.java index 27d889862..71349474e 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CodeDeltaCalculatorTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CodeDeltaCalculatorTest.java @@ -2,9 +2,9 @@ import java.nio.file.Paths; import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Function; @@ -12,6 +12,8 @@ import org.junit.jupiter.api.Test; +import edu.hm.hafner.metric.FileNode; +import edu.hm.hafner.metric.Node; import edu.hm.hafner.util.FilteredLog; import hudson.FilePath; @@ -33,7 +35,6 @@ * @author Florian Orendi */ class CodeDeltaCalculatorTest { - private static final String LOG_NAME = "Errors while calculating changes mapping:"; private static final String EMPTY_PATH = ""; @@ -85,7 +86,7 @@ void shouldMapScmChangesToReportPaths() throws CodeDeltaException { Set changes = codeDeltaCalculator.getCoverageRelevantChanges(delta); Map changesMap = changes.stream() .collect(Collectors.toMap(FileChanges::getFileName, Function.identity())); - CoverageNode tree = createMockedCoverageTree(); + Node tree = createMockedCoverageTree(); FilteredLog log = createFilteredLog(); Map should = new HashMap<>(); @@ -101,7 +102,7 @@ void shouldMapScmChangesToReportPaths() throws CodeDeltaException { @Test void shouldCreateEmptyMappingWithoutChanges() throws CodeDeltaException { CodeDeltaCalculator codeDeltaCalculator = createCodeDeltaCalculator(); - CoverageNode tree = createMockedCoverageTree(); + Node tree = createMockedCoverageTree(); FilteredLog log = createFilteredLog(); Set noChanges = new HashSet<>(); @@ -109,17 +110,18 @@ void shouldCreateEmptyMappingWithoutChanges() throws CodeDeltaException { } @Test - void shouldNotMapScmChangesWithAmbiguousPaths() { + void shouldNotMapScmChangesWithAmbiguousPaths() throws CodeDeltaException { CodeDeltaCalculator codeDeltaCalculator = createCodeDeltaCalculator(); FilteredLog log = createFilteredLog(); String path = "example"; Set changes = createAmbiguousFileChanges(path); - CoverageNode tree = mock(CoverageNode.class); - FileCoverageNode file1 = mock(FileCoverageNode.class); + Node tree = mock(Node.class); + FileNode file1 = mock(FileNode.class); when(file1.getPath()).thenReturn(path); - when(tree.getAllFileCoverageNodes()).thenReturn(Collections.singletonList(file1)); + when(tree.getAllFileNodes()).thenReturn(List.of(file1)); + when(tree.getFiles()).thenReturn(List.of(path)); assertThatThrownBy(() -> codeDeltaCalculator.mapScmChangesToReportPaths(changes, tree, log)) .isInstanceOf(CodeDeltaException.class) @@ -130,8 +132,8 @@ void shouldNotMapScmChangesWithAmbiguousPaths() { void shouldCreateOldPathMapping() throws CodeDeltaException { CodeDeltaCalculator codeDeltaCalculator = createCodeDeltaCalculator(); FilteredLog log = createFilteredLog(); - CoverageNode tree = createMockedCoverageTree(); - CoverageNode referenceTree = createMockedReferenceCoverageTree(); + Node tree = createMockedCoverageTree(); + Node referenceTree = createMockedReferenceCoverageTree(); Map changes = new HashMap<>(); changes.put(REPORT_PATH_MODIFY, createFileChanges(SCM_PATH_MODIFY, SCM_PATH_MODIFY, FileEditType.MODIFY)); changes.put(REPORT_PATH_RENAME, createFileChanges(SCM_PATH_RENAME, OLD_SCM_PATH_RENAME, FileEditType.RENAME)); @@ -149,8 +151,8 @@ void shouldNotCreateOldPathMappingWithMissingReferenceNodes() throws CodeDeltaEx CodeDeltaCalculator codeDeltaCalculator = createCodeDeltaCalculator(); FilteredLog log = createFilteredLog(); - CoverageNode tree = new CoverageNode(CoverageMetric.FILE, REPORT_PATH_RENAME); - CoverageNode referenceTree = new CoverageNode(CoverageMetric.FILE, REPORT_PATH_MODIFY); + Node tree = new FileNode(REPORT_PATH_RENAME); + Node referenceTree = new FileNode(REPORT_PATH_MODIFY); Map changes = new HashMap<>(); changes.put(REPORT_PATH_RENAME, createFileChanges(SCM_PATH_RENAME, OLD_SCM_PATH_RENAME, FileEditType.RENAME)); @@ -165,8 +167,8 @@ void shouldNotCreateOldPathMappingWithMissingReferenceNodes() throws CodeDeltaEx void shouldNotCreateOldPathMappingWithCodeDeltaMismatches() { CodeDeltaCalculator codeDeltaCalculator = createCodeDeltaCalculator(); FilteredLog log = createFilteredLog(); - CoverageNode tree = createMockedCoverageTree(); - CoverageNode referenceTree = createMockedReferenceCoverageTree(); + Node tree = createMockedCoverageTree(); + Node referenceTree = createMockedReferenceCoverageTree(); // two changes with the same former path Map changes = new HashMap<>(); @@ -257,19 +259,22 @@ private FileChanges createFileChanges(final String filePath, final String oldFil * Mocks a coverage tree which contains file nodes which represent {@link #REPORT_PATH_ADD_1}, {@link * #REPORT_PATH_ADD_2} and {@link #REPORT_PATH_MODIFY}. * - * @return the {@link CoverageNode root} of the tree + * @return the {@link Node root} of the tree */ - private CoverageNode createMockedCoverageTree() { - FileCoverageNode addFile1 = mock(FileCoverageNode.class); + private Node createMockedCoverageTree() { + FileNode addFile1 = mock(FileNode.class); when(addFile1.getPath()).thenReturn(REPORT_PATH_ADD_1); - FileCoverageNode addFile2 = mock(FileCoverageNode.class); + FileNode addFile2 = mock(FileNode.class); when(addFile2.getPath()).thenReturn(REPORT_PATH_ADD_2); - FileCoverageNode modifyFile = mock(FileCoverageNode.class); + FileNode modifyFile = mock(FileNode.class); when(modifyFile.getPath()).thenReturn(REPORT_PATH_MODIFY); - FileCoverageNode renameFile = mock(FileCoverageNode.class); + FileNode renameFile = mock(FileNode.class); when(renameFile.getPath()).thenReturn(REPORT_PATH_RENAME); - CoverageNode root = mock(CoverageNode.class); - when(root.getAllFileCoverageNodes()).thenReturn(Arrays.asList(addFile1, addFile2, modifyFile, renameFile)); + Node root = mock(Node.class); + when(root.getAllFileNodes()).thenReturn(Arrays.asList(addFile1, addFile2, modifyFile, renameFile)); + var files = root.getAllFileNodes().stream().map(FileNode::getPath).collect(Collectors.toList()); + when(root.getFiles()).thenReturn(files); + return root; } @@ -277,15 +282,18 @@ private CoverageNode createMockedCoverageTree() { * Mocks a reference coverage tree which contains file nodes which represent {@link #OLD_REPORT_PATH_RENAME} and * {@link #REPORT_PATH_MODIFY}. * - * @return the {@link CoverageNode root} of the tree + * @return the {@link Node root} of the tree */ - private CoverageNode createMockedReferenceCoverageTree() { - FileCoverageNode modifyFile = mock(FileCoverageNode.class); + private Node createMockedReferenceCoverageTree() { + FileNode modifyFile = mock(FileNode.class); when(modifyFile.getPath()).thenReturn(REPORT_PATH_MODIFY); - FileCoverageNode renameFile = mock(FileCoverageNode.class); + FileNode renameFile = mock(FileNode.class); when(renameFile.getPath()).thenReturn(OLD_REPORT_PATH_RENAME); - CoverageNode root = mock(CoverageNode.class); - when(root.getAllFileCoverageNodes()).thenReturn(Arrays.asList(renameFile, modifyFile)); + Node root = mock(Node.class); + when(root.getAllFileNodes()).thenReturn(Arrays.asList(renameFile, modifyFile)); + var files = root.getAllFileNodes().stream().map(FileNode::getPath).collect(Collectors.toList()); + when(root.getFiles()).thenReturn(files); + return root; } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageBuildActionTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageBuildActionTest.java index 07c9b09bd..80d39ef71 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageBuildActionTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageBuildActionTest.java @@ -1,19 +1,24 @@ package io.jenkins.plugins.coverage.model; import java.util.Locale; -import java.util.SortedMap; +import java.util.NavigableMap; import java.util.TreeMap; import org.apache.commons.lang3.math.Fraction; import org.junit.jupiter.api.Test; import org.junitpioneer.jupiter.DefaultLocale; +import edu.hm.hafner.metric.Coverage; +import edu.hm.hafner.metric.Coverage.CoverageBuilder; +import edu.hm.hafner.metric.Metric; +import edu.hm.hafner.metric.ModuleNode; +import edu.hm.hafner.metric.Node; +import edu.hm.hafner.metric.Value; + import hudson.model.FreeStyleBuild; import hudson.model.HealthReport; import hudson.model.Run; -import io.jenkins.plugins.coverage.model.Coverage.CoverageBuilder; - import static io.jenkins.plugins.coverage.model.testutil.CoverageStubs.*; import static io.jenkins.plugins.coverage.model.testutil.JobStubs.*; import static org.assertj.core.api.Assertions.*; @@ -30,51 +35,49 @@ class CoverageBuildActionTest { private static final Fraction COVERAGE_FRACTION = Fraction.ONE_HALF; private static final CoveragePercentage COVERAGE_PERCENTAGE = CoveragePercentage.valueOf(COVERAGE_FRACTION); - private static final CoverageMetric COVERAGE_METRIC = CoverageMetric.LINE; + private static final Metric COVERAGE_METRIC = Metric.LINE; + private static final Coverage VALUE = Coverage.valueOf(COVERAGE_METRIC, "1/2"); private static final int COVERAGE_FILE_CHANGES = 5; private static final long COVERAGE_LINE_CHANGES = 10; @Test void shouldNotLoadResultIfCoverageValuesArePersistedInAction() { - CoverageNode module = new CoverageNode(CoverageMetric.MODULE, "module"); + Node module = new ModuleNode("module"); CoverageBuilder coverageBuilder = new CoverageBuilder(); + Coverage percent50 = coverageBuilder.setMetric(Metric.BRANCH).setCovered(1).setMissed(1).build(); + Coverage percent80 = coverageBuilder.setMetric(Metric.LINE).setCovered(8).setMissed(2).build(); - Coverage percent50 = coverageBuilder.setCovered(1).setMissed(1).build(); - module.add(new CoverageLeaf(CoverageMetric.BRANCH, percent50)); - - Coverage percent80 = coverageBuilder.setCovered(8).setMissed(2).build(); - module.add(new CoverageLeaf(CoverageMetric.LINE, percent80)); + module.addValue(percent50); + module.addValue(percent80); CoverageBuildAction action = spy(createEmptyAction(module)); when(action.getResult()).thenThrow(new IllegalStateException("Result should not be accessed with getResult() when getting a coverage metric that is persisted in the build")); assertThat(action.getReferenceBuild()).isEmpty(); - assertThat(action.hasCoverage(CoverageMetric.LINE)).isTrue(); - assertThat(action.getCoverage(CoverageMetric.LINE)).isEqualTo(percent80); + assertThat(action.hasCoverage(Metric.LINE)).isTrue(); + assertThat(action.getCoverage(Metric.LINE)).isEqualTo(percent80); assertThat(action.getLineCoverage()).isEqualTo(percent80); - assertThat(action.hasCoverage(CoverageMetric.BRANCH)).isTrue(); - assertThat(action.getCoverage(CoverageMetric.BRANCH)).isEqualTo(percent50); + assertThat(action.hasCoverage(Metric.BRANCH)).isTrue(); + assertThat(action.getCoverage(Metric.BRANCH)).isEqualTo(percent50); assertThat(action.getBranchCoverage()).isEqualTo(percent50); - assertThatIllegalStateException().isThrownBy( - () -> action.hasCoverage(CoverageMetric.INSTRUCTION)); - assertThatIllegalStateException().isThrownBy( - () -> action.getCoverage(CoverageMetric.INSTRUCTION)); + assertThat(action.hasCoverage(Metric.INSTRUCTION)).isFalse(); + assertThat(action.getCoverage(Metric.INSTRUCTION)).isNull(); - assertThat(action.formatChangeCoverage(CoverageMetric.BRANCH)).isEqualTo("Branch: n/a"); + assertThat(action.formatChangeCoverage(Metric.BRANCH)).isEqualTo("BRANCH: n/a"); assertThat(action.formatChangeCoverageOverview()).isEqualTo("n/a"); - assertThat(action.formatIndirectCoverageChanges(CoverageMetric.BRANCH)).isEqualTo("Branch: n/a"); + assertThat(action.formatIndirectCoverageChanges(Metric.BRANCH)).isEqualTo("BRANCH: n/a"); assertThat(action.formatIndirectCoverageChangesOverview()).isEqualTo("n/a"); - assertThat(action.formatChangeCoverageDifference(CoverageMetric.BRANCH)).isEqualTo("n/a"); - assertThat(action.formatDelta(CoverageMetric.BRANCH)).isEqualTo("n/a"); + assertThat(action.formatChangeCoverageDifference(Metric.BRANCH)).isEqualTo("n/a"); + assertThat(action.formatDelta(Metric.BRANCH)).isEqualTo("n/a"); } - private static CoverageBuildAction createEmptyAction(final CoverageNode module) { + private static CoverageBuildAction createEmptyAction(final Node module) { return new CoverageBuildAction(mock(FreeStyleBuild.class), module, new HealthReport(), "-", new TreeMap<>(), new TreeMap<>(), new TreeMap<>(), new TreeMap<>(), false); @@ -82,69 +85,61 @@ private static CoverageBuildAction createEmptyAction(final CoverageNode module) @Test void shouldNotLoadResultIfDeltasArePersistedInAction() { - SortedMap deltas = new TreeMap<>(); + NavigableMap coverages = new TreeMap<>(); + NavigableMap deltas = new TreeMap<>(); CoverageBuilder coverageBuilder = new CoverageBuilder(); - CoveragePercentage percent50 = CoveragePercentage.valueOf(coverageBuilder.setCovered(1).setMissed(1).build() - .getCoveredFraction()); - CoveragePercentage percent80 = CoveragePercentage.valueOf(coverageBuilder.setCovered(8).setMissed(2).build() - .getCoveredFraction()); - deltas.put(CoverageMetric.BRANCH, percent50); + Coverage percent50 = coverageBuilder.setMetric(Metric.BRANCH).setCovered(1).setMissed(1).build(); + coverages.put(Metric.BRANCH, percent50); + deltas.put(Metric.BRANCH, percent50.getCoveredPercentage()); + + Coverage percent80 = coverageBuilder.setMetric(Metric.LINE).setCovered(8).setMissed(2).build(); + coverages.put(Metric.LINE, percent80); CoverageBuildAction action = new CoverageBuildAction(mock(FreeStyleBuild.class), - new CoverageNode(CoverageMetric.MODULE, "module"), + new ModuleNode("module"), mock(HealthReport.class), "-", - deltas, deltas, - deltas, deltas, false); + deltas, coverages, + deltas, coverages, false); CoverageBuildAction spy = spy(action); when(spy.getResult()).thenThrow(new IllegalArgumentException("Result should not be accessed with getResult() when getting a coverage metric that is persisted in the build")); assertThat(spy.hasChangeCoverage()).isTrue(); - assertThat(spy.hasChangeCoverage(CoverageMetric.LINE)).isFalse(); - assertThat(spy.hasChangeCoverage(CoverageMetric.BRANCH)).isTrue(); + assertThat(spy.hasChangeCoverage(Metric.LINE)).isTrue(); + assertThat(spy.getChangeCoverage(Metric.LINE)).isEqualTo(percent80); + assertThat(spy.hasChangeCoverage(Metric.BRANCH)).isTrue(); + assertThat(spy.getChangeCoverage(Metric.BRANCH)).isEqualTo(percent50); assertThat(spy.hasIndirectCoverageChanges()).isTrue(); - assertThat(spy.hasIndirectCoverageChanges(CoverageMetric.LINE)).isFalse(); - assertThat(spy.hasIndirectCoverageChanges(CoverageMetric.BRANCH)).isTrue(); - - assertThat(spy.hasChangeCoverageDifference(CoverageMetric.LINE)).isFalse(); - assertThat(spy.hasChangeCoverageDifference(CoverageMetric.BRANCH)).isTrue(); + assertThat(spy.hasIndirectCoverageChanges(Metric.LINE)).isTrue(); + assertThat(spy.getIndirectCoverageChanges(Metric.LINE)).isEqualTo(percent80); + assertThat(spy.hasIndirectCoverageChanges(Metric.BRANCH)).isTrue(); + assertThat(spy.getIndirectCoverageChanges(Metric.BRANCH)).isEqualTo(percent50); - assertThat(spy.hasDelta(CoverageMetric.LINE)).isFalse(); - assertThat(spy.hasDelta(CoverageMetric.BRANCH)).isTrue(); - assertThat(spy.getDifference()).contains(entry(CoverageMetric.BRANCH, percent50)); + assertThat(spy.hasChangeCoverageDifference(Metric.LINE)).isFalse(); + assertThat(spy.hasChangeCoverageDifference(Metric.BRANCH)).isTrue(); + assertThat(spy.getChangeCoverageDifference(Metric.BRANCH)).isEqualTo(percent50.getCoveredPercentage()); - deltas.put(CoverageMetric.LINE, percent80); + assertThat(spy.hasDelta(Metric.LINE)).isFalse(); + assertThat(spy.hasDelta(Metric.BRANCH)).isTrue(); + assertThat(spy.getDelta()).contains(entry(Metric.BRANCH, percent50.getCoveredPercentage())); assertThat(spy.hasChangeCoverage()).isTrue(); - assertThat(spy.hasChangeCoverage(CoverageMetric.LINE)).isTrue(); - assertThat(spy.hasChangeCoverage(CoverageMetric.BRANCH)).isTrue(); - // FIXME: those values are not persisted yet -// assertThat(spy.getChangeCoverage(CoverageMetric.LINE)).isEqualTo(percent80); + assertThat(spy.hasChangeCoverage(Metric.LINE)).isTrue(); + assertThat(spy.hasChangeCoverage(Metric.BRANCH)).isTrue(); assertThat(spy.hasIndirectCoverageChanges()).isTrue(); - assertThat(spy.hasIndirectCoverageChanges(CoverageMetric.LINE)).isTrue(); - assertThat(spy.hasIndirectCoverageChanges(CoverageMetric.BRANCH)).isTrue(); - // FIXME: those values are not persisted yet -// assertThat(spy.getIndirectCoverageChanges(CoverageMetric.LINE)).isEqualTo(percent80); - - assertThat(spy.hasChangeCoverageDifference(CoverageMetric.LINE)).isTrue(); - assertThat(spy.hasChangeCoverageDifference(CoverageMetric.BRANCH)).isTrue(); - // FIXME: those values are not persisted yet -// assertThat(spy.getIndirectCoverageChanges(CoverageMetric.LINE)).isEqualTo(percent80); - - assertThat(spy.hasDelta(CoverageMetric.LINE)).isTrue(); - assertThat(spy.hasDelta(CoverageMetric.BRANCH)).isTrue(); - assertThat(spy.getDifference()).contains(entry(CoverageMetric.LINE, percent80)); - assertThat(spy.getDifference()).contains(entry(CoverageMetric.BRANCH, percent50)); + assertThat(spy.hasIndirectCoverageChanges(Metric.LINE)).isTrue(); + assertThat(spy.getIndirectCoverageChanges(Metric.LINE)).isEqualTo(percent80); + assertThat(spy.hasIndirectCoverageChanges(Metric.BRANCH)).isTrue(); } @Test void shouldCreateViewModel() { - CoverageNode root = new CoverageNode(COVERAGE_METRIC, "top-level"); + Node root = new ModuleNode("top-level"); CoverageBuildAction action = createEmptyAction(root); assertThat(action.getTarget()).extracting(CoverageViewModel::getNode).isSameAs(root); @@ -157,20 +152,19 @@ void shouldGetCoverageForSpecifiedMetric() { assertThat(action.hasCoverage(COVERAGE_METRIC)).isTrue(); assertThat(action.getCoverage(COVERAGE_METRIC)) - .extracting(Coverage::getCoveredPercentage) - .isEqualTo(COVERAGE_PERCENTAGE); - assertThat(action.formatCoverage(COVERAGE_METRIC)).isEqualTo("Line: 50.00%"); + .isEqualTo(VALUE); + assertThat(action.formatCoverage(COVERAGE_METRIC)).isEqualTo("LINE: 50.00%"); } @Test void shouldGetCoverageDifferenceForSpecifiedMetric() { CoverageBuildAction action = createCoverageBuildAction(); assertThat(action.hasDelta(COVERAGE_METRIC)).isTrue(); - assertThat(action.hasDelta(CoverageMetric.BRANCH)).isFalse(); - assertThat(action.getDifference()) + assertThat(action.hasDelta(Metric.BRANCH)).isFalse(); + assertThat(action.getDelta()) .hasSize(1) .containsKey(COVERAGE_METRIC) - .containsValue(COVERAGE_PERCENTAGE); + .containsValue(COVERAGE_FRACTION); assertThat(action.formatDelta(COVERAGE_METRIC)) .isEqualTo(COVERAGE_PERCENTAGE.formatDeltaPercentage(LOCALE)); } @@ -179,7 +173,7 @@ void shouldGetCoverageDifferenceForSpecifiedMetric() { void shouldGetChangeCoverageDifferences() { CoverageBuildAction action = createCoverageBuildAction(); assertThat(action.hasChangeCoverageDifference(COVERAGE_METRIC)).isTrue(); - assertThat(action.getChangeCoverageDifference(COVERAGE_METRIC)).isEqualTo(COVERAGE_PERCENTAGE); + assertThat(action.getChangeCoverageDifference(COVERAGE_METRIC)).isEqualTo(COVERAGE_FRACTION); } @Test @@ -188,9 +182,7 @@ void shouldGetChangeCoverageForSpecifiedMetric() { assertThat(action.hasChangeCoverage()).isTrue(); assertThat(action.hasCodeChanges()).isTrue(); assertThat(action.hasChangeCoverage(COVERAGE_METRIC)).isTrue(); - assertThat(action.getChangeCoverage(COVERAGE_METRIC)) - .extracting(Coverage::getCoveredPercentage) - .isEqualTo(COVERAGE_PERCENTAGE); + assertThat(action.getChangeCoverage(COVERAGE_METRIC)).isEqualTo(VALUE); } @Test @@ -198,9 +190,7 @@ void shouldGetIndirectCoverageChangesForSpecifiedMetric() { CoverageBuildAction action = createIndirectCoverageChangesBuildAction(); assertThat(action.hasIndirectCoverageChanges()).isTrue(); assertThat(action.hasIndirectCoverageChanges(COVERAGE_METRIC)).isTrue(); - assertThat(action.getIndirectCoverageChanges(COVERAGE_METRIC)) - .extracting(Coverage::getCoveredPercentage) - .isEqualTo(COVERAGE_PERCENTAGE); + assertThat(action.getIndirectCoverageChanges(COVERAGE_METRIC)).isEqualTo(VALUE); } @Test @@ -231,8 +221,8 @@ void shouldFormatChangeCoverageDifference() { * @return the created action */ private CoverageBuildAction createCoverageBuildAction() { - CoverageNode root = new CoverageNode(COVERAGE_METRIC, "Line"); - root.add(new CoverageLeaf(COVERAGE_METRIC, new CoverageBuilder().setCovered(1).setMissed(1).build())); + ModuleNode root = new ModuleNode("Line"); + root.addValue(new CoverageBuilder().setMetric(COVERAGE_METRIC).setCovered(1).setMissed(1).build()); return createCoverageBuildAction(root); } @@ -243,7 +233,7 @@ private CoverageBuildAction createCoverageBuildAction() { * @return the created action */ private CoverageBuildAction createChangeCoverageBuildAction() { - CoverageNode root = createChangeCoverageNode(COVERAGE_FRACTION, COVERAGE_METRIC, + Node root = createChangeCoverageNode(COVERAGE_FRACTION, COVERAGE_METRIC, COVERAGE_FILE_CHANGES, COVERAGE_LINE_CHANGES); return createCoverageBuildAction(root); } @@ -255,30 +245,30 @@ private CoverageBuildAction createChangeCoverageBuildAction() { * @return the created action */ private CoverageBuildAction createIndirectCoverageChangesBuildAction() { - CoverageNode root = createIndirectCoverageChangesNode(COVERAGE_FRACTION, COVERAGE_METRIC, + Node root = createIndirectCoverageChangesNode(COVERAGE_FRACTION, COVERAGE_METRIC, COVERAGE_FILE_CHANGES, COVERAGE_LINE_CHANGES); return createCoverageBuildAction(root); } /** - * Creates a {@link CoverageBuildAction} with the passed {@link CoverageNode result}. + * Creates a {@link CoverageBuildAction} with the passed {@link Node result}. * * @param root * The result of the action * * @return the created action */ - private CoverageBuildAction createCoverageBuildAction(final CoverageNode root) { + private CoverageBuildAction createCoverageBuildAction(final Node root) { Run build = createBuild(); - TreeMap deltas = new TreeMap<>(); - deltas.put(COVERAGE_METRIC, COVERAGE_PERCENTAGE); - TreeMap changeCoverage = new TreeMap<>(); - changeCoverage.put(COVERAGE_METRIC, COVERAGE_PERCENTAGE); - TreeMap changeCoverageDifference = new TreeMap<>(); - changeCoverageDifference.put(COVERAGE_METRIC, COVERAGE_PERCENTAGE); - TreeMap indirectCoverageChanges = new TreeMap<>(); - indirectCoverageChanges.put(COVERAGE_METRIC, COVERAGE_PERCENTAGE); + NavigableMap deltas = new TreeMap<>(); + deltas.put(COVERAGE_METRIC, COVERAGE_FRACTION); + NavigableMap changeCoverage = new TreeMap<>(); + changeCoverage.put(COVERAGE_METRIC, VALUE); + NavigableMap changeCoverageDifference = new TreeMap<>(); + changeCoverageDifference.put(COVERAGE_METRIC, COVERAGE_FRACTION); + NavigableMap indirectCoverageChanges = new TreeMap<>(); + indirectCoverageChanges.put(COVERAGE_METRIC, VALUE); return new CoverageBuildAction(build, root, new HealthReport(), "-", deltas, changeCoverage, changeCoverageDifference, indirectCoverageChanges, false); @@ -290,7 +280,7 @@ private CoverageBuildAction createCoverageBuildAction(final CoverageNode root) { * @return the formatted text */ private String getFormattedLineCoverage() { - return "Line: " + COVERAGE_PERCENTAGE.formatPercentage(LOCALE); + return "LINE: " + COVERAGE_PERCENTAGE.formatPercentage(LOCALE); } /** diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageJobActionTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageJobActionTest.java index d6963f3e0..3d22d4d6c 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageJobActionTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageJobActionTest.java @@ -9,6 +9,8 @@ import edu.hm.hafner.echarts.Build; import edu.hm.hafner.echarts.BuildResult; import edu.hm.hafner.echarts.LinesChartModel; +import edu.hm.hafner.metric.Coverage.CoverageBuilder; +import edu.hm.hafner.metric.Metric; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; @@ -43,7 +45,7 @@ void shouldIgnoreIndexIfNoActionFound() throws IOException { void shouldNavigateToLastAction() throws IOException { FreeStyleBuild build = mock(FreeStyleBuild.class); - CoverageBuildAction action = creataBuildAction(build); + CoverageBuildAction action = createBuildAction(build); when(build.getAction(CoverageBuildAction.class)).thenReturn(action); when(build.getNumber()).thenReturn(15); @@ -63,7 +65,7 @@ void shouldNavigateToLastAction() throws IOException { void shouldCreateTrendChartForLineAndBranchCoverage() throws IOException { FreeStyleBuild build = mock(FreeStyleBuild.class); - CoverageBuildAction action = creataBuildAction(build); + CoverageBuildAction action = createBuildAction(build); when(build.getAction(CoverageBuildAction.class)).thenReturn(action); int buildNumber = 15; @@ -93,12 +95,12 @@ void shouldCreateTrendChartForLineAndBranchCoverage() throws IOException { }); } - private CoverageBuildAction creataBuildAction(final FreeStyleBuild build) { + private CoverageBuildAction createBuildAction(final FreeStyleBuild build) { CoverageBuildAction action = mock(CoverageBuildAction.class); when(action.getOwner()).thenAnswer(i -> build); when(action.getUrlName()).thenReturn("coverage"); - when(action.getBranchCoverage()).thenReturn(new Coverage.CoverageBuilder().setCovered(9).setMissed(1).build()); - when(action.getLineCoverage()).thenReturn(new Coverage.CoverageBuilder().setCovered(10).setMissed(10).build()); + when(action.getBranchCoverage()).thenReturn(new CoverageBuilder().setMetric(Metric.BRANCH).setCovered(9).setMissed(1).build()); + when(action.getLineCoverage()).thenReturn(new CoverageBuilder().setMetric(Metric.LINE).setCovered(10).setMissed(10).build()); return action; } } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageLeafTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageLeafTest.java deleted file mode 100644 index 26529470e..000000000 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageLeafTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.jenkins.plugins.coverage.model; - -import org.junit.jupiter.api.Test; - -import nl.jqno.equalsverifier.EqualsVerifier; - -import io.jenkins.plugins.coverage.model.Coverage.CoverageBuilder; - -import static io.jenkins.plugins.coverage.model.Assertions.*; - -/** - * Tests the class {@link CoverageLeaf}. - * - * @author Ullrich Hafner - */ -class CoverageLeafTest extends AbstractCoverageTest { - private static final Coverage COVERED = new Coverage.CoverageBuilder().setCovered(1).setMissed(0).build(); - - @Test - void shouldCreateLeaf() { - CoverageLeaf coverageLeaf = new CoverageLeaf(LINE, COVERED); - - assertThat(coverageLeaf).hasMetric(LINE).hasToString("[Line]: 100.00% (1/1)"); - assertThat(coverageLeaf.getCoverage(LINE)).isEqualTo(COVERED); - assertThat(coverageLeaf.getCoverage(CoverageMetric.MODULE)).isEqualTo(CoverageBuilder.NO_COVERAGE); - } - - @Test - void shouldObeyEqualsContract() { - EqualsVerifier.forClass(CoverageLeaf.class).verify(); - } -} diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageMetricTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageMetricTest.java deleted file mode 100644 index 172edebf9..000000000 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageMetricTest.java +++ /dev/null @@ -1,73 +0,0 @@ -package io.jenkins.plugins.coverage.model; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.*; - -/** - * Tests the class {@link CoverageMetric}. - * - * @author Ullrich Hafner - */ -class CoverageMetricTest { - @Test - void shouldSortMetrics() { - List all = new ArrayList<>(); - all.add(CoverageMetric.MODULE); - all.add(CoverageMetric.PACKAGE); - all.add(CoverageMetric.FILE); - all.add(CoverageMetric.CLASS); - all.add(CoverageMetric.METHOD); - all.add(CoverageMetric.LINE); - all.add(CoverageMetric.BRANCH); - all.add(CoverageMetric.INSTRUCTION); - - Collections.sort(all); - verifyOrder(all); - - Collections.reverse(all); - assertThat(all).containsExactly( - CoverageMetric.BRANCH, - CoverageMetric.INSTRUCTION, - CoverageMetric.LINE, - CoverageMetric.METHOD, - CoverageMetric.CLASS, - CoverageMetric.FILE, - CoverageMetric.PACKAGE, - CoverageMetric.MODULE); - - Collections.sort(all); - verifyOrder(all); - } - - @Test - void shouldGetAvailableMetrics() { - assertThat(CoverageMetric.getAvailableCoverageMetrics()).containsExactlyInAnyOrder( - CoverageMetric.BRANCH, - CoverageMetric.INSTRUCTION, - CoverageMetric.LINE, - CoverageMetric.METHOD, - CoverageMetric.CLASS, - CoverageMetric.FILE, - CoverageMetric.PACKAGE, - CoverageMetric.MODULE - ); - } - - private void verifyOrder(final List all) { - assertThat(all).containsExactly( - CoverageMetric.MODULE, - CoverageMetric.PACKAGE, - CoverageMetric.FILE, - CoverageMetric.CLASS, - CoverageMetric.METHOD, - CoverageMetric.LINE, - CoverageMetric.INSTRUCTION, - CoverageMetric.BRANCH - ); - } -} diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageNodeTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageNodeTest.java deleted file mode 100644 index 7dce47922..000000000 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageNodeTest.java +++ /dev/null @@ -1,562 +0,0 @@ -package io.jenkins.plugins.coverage.model; - -import java.util.List; -import java.util.Locale; -import java.util.Optional; - -import org.apache.commons.lang3.math.Fraction; -import org.junit.jupiter.api.Test; -import org.junitpioneer.jupiter.DefaultLocale; - -import nl.jqno.equalsverifier.EqualsVerifier; -import nl.jqno.equalsverifier.Warning; - -import io.jenkins.plugins.coverage.model.Coverage.CoverageBuilder; -import io.jenkins.plugins.coverage.targets.CoverageResult; - -import static io.jenkins.plugins.coverage.model.Assertions.*; -import static org.mockito.Mockito.*; - -/** - * Tests the class {@link CoverageNode}. - * - * @author Ullrich Hafner - * @author Florian Orendi - */ -@DefaultLocale("en") -class CoverageNodeTest extends AbstractCoverageTest { - private static final String PROJECT_NAME = "Java coding style: jacoco-codingstyle.xml"; - - @Test - void shouldProvideEmptyPathForDefaultPackage() { - PackageCoverageNode node = new PackageCoverageNode("-"); - assertThat(node.getPath()).isEqualTo(""); - } - - @Test - void shouldReturnCoberturaNpeReportIssue473() { - CoverageResult result = readCoberturaResult("cobertura-npe.xml"); - CoverageNode tree = new CoverageNodeConverter().convert(result); - - assertThat(tree.getAll(MODULE)).hasSize(1).extracting(CoverageNode::getName).containsOnly("cobertura: cobertura-npe.xml"); - assertThat(tree.getAll(PACKAGE)).hasSize(1).extracting(CoverageNode::getName).containsOnly("CoverageTest.Service"); - assertThat(tree.getAll(FILE)).hasSize(2).extracting(CoverageNode::getName).containsOnly("Program.cs", "Startup.cs"); - assertThat(tree.getAll(CLASS)).hasSize(2).extracting(CoverageNode::getName).containsOnly("Lisec.CoverageTest.Program", "Lisec.CoverageTest.Startup"); - - assertThat(tree).hasOnlyMetrics(MODULE, PACKAGE, FILE, CLASS, METHOD, LINE, BRANCH); - assertThat(tree.getMetricsDistribution()).containsExactly( - entry(MODULE, getCoverage(1, 0)), - entry(PACKAGE, getCoverage(1, 0)), - entry(FILE, getCoverage(2, 0)), - entry(CLASS, getCoverage(2, 0)), - entry(METHOD, getCoverage(4, 1)), - entry(LINE, getCoverage(44, 9)), - entry(BRANCH, getCoverage(3, 1))); - } - - private static Coverage getCoverage(final int covered, final int missed) { - return new CoverageBuilder().setCovered(covered).setMissed(missed).build(); - } - - @Test - void shouldReturnEmptyCoverageIfNotFound() { - CoverageNode root = readExampleReport(); - - assertThat(root.getCoverage(CoverageMetric.valueOf("new"))).isNotSet(); - } - - @Test - void shouldSplitPackagesWithoutPackageNodes() { - CoverageNode root = new CoverageNode(CoverageMetric.MODULE, "Root"); - assertThat(root.getAll(PACKAGE)).hasSize(0); - root.splitPackages(); - assertThat(root.getAll(PACKAGE)).hasSize(0); - - root.add(new CoverageNode(CoverageMetric.FILE, "file.c")); - root.splitPackages(); - assertThat(root.getAll(PACKAGE)).hasSize(0); - } - - @Test - void shouldSplitPackagesWithoutName() { - CoverageNode root = new CoverageNode(CoverageMetric.MODULE, "Root"); - assertThat(root.getAll(PACKAGE)).hasSize(0); - root.splitPackages(); - assertThat(root.getAll(PACKAGE)).hasSize(0); - - root.add(new CoverageNode(CoverageMetric.PACKAGE, "")); - assertThat(root.getAll(PACKAGE)).hasSize(1); - root.splitPackages(); - assertThat(root.getAll(PACKAGE)).hasSize(1); - } - - @Test - void shouldSplitPackagesWithSingleDot() { - CoverageNode root = new CoverageNode(CoverageMetric.MODULE, "Root"); - assertThat(root.getAll(PACKAGE)).hasSize(0); - root.splitPackages(); - assertThat(root.getAll(PACKAGE)).hasSize(0); - - root.add(new CoverageNode(CoverageMetric.PACKAGE, ".")); - assertThat(root.getAll(PACKAGE)).hasSize(1); - root.splitPackages(); - assertThat(root.getAll(PACKAGE)).hasSize(1); - } - - @Test - void shouldConvertCodingStyleToTree() { - CoverageNode tree = readExampleReport(); - - verifyCoverageMetrics(tree); - - assertThat(tree.getAll(MODULE)).hasSize(1); - assertThat(tree.getAll(PACKAGE)).hasSize(1); - List files = tree.getAll(FILE); - assertThat(files).hasSize(10); - assertThat(tree.getAll(CLASS)).hasSize(18); - assertThat(tree.getAll(METHOD)).hasSize(102); - - assertThat(tree).hasOnlyMetrics(MODULE, PACKAGE, FILE, CLASS, METHOD, LINE, BRANCH, INSTRUCTION) - .hasToString("[Module] " + PROJECT_NAME); - assertThat(tree.getMetricsDistribution()).containsExactly( - entry(MODULE, new Coverage.CoverageBuilder().setCovered(1).setMissed(0).build()), - entry(PACKAGE, new Coverage.CoverageBuilder().setCovered(1).setMissed(0).build()), - entry(FILE, new Coverage.CoverageBuilder().setCovered(7).setMissed(3).build()), - entry(CLASS, new Coverage.CoverageBuilder().setCovered(15).setMissed(3).build()), - entry(METHOD, new Coverage.CoverageBuilder().setCovered(97).setMissed(5).build()), - entry(LINE, new Coverage.CoverageBuilder().setCovered(294).setMissed(29).build()), - entry(INSTRUCTION, new Coverage.CoverageBuilder().setCovered(1260).setMissed(90).build()), - entry(BRANCH, new Coverage.CoverageBuilder().setCovered(109).setMissed(7).build())); - assertThat(tree.getMetricFractions()).containsExactly( - entry(MODULE, Fraction.ONE), - entry(PACKAGE, Fraction.ONE), - entry(FILE, Fraction.getFraction(7, 7 + 3)), - entry(CLASS, Fraction.getFraction(15, 15 + 3)), - entry(METHOD, Fraction.getFraction(97, 97 + 5)), - entry(LINE, Fraction.getFraction(294, 294 + 29)), - entry(INSTRUCTION, Fraction.getFraction(1260, 1260 + 90)), - entry(BRANCH, Fraction.getFraction(109, 109 + 7))); - assertThat(tree.getMetricPercentages()).containsExactly( - entry(MODULE, CoveragePercentage.valueOf(Fraction.ONE)), - entry(PACKAGE, CoveragePercentage.valueOf(Fraction.ONE)), - entry(FILE, CoveragePercentage.valueOf( - Fraction.getFraction(7, 7 + 3))), - entry(CLASS, CoveragePercentage.valueOf( - Fraction.getFraction(15, 15 + 3))), - entry(METHOD, CoveragePercentage.valueOf( - Fraction.getFraction(97, 97 + 5))), - entry(LINE, CoveragePercentage.valueOf( - Fraction.getFraction(294, 294 + 29))), - entry(INSTRUCTION, CoveragePercentage.valueOf( - Fraction.getFraction(1260, 1260 + 90))), - entry(BRANCH, CoveragePercentage.valueOf( - Fraction.getFraction(109, 109 + 7)))); - - assertThat(tree.getChildren()).hasSize(1).element(0).satisfies( - packageNode -> assertThat(packageNode).hasName("edu.hm.hafner.util") - ); - } - - private void verifyCoverageMetrics(final CoverageNode tree) { - Fraction coverageFraction = Fraction.getFraction(294, 294 + 29); - Fraction missedFraction = Fraction.getFraction(29, 294 + 29); - assertThat(tree.getCoverage(LINE)).isSet() - .hasCovered(294) - .hasCoveredFraction(coverageFraction) - .hasCoveredPercentage(CoveragePercentage.valueOf(coverageFraction)) - .hasMissed(29) - .hasMissedFraction(missedFraction) - .hasMissedPercentage(CoveragePercentage.valueOf(missedFraction)) - .hasTotal(294 + 29); - assertThat(tree.printCoverageFor(LINE)).isEqualTo("91.02%"); - assertThat(tree.printCoverageFor(LINE, Locale.GERMAN)).isEqualTo("91,02%"); - - coverageFraction = Fraction.getFraction(109, 109 + 7); - missedFraction = Fraction.getFraction(7, 109 + 7); - assertThat(tree.getCoverage(BRANCH)).isSet() - .hasCovered(109) - .hasCoveredFraction(coverageFraction) - .hasCoveredPercentage(CoveragePercentage.valueOf(coverageFraction)) - .hasMissed(7) - .hasMissedFraction(missedFraction) - .hasMissedPercentage(CoveragePercentage.valueOf(missedFraction)) - .hasTotal(109 + 7); - assertThat(tree.printCoverageFor(BRANCH)).isEqualTo("93.97%"); - assertThat(tree.printCoverageFor(BRANCH, Locale.GERMAN)).isEqualTo("93,97%"); - - coverageFraction = Fraction.getFraction(1260, 1260 + 90); - missedFraction = Fraction.getFraction(90, 1260 + 90); - assertThat(tree.getCoverage(INSTRUCTION)).isSet() - .hasCovered(1260) - .hasCoveredFraction(coverageFraction) - .hasCoveredPercentage(CoveragePercentage.valueOf(coverageFraction)) - .hasMissed(90) - .hasMissedFraction(missedFraction) - .hasMissedPercentage(CoveragePercentage.valueOf(missedFraction)) - .hasTotal(1260 + 90); - assertThat(tree.printCoverageFor(INSTRUCTION)).isEqualTo("93.33%"); - assertThat(tree.printCoverageFor(INSTRUCTION, Locale.GERMAN)).isEqualTo("93,33%"); - - coverageFraction = Fraction.ONE; - missedFraction = Fraction.ZERO; - assertThat(tree.getCoverage(MODULE)).isSet() - .hasCovered(1) - .hasCoveredFraction(coverageFraction) - .hasCoveredPercentage(CoveragePercentage.valueOf(coverageFraction)) - .hasMissed(0) - .hasMissedFraction(missedFraction) - .hasMissedPercentage(CoveragePercentage.valueOf(missedFraction)) - .hasTotal(1); - assertThat(tree.printCoverageFor(MODULE)).isEqualTo("100.00%"); - assertThat(tree.printCoverageFor(MODULE, Locale.GERMAN)).isEqualTo("100,00%"); - - assertThat(tree).hasName(PROJECT_NAME) - .doesNotHaveParent() - .isRoot() - .hasMetric(MODULE).hasParentName(CoverageNode.ROOT); - } - - @Test - void shouldNotReturnCoverageValuesWithoutLeaves() { - CoverageNode coverageNode = new CoverageNode(MODULE, "Root"); - assertThat(coverageNode).isRoot(); - assertThat(coverageNode.getMetricFractions()).isEmpty(); - assertThat(coverageNode.getMetricPercentages()).isEmpty(); - } - - @Test - void shouldSplitPackages() { - CoverageNode tree = readExampleReport(); - - tree.splitPackages(); - - verifyCoverageMetrics(tree); - - assertThat(tree.getAll(PACKAGE)).hasSize(4); - assertThat(tree.getMetricsDistribution()).contains( - entry(PACKAGE, new Coverage.CoverageBuilder().setCovered(4).setMissed(0).build())); - - assertThat(tree.getChildren()).hasSize(1).element(0).satisfies( - packageNode -> assertThat(packageNode).hasName("edu") - .hasParent() - .hasParentName(PROJECT_NAME) - ); - } - - @Test - void shouldComputeDelta() { - CoverageNode tree = readNode("jacoco-analysis-model.xml"); - - String checkStyleParser = "CheckStyleParser.java"; - String checkStyleParserPath = "edu/hm/hafner/analysis/parser/checkstyle/" + checkStyleParser; - Optional wrappedCheckStyle = tree.find(CoverageMetric.FILE, checkStyleParserPath); - assertThat(wrappedCheckStyle).isNotEmpty().hasValueSatisfying( - node -> assertThat(node) - .hasName(checkStyleParser) - .hasPath(checkStyleParserPath) - ); - - CoverageNode checkStyle = wrappedCheckStyle.get(); - assertThat(checkStyle.getMetricFractions()) - .containsEntry(FILE, Fraction.ONE) - .containsEntry(CLASS, Fraction.ONE) - .containsEntry(METHOD, Fraction.getFraction(6, 6)) - .containsEntry(LINE, Fraction.getFraction(41, 42)) - .containsEntry(INSTRUCTION, Fraction.getFraction(180, 187)) - .containsEntry(BRANCH, Fraction.getFraction(11, 12)); - assertThat(checkStyle.getMetricFractions()) - .containsEntry(FILE, Fraction.ONE) - .containsEntry(CLASS, Fraction.ONE) - .containsEntry(METHOD, Fraction.getFraction(6, 6)) - .containsEntry(LINE, Fraction.getFraction(41, 42)) - .containsEntry(INSTRUCTION, Fraction.getFraction(180, 187)) - .containsEntry(BRANCH, Fraction.getFraction(11, 12)); - - String pmdParser = "PmdParser.java"; - String pmdParserPath = "edu/hm/hafner/analysis/parser/pmd/" + pmdParser; - Optional wrappedPmd = tree.find(CoverageMetric.FILE, pmdParserPath); - assertThat(wrappedPmd).isNotEmpty().hasValueSatisfying( - node -> assertThat(node) - .hasName(pmdParser) - .hasPath(pmdParserPath) - ); - - CoverageNode pmd = wrappedPmd.get(); - assertThat(pmd.getMetricFractions()) - .containsEntry(FILE, Fraction.ONE) - .containsEntry(CLASS, Fraction.ONE) - .containsEntry(METHOD, Fraction.getFraction(8, 8)) - .containsEntry(LINE, Fraction.getFraction(72, 79)) - .containsEntry(INSTRUCTION, Fraction.getFraction(285, 313)) - .containsEntry(BRANCH, Fraction.getFraction(15, 18)); - assertThat(pmd.getMetricPercentages()) - .containsEntry(FILE, CoveragePercentage.valueOf(Fraction.ONE)) - .containsEntry(CLASS, CoveragePercentage.valueOf(Fraction.ONE)) - .containsEntry(METHOD, CoveragePercentage.valueOf( - Fraction.getFraction(8, 8))) - .containsEntry(LINE, CoveragePercentage.valueOf( - Fraction.getFraction(72, 79))) - .containsEntry(INSTRUCTION, CoveragePercentage.valueOf( - Fraction.getFraction(285, 313))) - .containsEntry(BRANCH, CoveragePercentage.valueOf( - Fraction.getFraction(15, 18))); - - assertThat(checkStyle.computeDelta(pmd)) - .containsEntry(FILE, Fraction.ZERO) - .containsEntry(CLASS, Fraction.ZERO) - .containsEntry(METHOD, Fraction.getFraction(0, 12)) - .containsEntry(LINE, Fraction.getFraction(215, 3318)) - .containsEntry(INSTRUCTION, Fraction.getFraction(3045, 58_531)) - .containsEntry(BRANCH, Fraction.getFraction(1, 12)); - assertThat(checkStyle.computeDeltaAsPercentage(pmd)) - .containsEntry(FILE, CoveragePercentage.valueOf(Fraction.ZERO)) - .containsEntry(CLASS, CoveragePercentage.valueOf(Fraction.ZERO)) - .containsEntry(METHOD, CoveragePercentage.valueOf( - Fraction.getFraction(0, 12))) - .containsEntry(LINE, CoveragePercentage.valueOf( - Fraction.getFraction(215, 3318))) - .containsEntry(INSTRUCTION, CoveragePercentage.valueOf( - Fraction.getFraction(3045, 58_531))) - .containsEntry(BRANCH, CoveragePercentage.valueOf( - Fraction.getFraction(1, 12))); - - assertThat(pmd.computeDelta(checkStyle)) - .containsEntry(FILE, Fraction.ZERO) - .containsEntry(CLASS, Fraction.ZERO) - .containsEntry(METHOD, Fraction.getFraction(0, 12)) - .containsEntry(LINE, Fraction.getFraction(-215, 3318)) - .containsEntry(INSTRUCTION, Fraction.getFraction(-3045, 58_531)) - .containsEntry(BRANCH, Fraction.getFraction(-1, 12)); - assertThat(pmd.computeDeltaAsPercentage(checkStyle)) - .containsEntry(FILE, CoveragePercentage.valueOf(Fraction.ZERO)) - .containsEntry(CLASS, CoveragePercentage.valueOf(Fraction.ZERO)) - .containsEntry(METHOD, CoveragePercentage.valueOf( - Fraction.getFraction(0, 12))) - .containsEntry(LINE, CoveragePercentage.valueOf( - Fraction.getFraction(-215, 3318))) - .containsEntry(INSTRUCTION, CoveragePercentage.valueOf( - Fraction.getFraction(-3045, 58_531))) - .containsEntry(BRANCH, CoveragePercentage.valueOf( - Fraction.getFraction(-1, 12))); - } - - @Test - void shouldSplitComplexPackageStructure() { - CoverageNode tree = readNode("jacoco-analysis-model.xml"); - - assertThat(tree.getAll(PACKAGE)).hasSize(18); - - tree.splitPackages(); - - assertThat(tree.getAll(PACKAGE)).hasSize(3 + 18); - } - - @Test - void shouldNotSplitPackagesIfOnWrongHierarchyNode() { - CoverageNode tree = readExampleReport(); - CoverageNode packageNode = tree.getChildren().get(0); - assertThat(packageNode).hasName("edu.hm.hafner.util").hasPath("edu/hm/hafner/util"); - - List files = packageNode.getChildren(); - - packageNode.splitPackages(); - assertThat(packageNode).hasName("edu.hm.hafner.util"); - assertThat(packageNode).hasChildren(files); - } - - @Test - void shouldThrowExceptionWhenObtainingAllBasicBlocks() { - CoverageNode tree = readExampleReport(); - - assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> tree.getAll(LINE)); - assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> tree.getAll(BRANCH)); - assertThatExceptionOfType(AssertionError.class).isThrownBy(() -> tree.getAll(INSTRUCTION)); - } - - @Test - void shouldFindImportantElements() { - CoverageNode tree = readExampleReport(); - - assertThat(tree.getImportantMetrics()).containsExactly(LINE, BRANCH); - } - - @Test - void shouldFindFiles() { - CoverageNode tree = readExampleReport(); - tree.splitPackages(); - - String fileName = "Ensure.java"; - String filePath = "edu/hm/hafner/util/" + fileName; - assertThat(tree.findByHashCode(FILE, filePath.hashCode())).isNotEmpty().hasValueSatisfying( - node -> assertThat(node).hasPath(filePath).isNotRoot()); - assertThat(tree.findByHashCode(FILE, fileName.hashCode())).isEmpty(); - assertThat(tree.findByHashCode(FILE, "not-found".hashCode())).isEmpty(); - - String noBranchCoverage = "NoSuchElementException.java"; - String noBranchCoveragePath = "edu/hm/hafner/util/" + noBranchCoverage; - assertThat(tree.find(FILE, noBranchCoveragePath)).isNotEmpty().hasValueSatisfying( - node -> { - assertThat(node) - .hasName(noBranchCoverage) - .hasPath(noBranchCoveragePath) - .isNotRoot(); - assertThat(node.getCoverage(BRANCH)).isNotSet(); - assertThat(node.printCoverageFor(BRANCH)).isEqualTo(Messages.Coverage_Not_Available()); - } - ); - } - - @Test - void shouldCreatePackageName() { - CoverageNode tree = readExampleReport(); - - String fileName = "Ensure.java"; - String filePath = "edu/hm/hafner/util/" + fileName; - assertThat(tree.find(FILE, filePath)).isNotEmpty().hasValueSatisfying( - node -> assertThat(node).hasName(fileName) - .hasParentName("edu.hm.hafner.util") - .hasParent() - .isNotRoot() - ); - - tree.splitPackages(); - assertThat(tree.find(FILE, filePath)).isNotEmpty().hasValueSatisfying( - node -> assertThat(node).hasName(fileName) - .hasParentName("edu.hm.hafner.util") - .hasParent() - .isNotRoot() - ); - } - - @Test - void shouldRemovePackagesWithoutFiles() { - CoverageNode tree = readNode("jacoco-analysis-model.xml"); - tree.splitPackages(); - int packageNodes = tree.getChildren().size(); - CoverageNode filteredTree = tree.filterPackageStructure(); - - assertThat(filteredTree.getChildren().stream() - .noneMatch(node -> node.getChildren().stream() - .noneMatch(child -> child.getMetric().equals(FILE)))) - .isTrue(); - assertThat(tree.getChildren().size()).isEqualTo(packageNodes); - } - - @Test - void shouldGetAllFileCoverageNodes() { - CoverageNode tree = readNode("jacoco-analysis-model.xml"); - tree.splitPackages(); - assertThat(tree.getAllFileCoverageNodes()) - .hasSize(307) - .satisfies(nodes -> nodes.forEach( - node -> assertThat(node).isInstanceOf(FileCoverageNode.class))); - } - - @Test - void shouldProvideExistentChangeCoverage() { - CoverageNode tree = createTreeWithMockedTreeCreator(); - assertThat(tree.hasCodeChanges()).isTrue(); - assertThat(tree.hasChangeCoverage()).isTrue(); - assertThat(tree.hasChangeCoverage(LINE)).isTrue(); - assertThat(tree.getChangeCoverageTree()).isEqualTo(tree); - assertThat(tree.getFileAmountWithChangedCoverage()).isOne(); - assertThat(tree.getLineAmountWithChangedCoverage()).isOne(); - } - - @Test - void shouldProvideExistentIndirectCoverageChanges() { - CoverageNode tree = createTreeWithMockedTreeCreator(); - assertThat(tree.hasIndirectCoverageChanges()).isTrue(); - assertThat(tree.hasIndirectCoverageChanges(LINE)).isTrue(); - assertThat(tree.getIndirectCoverageChangesTree()).isEqualTo(tree); - assertThat(tree.getFileAmountWithIndirectCoverageChanges()).isOne(); - assertThat(tree.getLineAmountWithIndirectCoverageChanges()).isOne(); - } - - @Test - void shouldDetermineNotExistentChangeCoverage() { - CoverageNode tree = createTreeWithMockedTreeCreatorWithoutChanges(); - assertThat(tree.hasCodeChanges()).isFalse(); - assertThat(tree.hasChangeCoverage()).isFalse(); - assertThat(tree.hasChangeCoverage(LINE)).isFalse(); - assertThat(tree.getChangeCoverageTree()) - .hasNoChildren() - .hasNoLeaves(); - assertThat(tree.getFileAmountWithChangedCoverage()).isZero(); - assertThat(tree.getLineAmountWithChangedCoverage()).isZero(); - } - - @Test - void shouldDetermineNotExistentIndirectCoverageChanges() { - CoverageNode tree = createTreeWithMockedTreeCreatorWithoutChanges(); - assertThat(tree.hasIndirectCoverageChanges()).isFalse(); - assertThat(tree.hasIndirectCoverageChanges(LINE)).isFalse(); - assertThat(tree.getIndirectCoverageChangesTree()) - .hasNoChildren() - .hasNoLeaves(); - assertThat(tree.getFileAmountWithIndirectCoverageChanges()).isZero(); - assertThat(tree.getLineAmountWithIndirectCoverageChanges()).isZero(); - } - - @Test - void shouldObeyEqualsContract() { - EqualsVerifier.forClass(CoverageNode.class) - .withPrefabValues(CoverageNode.class, - new CoverageNode(CoverageMetric.FILE, "file.txt"), - new CoverageNode(CoverageMetric.LINE, "line")) - .suppress(Warning.NONFINAL_FIELDS) - .usingGetClass() - .withIgnoredFields("parent") - .verify(); - } - - /** - * Reads the coverage tree from the report 'jacoco-codingstyle.xml'. - * - * @return the {@link CoverageNode} root of the tree - */ - private CoverageNode readExampleReport() { - return readNode("jacoco-codingstyle.xml"); - } - - /** - * Creates a coverage tree with a mocked {@link CoverageTreeCreator} in order to test calculations for change - * coverage and indirect coverage changes. - * - * @return the {@link CoverageNode root} of the created tree - */ - private CoverageNode createTreeWithMockedTreeCreator() { - CoverageTreeCreator coverageTreeCreator = mock(CoverageTreeCreator.class); - CoverageNode root = new CoverageNode(CoverageMetric.MODULE, CoverageNode.ROOT, coverageTreeCreator); - - FileCoverageNode fileNode = new FileCoverageNode("test", "test"); - fileNode.putCoveragePerLine(1, CoverageBuilder.NO_COVERAGE); - fileNode.addChangedCodeLine(1); - fileNode.addChangedCodeLine(2); - fileNode.putIndirectCoverageChange(3, 1); - CoverageNode tree = readExampleReport(); - root.add(tree); - root.add(fileNode); - - when(coverageTreeCreator.createChangeCoverageTree(root)).thenReturn(root); - when(coverageTreeCreator.createIndirectCoverageChangesTree(root)).thenReturn(root); - - return root; - } - - /** - * Creates a coverage tree with a mocked {@link CoverageTreeCreator} and without changes in order to test - * calculations for non existent change coverage and indirect coverage changes. - * - * @return the {@link CoverageNode root} of the created tree - */ - private CoverageNode createTreeWithMockedTreeCreatorWithoutChanges() { - CoverageTreeCreator coverageTreeCreator = mock(CoverageTreeCreator.class); - CoverageNode root = new CoverageNode(CoverageMetric.MODULE, CoverageNode.ROOT, coverageTreeCreator); - - when(coverageTreeCreator.createChangeCoverageTree(root)).thenReturn(root); - when(coverageTreeCreator.createIndirectCoverageChangesTree(root)).thenReturn(root); - - return root; - } -} diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePercentageTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePercentageTest.java index 3d9964650..13233f83c 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePercentageTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePercentageTest.java @@ -5,6 +5,9 @@ import org.apache.commons.lang3.math.Fraction; import org.junit.jupiter.api.Test; +import edu.hm.hafner.metric.Coverage; +import edu.hm.hafner.metric.Metric; + import nl.jqno.equalsverifier.EqualsVerifier; import static io.jenkins.plugins.coverage.model.Assertions.assertThat; @@ -93,6 +96,6 @@ void shouldSerializeInstance() { .hasToString("49.00%"); assertThatIllegalArgumentException().isThrownBy( - () -> Coverage.valueOf("1/0")); + () -> Coverage.valueOf(Metric.LINE, "1/0")); } } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java index a9d118392..909b06b1a 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java @@ -7,6 +7,10 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import edu.hm.hafner.metric.Coverage; +import edu.hm.hafner.metric.Coverage.CoverageBuilder; +import edu.hm.hafner.metric.Metric; + import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; import org.jenkinsci.plugins.workflow.job.WorkflowJob; import hudson.model.FreeStyleProject; @@ -18,6 +22,9 @@ import io.jenkins.plugins.coverage.adapter.CoberturaReportAdapter; import io.jenkins.plugins.coverage.adapter.CoverageAdapter; import io.jenkins.plugins.coverage.adapter.JacocoReportAdapter; +import io.jenkins.plugins.coverage.metrics.CoverageRecorder; +import io.jenkins.plugins.coverage.metrics.CoverageTool; +import io.jenkins.plugins.coverage.metrics.CoverageTool.CoverageParser; import io.jenkins.plugins.util.IntegrationTestWithJenkinsPerSuite; import static org.assertj.core.api.Assertions.*; @@ -162,11 +169,12 @@ void freestyleForNoJacoco() { @Test void freestyleForOneJacoco() { FreeStyleProject project = createFreeStyleProject(); - copyFilesToWorkspace(project, JACOCO_ANALYSIS_MODEL_FILE); - CoveragePublisher coveragePublisher = new CoveragePublisher(); - JacocoReportAdapter jacocoReportAdapter = new JacocoReportAdapter(JACOCO_ANALYSIS_MODEL_FILE); - coveragePublisher.setAdapters(Collections.singletonList(jacocoReportAdapter)); - project.getPublishersList().add(coveragePublisher); + copyFileToWorkspace(project, JACOCO_ANALYSIS_MODEL_FILE, "jacoco.xml"); + CoverageRecorder recorder = new CoverageRecorder(); + var tool = new CoverageTool(); + tool.setParser(CoverageParser.JACOCO); + recorder.setTools(List.of(tool)); + project.getPublishersList().add(recorder); verifyForOneJacoco(project); } @@ -342,7 +350,9 @@ private void verifyForOneJacoco(final ParameterizedJob project) { CoverageBuildAction coverageResult = build.getAction(CoverageBuildAction.class); assertThat(coverageResult.getLineCoverage()) - .isEqualTo(new Coverage.CoverageBuilder().setCovered(JACOCO_COVERED_LINES) + .isEqualTo(new Coverage.CoverageBuilder() + .setMetric(Metric.LINE) + .setCovered(JACOCO_COVERED_LINES) .setMissed(JACOCO_ALL_LINES - JACOCO_COVERED_LINES) .build()); } @@ -396,7 +406,7 @@ private void verifyForOneCobertura(final ParameterizedJob project) { CoverageBuildAction coverageResult = build.getAction(CoverageBuildAction.class); assertThat(coverageResult.getLineCoverage()) - .isEqualTo(new Coverage.CoverageBuilder().setCovered(COBERTURA_COVERED_LINES) + .isEqualTo(new CoverageBuilder().setMetric(Metric.LINE).setCovered(COBERTURA_COVERED_LINES) .setMissed(COBERTURA_ALL_LINES - COBERTURA_COVERED_LINES) .build()); diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginSourceITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginSourceITest.java index 5fa8e5f39..7f7a23266 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginSourceITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginSourceITest.java @@ -10,6 +10,10 @@ import org.junit.jupiter.api.Test; +import edu.hm.hafner.metric.Coverage; +import edu.hm.hafner.metric.Coverage.CoverageBuilder; +import edu.hm.hafner.metric.Metric; +import edu.hm.hafner.metric.Node; import edu.hm.hafner.util.PathUtil; import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; @@ -172,12 +176,11 @@ private void verifySourceCodeInBuild(final Run build, final String sourceC private CoverageViewModel verifyViewModel(final Run build) { CoverageBuildAction action = build.getAction(CoverageBuildAction.class); assertThat(action.getLineCoverage()) - .isEqualTo(new Coverage.CoverageBuilder().setCovered(8).setMissed(0).build()); + .isEqualTo(new CoverageBuilder().setMetric(Metric.LINE).setCovered(8).setMissed(0).build()); - Optional fileNode = action.getResult().find(CoverageMetric.FILE, SOURCE_FILE_PATH); + Optional fileNode = action.getResult().find(Metric.FILE, SOURCE_FILE_PATH); assertThat(fileNode).isNotEmpty() - .hasValueSatisfying(node -> - assertThat(node.getPath()).isEqualTo(SOURCE_FILE_PATH)); + .hasValueSatisfying(node -> assertThat(node.getPath()).isEqualTo(SOURCE_FILE_PATH)); return action.getTarget(); } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageResultTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageResultTest.java deleted file mode 100644 index f8bb16382..000000000 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageResultTest.java +++ /dev/null @@ -1,96 +0,0 @@ -package io.jenkins.plugins.coverage.model; - -import org.junit.jupiter.api.Test; - -import io.jenkins.plugins.coverage.adapter.JacocoReportAdapter.JacocoReportAdapterDescriptor; -import io.jenkins.plugins.coverage.adapter.JavaCoverageReportAdapterDescriptor; -import io.jenkins.plugins.coverage.exception.CoverageException; -import io.jenkins.plugins.coverage.targets.CoverageElement; -import io.jenkins.plugins.coverage.targets.CoverageResult; -import io.jenkins.plugins.coverage.targets.Ratio; - -import static io.jenkins.plugins.coverage.model.Assertions.*; - -/** - * TODO: Move to corresponding test class. - * - * @author Ullrich Hafner - */ -class CoverageResultTest extends AbstractCoverageTest { - private static final String EXPECTED_PACKAGE_NAME = "edu.hm.hafner.util"; - private static final String TREE_STRING_BUILDER_FILE_NAME = "TreeStringBuilder.java"; - private static final String TREE_STRING_BUILDER_CLASS = "edu.hm.hafner.util.TreeStringBuilder"; - - private static final CoverageElement LINE = CoverageElement.LINE; - private static final CoverageElement INSTRUCTION = JacocoReportAdapterDescriptor.INSTRUCTION; - private static final CoverageElement BRANCH = CoverageElement.CONDITIONAL; - private static final CoverageElement PACKAGE = JavaCoverageReportAdapterDescriptor.PACKAGE; - private static final CoverageElement REPORT = CoverageElement.REPORT; - private static final CoverageElement SOURCE_FILE = CoverageElement.FILE; - private static final CoverageElement CLASS_NAME = JavaCoverageReportAdapterDescriptor.CLASS; - - @Test - void shouldNeverReturnNullForSets() throws CoverageException { - CoverageResult project = readReport("jacoco-codingstyle.xml"); - - assertThat(project.getAdditionalProperty("not-found")).isEmpty(); - } - - @Test - void shouldReadJaCoCoResult() throws CoverageException { - CoverageResult project = readReport("jacoco-codingstyle.xml"); - - assertThat(project.getElement()).isEqualTo(REPORT); - assertThat(project.hasSingletonChild()).isTrue(); - assertThat(project.getChildren()).hasSize(1).containsExactly(EXPECTED_PACKAGE_NAME); - - CoverageResult utilPackage = project.getSingletonChild(); - assertThat(utilPackage.getElement()).isEqualTo(PACKAGE); - assertThat(utilPackage.getChildren()).hasSize(10).containsExactly( - "Ensure.java", - "FilteredLog.java", - "Generated.java", - "NoSuchElementException.java", - "PathUtil.java", - "PrefixLogger.java", - "StringContainsUtils.java", - "TreeString.java", - TREE_STRING_BUILDER_FILE_NAME, - "VisibleForTesting.java"); - verifyCoverage(utilPackage, LINE, 294, 29); - verifyCoverage(utilPackage, INSTRUCTION, 1260, 90); - verifyCoverage(utilPackage, BRANCH, 109, 7); - - CoverageResult treeStringBuilderFile = utilPackage.getChild(TREE_STRING_BUILDER_FILE_NAME); - assertThat(treeStringBuilderFile.getElement()).isEqualTo(SOURCE_FILE); - assertThat(treeStringBuilderFile.getChildren()).hasSize(2).containsExactly( - TREE_STRING_BUILDER_CLASS, - "edu.hm.hafner.util.TreeStringBuilder.Child"); - verifyCoverage(treeStringBuilderFile, LINE, 51, 2); - - // FIXME: check why there is no INSTRUCTION result - // verifyCoverage(treeStringBuilderFile, INSTRUCTION, 229, 4); - verifyCoverage(treeStringBuilderFile, BRANCH, 17, 1); - - CoverageResult treeStringBuilderClass = treeStringBuilderFile.getChild(TREE_STRING_BUILDER_CLASS); - assertThat(treeStringBuilderClass.getElement()).isEqualTo(CLASS_NAME); - assertThat(treeStringBuilderClass.getChildren()).hasSize(7) - .containsExactly("edu.hm.hafner.util.TreeString intern(edu.hm.hafner.util.TreeString)", - "edu.hm.hafner.util.TreeString intern(java.lang.String)", - "edu.hm.hafner.util.TreeStringBuilder$Child getRoot()", - "void ()", - "void ()", - "void dedup()", - "void setRoot(edu.hm.hafner.util.TreeStringBuilder$Child)"); - verifyCoverage(treeStringBuilderClass, LINE, 8, 2); - verifyCoverage(treeStringBuilderClass, INSTRUCTION, 37, 4); - assertThat(treeStringBuilderClass.getCoverageFor(BRANCH)).isEmpty(); - } - - private void verifyCoverage(final CoverageResult actualResult, - final CoverageElement coverageElement, final int covered, final int missed) { - assertThat(actualResult.getCoverageFor(coverageElement)) - .isNotEmpty() - .contains(Ratio.create(covered, covered + missed)); - } -} diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTest.java deleted file mode 100644 index 1e7283682..000000000 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTest.java +++ /dev/null @@ -1,136 +0,0 @@ -package io.jenkins.plugins.coverage.model; - -import org.apache.commons.lang3.math.Fraction; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; -import org.junitpioneer.jupiter.DefaultLocale; - -import nl.jqno.equalsverifier.EqualsVerifier; - -import io.jenkins.plugins.coverage.model.Coverage.CoverageBuilder; - -import static io.jenkins.plugins.coverage.model.Assertions.*; -import static io.jenkins.plugins.coverage.model.Coverage.CoverageBuilder.*; - -/** - * Tests the class {@link Coverage}. - * - * @author Ullrich Hafner - */ -@DefaultLocale("en") -class CoverageTest { - @Test - void shouldProvideNullObject() { - assertThat(NO_COVERAGE).isNotSet() - .hasCovered(0) - .hasCoveredFraction(Fraction.ZERO) - .hasCoveredPercentage(CoveragePercentage.valueOf(Fraction.ZERO)) - .hasRoundedPercentage(0) - .hasMissed(0) - .hasMissedFraction(Fraction.ZERO) - .hasMissedPercentage(CoveragePercentage.valueOf(Fraction.ZERO)) - .hasTotal(0) - .hasToString(Messages.Coverage_Not_Available()); - assertThat(NO_COVERAGE.formatCoveredPercentage()).isEqualTo(Messages.Coverage_Not_Available()); - assertThat(NO_COVERAGE.formatMissedPercentage()).isEqualTo(Messages.Coverage_Not_Available()); - assertThat(NO_COVERAGE.add(NO_COVERAGE)).isEqualTo(NO_COVERAGE); - - assertThat(NO_COVERAGE.serializeToString()).isEqualTo("0/0"); - assertThat(Coverage.valueOf("0/0")).isEqualTo(NO_COVERAGE); - } - - @Test - void shouldCreatePercentages() { - Coverage coverage = new CoverageBuilder().setCovered(6).setMissed(4).build(); - Fraction coverageFraction = Fraction.getFraction(6, 10); - Fraction missedFraction = Fraction.getFraction(4, 10); - assertThat(coverage).isSet() - .hasCovered(6) - .hasCoveredFraction(coverageFraction) - .hasCoveredPercentage(CoveragePercentage.valueOf(coverageFraction)) - .hasRoundedPercentage(60) - .hasMissed(4) - .hasMissedFraction(missedFraction) - .hasMissedPercentage(CoveragePercentage.valueOf(missedFraction)) - .hasTotal(10) - .hasToString("60.00% (6/10)"); - - assertThat(coverage.serializeToString()).isEqualTo("6/10"); - assertThat(Coverage.valueOf("6/10")).isEqualTo(coverage); - - assertThat(coverage.formatCoveredPercentage()).isEqualTo("60.00%"); - assertThat(coverage.formatMissedPercentage()).isEqualTo("40.00%"); - - assertThat(coverage.add(NO_COVERAGE)).isEqualTo(coverage); - Coverage sum = coverage.add(new CoverageBuilder().setCovered(10).setMissed(0).build()); - assertThat(sum).isEqualTo(new CoverageBuilder().setCovered(16).setMissed(4).build()).hasRoundedPercentage(80); - assertThat(sum.formatCoveredPercentage()).isEqualTo("80.00%"); - assertThat(sum.formatMissedPercentage()).isEqualTo("20.00%"); - } - - @Test - void shouldThrowExceptionForInvalidBuilderArguments() { - assertThatIllegalArgumentException().isThrownBy(() -> - new CoverageBuilder().setCovered(1).setMissed(1).setTotal(1).build()); - assertThatIllegalArgumentException().isThrownBy(() -> - new CoverageBuilder().setCovered(1).build()); - assertThatIllegalArgumentException().isThrownBy(() -> - new CoverageBuilder().setMissed(1).build()); - assertThatIllegalArgumentException().isThrownBy(() -> - new CoverageBuilder().setTotal(1).build()); - } - - @Test - void shouldProvideMultipleOptionsToCreateCoverage() { - assertThat(new CoverageBuilder().setCovered(1).setMissed(2).build()) - .hasCovered(1) - .hasMissed(2) - .hasTotal(3); - assertThat(new CoverageBuilder().setCovered(1).setTotal(3).build()) - .hasCovered(1) - .hasMissed(2) - .hasTotal(3); - assertThat(new CoverageBuilder().setMissed(2).setTotal(3).build()) - .hasCovered(1) - .hasMissed(2) - .hasTotal(3); - } - - @Test - void shouldCacheValues() { - for (int covered = 0; covered < CACHE_SIZE; covered++) { - for (int missed = 0; missed < CACHE_SIZE; missed++) { - CoverageBuilder builder = new CoverageBuilder().setCovered(covered).setMissed(missed); - - assertThat(builder.build()) - .isSameAs(builder.build()) - .hasCovered(covered) - .hasMissed(missed); - } - } - - CoverageBuilder coveredOutOfCache = new CoverageBuilder().setCovered(CACHE_SIZE).setMissed(CACHE_SIZE - 1); - assertThat(coveredOutOfCache.build()) - .isNotSameAs(coveredOutOfCache.build()) - .isEqualTo(coveredOutOfCache.build()); - CoverageBuilder missedOutOfCache = new CoverageBuilder().setCovered(CACHE_SIZE - 1).setMissed(CACHE_SIZE); - assertThat(missedOutOfCache.build()) - .isNotSameAs(missedOutOfCache.build()) - .isEqualTo(missedOutOfCache.build()); - } - - @ParameterizedTest(name = "[{index}] Illegal coverage serialization = \"{0}\"") - @ValueSource(strings = {"", "-", "/", "0/", "0/0/0", "/0", "a/1", "1/a", "1.0/1.0", "4/3"}) - @DisplayName("Should throw exception for illegal serializations") - void shouldThrowExceptionForInvalidCoverages(final String serialization) { - assertThatIllegalArgumentException().isThrownBy(() -> Coverage.valueOf(serialization)) - .withMessageContaining(serialization); - } - - @Test - void shouldVerifyEquals() { - EqualsVerifier.forClass(Coverage.class).verify(); - } -} diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTreeCreatorTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTreeCreatorTest.java index ee82a3e59..7597fa943 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTreeCreatorTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTreeCreatorTest.java @@ -8,6 +8,12 @@ import org.junit.jupiter.api.Test; +import edu.hm.hafner.metric.Coverage; +import edu.hm.hafner.metric.Coverage.CoverageBuilder; +import edu.hm.hafner.metric.FileNode; +import edu.hm.hafner.metric.Metric; +import edu.hm.hafner.metric.Node; + import static org.assertj.core.api.Assertions.*; /** @@ -20,8 +26,8 @@ class CoverageTreeCreatorTest extends AbstractCoverageTest { @Test void shouldCreateEmptyChangeCoverageTreeWithoutChanges() { CoverageTreeCreator coverageTreeCreator = createCoverageTreeCreator(); - CoverageNode tree = readCoverageTree(); - CoverageNode changeCoverageTree = coverageTreeCreator.createChangeCoverageTree(tree); + Node tree = readCoverageTree(); + Node changeCoverageTree = coverageTreeCreator.createChangeCoverageTree(tree); assertThat(changeCoverageTree) .isNotNull() .isNotSameAs(tree) @@ -30,15 +36,15 @@ void shouldCreateEmptyChangeCoverageTreeWithoutChanges() { assertThat(root.getPath()).isEqualTo(tree.getPath()); assertThat(root.getMetric()).isEqualTo(tree.getMetric()); assertThat(root.getChildren()).isEmpty(); - assertThat(root.getLeaves()).isEmpty(); + assertThat(root.getValues()).isEmpty(); }); } @Test void shouldCreateEmptyIndirectCoverageChangesTreeWithoutChanges() { CoverageTreeCreator coverageTreeCreator = createCoverageTreeCreator(); - CoverageNode tree = readCoverageTree(); - CoverageNode indirectCoverageChangesTree = coverageTreeCreator.createIndirectCoverageChangesTree(tree); + Node tree = readCoverageTree(); + Node indirectCoverageChangesTree = coverageTreeCreator.createIndirectCoverageChangesTree(tree); assertThat(indirectCoverageChangesTree) .isNotNull() .isNotSameAs(tree) @@ -47,15 +53,15 @@ void shouldCreateEmptyIndirectCoverageChangesTreeWithoutChanges() { assertThat(root.getPath()).isEqualTo(tree.getPath()); assertThat(root.getMetric()).isEqualTo(tree.getMetric()); assertThat(root.getChildren()).isEmpty(); - assertThat(root.getLeaves()).isEmpty(); + assertThat(root.getValues()).isEmpty(); }); } @Test void shouldCreateChangeCoverageTree() { CoverageTreeCreator coverageTreeCreator = createCoverageTreeCreator(); - CoverageNode tree = createTreeWithChangeCoverage(); - CoverageNode changeCoverageTree = coverageTreeCreator.createChangeCoverageTree(tree); + Node tree = createTreeWithChangeCoverage(); + Node changeCoverageTree = coverageTreeCreator.createChangeCoverageTree(tree); assertThat(changeCoverageTree) .isNotNull() .isNotSameAs(tree) @@ -64,9 +70,9 @@ void shouldCreateChangeCoverageTree() { assertThat(root.getPath()).isEqualTo(tree.getPath()); assertThat(root.getMetric()).isEqualTo(tree.getMetric()); assertThat(root.getAll(FILE)).hasSize(1); - assertThat(root.getCoverage(LINE)).isEqualTo( + assertThat(root.getValue(LINE)).isNotEmpty().contains( new Coverage.CoverageBuilder().setCovered(2).setMissed(2).build()); - assertThat(root.getCoverage(BRANCH)).isEqualTo( + assertThat(root.getValue(BRANCH)).isNotEmpty().contains( new Coverage.CoverageBuilder().setCovered(4).setMissed(4).build()); }); } @@ -74,8 +80,8 @@ void shouldCreateChangeCoverageTree() { @Test void shouldCreateIndirectCoverageChangesTree() { CoverageTreeCreator coverageTreeCreator = createCoverageTreeCreator(); - CoverageNode tree = createTreeWithIndirectCoverageChanges(); - CoverageNode indirectCoverageChangesTree = coverageTreeCreator.createIndirectCoverageChangesTree(tree); + Node tree = createTreeWithIndirectCoverageChanges(); + Node indirectCoverageChangesTree = coverageTreeCreator.createIndirectCoverageChangesTree(tree); assertThat(indirectCoverageChangesTree) .isNotNull() .isNotSameAs(tree) @@ -84,9 +90,9 @@ void shouldCreateIndirectCoverageChangesTree() { assertThat(root.getPath()).isEqualTo(tree.getPath()); assertThat(root.getMetric()).isEqualTo(tree.getMetric()); assertThat(root.getAll(FILE)).hasSize(1); - assertThat(root.getCoverage(LINE)).isEqualTo( + assertThat(root.getValue(LINE)).isNotEmpty().contains( new Coverage.CoverageBuilder().setCovered(2).setMissed(2).build()); - assertThat(root.getCoverage(BRANCH)).isEqualTo( + assertThat(root.getValue(BRANCH)).isNotEmpty().contains( new Coverage.CoverageBuilder().setCovered(4).setMissed(4).build()); }); } @@ -103,40 +109,38 @@ private CoverageTreeCreator createCoverageTreeCreator() { /** * Reads the coverage tree from the report 'jacoco-codingstyle.xml'. * - * @return the {@link CoverageNode} root of the tree + * @return the {@link Node} root of the tree */ - private CoverageNode readCoverageTree() { - CoverageNode root = readNode("jacoco-codingstyle.xml"); - root.splitPackages(); - return root; + private Node readCoverageTree() { + return readJacocoResult("jacoco-codingstyle.xml"); } /** - * Creates a coverage tree which contains a {@link FileCoverageNode} with changed code lines and change coverage. + * Creates a coverage tree which contains a {@link FileNode} with changed code lines and change coverage. * * @return the root of the tree */ - private CoverageNode createTreeWithChangeCoverage() { - CoverageNode root = readCoverageTree(); - FileCoverageNode file = attachFileCoverageNodeToTree(root); + private Node createTreeWithChangeCoverage() { + Node root = readCoverageTree(); + FileNode file = attachFileCoverageNodeToTree(root); attachCodeChanges(file); return root; } /** - * Creates a coverage tree which contains a {@link FileCoverageNode} with indirect coverage changes. + * Creates a coverage tree which contains a {@link FileNode} with indirect coverage changes. * * @return the root of the tree */ - private CoverageNode createTreeWithIndirectCoverageChanges() { - CoverageNode root = readCoverageTree(); - FileCoverageNode file = attachFileCoverageNodeToTree(root); + private Node createTreeWithIndirectCoverageChanges() { + Node root = readCoverageTree(); + FileNode file = attachFileCoverageNodeToTree(root); attachIndirectCoverageChanges(file); return root; } /** - * Attaches a custom {@link FileCoverageNode file node} to an existing coverage tree and returns the inserted + * Attaches a custom {@link FileNode file node} to an existing coverage tree and returns the inserted * instance. * * @param root @@ -144,58 +148,56 @@ private CoverageNode createTreeWithIndirectCoverageChanges() { * * @return the inserted instance */ - private FileCoverageNode attachFileCoverageNodeToTree(final CoverageNode root) { - FileCoverageNode file = new FileCoverageNode("CoverageTreeTest.java", ""); - Optional packageNode = root.getAll(PACKAGE).stream().findFirst(); + private FileNode attachFileCoverageNodeToTree(final Node root) { + FileNode file = new FileNode("CoverageTreeTest.java"); + Optional packageNode = root.getAll(PACKAGE).stream().findFirst(); assertThat(packageNode).isNotEmpty(); - packageNode.get().add(file); - file.setParent(packageNode.get()); + packageNode.get().addChild(file); attachCoveragePerLine(file); return file; } /** - * Attaches the coverage-per-line to the passed {@link FileCoverageNode node}. + * Attaches the coverage-per-line to the passed {@link FileNode node}. * * @param file * The node to which coverage information should be added */ - private void attachCoveragePerLine(final FileCoverageNode file) { - SortedMap coveragePerLine = new TreeMap<>(); - coveragePerLine.put(10, new Coverage.CoverageBuilder().setCovered(1).setMissed(0).build()); - coveragePerLine.put(11, new Coverage.CoverageBuilder().setCovered(0).setMissed(4).build()); - coveragePerLine.put(12, new Coverage.CoverageBuilder().setCovered(4).setMissed(0).build()); - coveragePerLine.put(13, new Coverage.CoverageBuilder().setCovered(0).setMissed(1).build()); - file.setCoveragePerLine(coveragePerLine); + private void attachCoveragePerLine(final FileNode file) { + var builder = new CoverageBuilder().setMetric(Metric.LINE); + file.addLineCoverage(10, builder.setCovered(1).setMissed(0).build()); + file.addLineCoverage(11, builder.setCovered(0).setMissed(4).build()); + file.addLineCoverage(12, builder.setCovered(4).setMissed(0).build()); + file.addLineCoverage(13, builder.setCovered(0).setMissed(1).build()); } /** - * Attaches indirect coverage changes to the passed {@link FileCoverageNode node}. + * Attaches indirect coverage changes to the passed {@link FileNode node}. * * @param file * The node to which information about indirect coverage changes should be added */ - private void attachIndirectCoverageChanges(final FileCoverageNode file) { + private void attachIndirectCoverageChanges(final FileNode file) { SortedMap indirectChanges = new TreeMap<>(); indirectChanges.put(10, 1); indirectChanges.put(11, -4); indirectChanges.put(12, 4); indirectChanges.put(13, -1); - file.setIndirectCoverageChanges(indirectChanges); + file.getIndirectCoverageChanges().putAll(indirectChanges); } /** - * Attaches changed code lines to the passed {@link FileCoverageNode node}. + * Attaches changed code lines to the passed {@link FileNode node}. * * @param file * The node to which information about changed code lines should be added */ - private void attachCodeChanges(final FileCoverageNode file) { + private void attachCodeChanges(final FileNode file) { SortedSet changes = new TreeSet<>(); changes.add(10); changes.add(11); changes.add(12); changes.add(13); - file.setChangedCodeLines(changes); + file.getChangedLines().addAll(changes); } } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageViewModelTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageViewModelTest.java index e41c1e333..28792f2f0 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageViewModelTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageViewModelTest.java @@ -1,15 +1,18 @@ package io.jenkins.plugins.coverage.model; +import java.util.NavigableMap; import java.util.NoSuchElementException; -import java.util.SortedMap; import java.util.TreeMap; import org.apache.commons.lang3.math.Fraction; import org.junit.jupiter.api.Test; -import hudson.model.Run; +import edu.hm.hafner.metric.Coverage; +import edu.hm.hafner.metric.Metric; +import edu.hm.hafner.metric.Node; +import edu.hm.hafner.metric.Value; -import io.jenkins.plugins.coverage.model.Coverage.CoverageBuilder; +import hudson.model.Run; import static io.jenkins.plugins.coverage.model.Assertions.*; import static io.jenkins.plugins.coverage.model.CoverageViewModel.*; @@ -54,27 +57,9 @@ void shouldReportOverview() { ); } - @Test - void shouldProvideChangeCoverage() { - CoverageNode node = createChangeCoverageNode(Fraction.ZERO, LINE, 1, 1); - CoverageViewModel model = createModelFromMock(node); - assertThat(model.hasChangeCoverage()).isTrue(); - - CoverageOverview overview = model.getChangeCoverageOverview(); - assertThatJson(overview).node("metrics").isArray().containsExactly( - "File", "Line", "Branch" - ); - assertThatJson(overview).node("covered").isArray().containsExactly( - 0, 0, 0 - ); - assertThatJson(overview).node("missed").isArray().containsExactly( - 0, 0, 0 - ); - } - @Test void shouldProvideIndirectCoverageChanges() { - CoverageNode node = createIndirectCoverageChangesNode(Fraction.ZERO, LINE, 1, 1); + Node node = createIndirectCoverageChangesNode(Fraction.ZERO, LINE, 1, 1); CoverageViewModel model = createModelFromMock(node); assertThat(model.hasIndirectCoverageChanges()).isTrue(); } @@ -91,25 +76,23 @@ void shouldProvideRightTableModelById() { } private CoverageViewModel createModel() { - return new CoverageViewModel(mock(Run.class), readNode("jacoco-codingstyle.xml")); + return new CoverageViewModel(mock(Run.class), readJacocoResult("jacoco-codingstyle.xml")); } /** - * Creates a {@link CoverageViewModel} which represents a mocked {@link CoverageNode}. + * Creates a {@link CoverageViewModel} which represents a mocked {@link Node}. * * @param mock * The mocked node * * @return the created model */ - private CoverageViewModel createModelFromMock(final CoverageNode mock) { - when(mock.filterPackageStructure()).thenReturn(mock); - - SortedMap changeMetricsDistribution = new TreeMap<>(); - changeMetricsDistribution.put(LINE, CoverageBuilder.NO_COVERAGE); - changeMetricsDistribution.put(BRANCH, CoverageBuilder.NO_COVERAGE); - changeMetricsDistribution.put(FILE, CoverageBuilder.NO_COVERAGE); - changeMetricsDistribution.put(PACKAGE, CoverageBuilder.NO_COVERAGE); + private CoverageViewModel createModelFromMock(final Node mock) { + NavigableMap changeMetricsDistribution = new TreeMap<>(); + changeMetricsDistribution.put(LINE, Coverage.nullObject(Metric.LINE)); + changeMetricsDistribution.put(BRANCH, Coverage.nullObject(Metric.BRANCH)); + changeMetricsDistribution.put(FILE, Coverage.nullObject(Metric.FILE)); + changeMetricsDistribution.put(PACKAGE, Coverage.nullObject(Metric.PACKAGE)); when(mock.getMetricsDistribution()).thenReturn(changeMetricsDistribution); return new CoverageViewModel(mock(Run.class), mock); diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageXmlStreamTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageXmlStreamTest.java deleted file mode 100644 index ec98b9fc6..000000000 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageXmlStreamTest.java +++ /dev/null @@ -1,164 +0,0 @@ -package io.jenkins.plugins.coverage.model; - -import java.nio.file.Path; -import java.util.NavigableMap; -import java.util.NavigableSet; -import java.util.TreeMap; -import java.util.TreeSet; - -import org.junit.jupiter.api.Test; - -import edu.hm.hafner.util.SerializableTest; - -import io.jenkins.plugins.coverage.adapter.JacocoReportAdapter; -import io.jenkins.plugins.coverage.adapter.JacocoReportAdapter.JacocoReportAdapterDescriptor; -import io.jenkins.plugins.coverage.exception.CoverageException; -import io.jenkins.plugins.coverage.model.Coverage.CoverageBuilder; -import io.jenkins.plugins.coverage.model.CoverageXmlStream.HitsMapConverter; -import io.jenkins.plugins.coverage.model.CoverageXmlStream.IntegerSetConverter; -import io.jenkins.plugins.coverage.model.CoverageXmlStream.LineMapConverter; -import io.jenkins.plugins.coverage.model.CoverageXmlStream.MetricPercentageMapConverter; -import io.jenkins.plugins.coverage.targets.CoverageElementRegister; -import io.jenkins.plugins.coverage.targets.CoverageResult; - -import static io.jenkins.plugins.coverage.model.Assertions.*; - -/** - * Tests the class {@link CoverageXmlStream}. - * - * @author Ullrich Hafner - */ -class CoverageXmlStreamTest extends SerializableTest { - private static final CoverageBuilder BUILDER = new CoverageBuilder(); - private static final String EMPTY = "[]"; - - @Override - protected CoverageNode createSerializable() { - // TODO: replace with actual result - try { - JacocoReportAdapter parser = new JacocoReportAdapter("unused"); - CoverageElementRegister.addCoverageElements(new JacocoReportAdapterDescriptor().getCoverageElements()); - CoverageResult result = parser.getResult(getResourceAsFile("jacoco-codingstyle.xml").toFile()); - result.stripGroup(); - - return new CoverageNodeConverter().convert(result); - } - catch (CoverageException exception) { - throw new AssertionError(exception); - } - } - - @Test - void shouldSaveAndRestoreTree() { - CoverageXmlStream xmlStream = new CoverageXmlStream(); - - Path saved = createTempFile(); - CoverageNode convertedNode = createSerializable(); - - xmlStream.write(saved, convertedNode); - CoverageNode restored = xmlStream.read(saved); - assertThat(restored).isEqualTo(convertedNode); - } - - @Test - void shouldConvertMap2String() { - NavigableMap map = new TreeMap<>(); - - LineMapConverter converter = new LineMapConverter(); - - assertThat(converter.marshal(map)).isEqualTo(EMPTY); - - map.put(10, BUILDER.setCovered(5).setTotal(8).build()); - assertThat(converter.marshal(map)).isEqualTo("[10: 5/8]"); - - map.put(20, BUILDER.setCovered(6).setTotal(6).build()); - assertThat(converter.marshal(map)).isEqualTo("[10: 5/8, 20: 6/6]"); - } - - @Test - void shouldConvertString2Map() { - LineMapConverter converter = new LineMapConverter(); - - assertThat(converter.unmarshal(EMPTY)).isEmpty(); - Coverage first = BUILDER.setCovered(5).setTotal(8).build(); - assertThat(converter.unmarshal("[10: 5/8]")) - .containsExactly(entry(10, first)); - assertThat(converter.unmarshal("[10: 5/8, 20: 6/6]")) - .containsExactly(entry(10, first), entry(20, BUILDER.setCovered(6).setTotal(6).build())); - } - - @Test - void shouldConvertMetricMap2String() { - NavigableMap map = new TreeMap<>(); - - MetricPercentageMapConverter converter = new MetricPercentageMapConverter(); - - assertThat(converter.marshal(map)).isEqualTo(EMPTY); - - map.put(CoverageMetric.BRANCH, CoveragePercentage.valueOf(50, 1)); - assertThat(converter.marshal(map)).isEqualTo("[Branch: 50/1]"); - - map.put(CoverageMetric.LINE, CoveragePercentage.valueOf(3, 4)); - assertThat(converter.marshal(map)).isEqualTo("[Line: 3/4, Branch: 50/1]"); - } - - @Test - void shouldConvertString2MetricMap() { - MetricPercentageMapConverter converter = new MetricPercentageMapConverter(); - - assertThat(converter.unmarshal(EMPTY)).isEmpty(); - CoveragePercentage first = CoveragePercentage.valueOf(50, 1); - assertThat(converter.unmarshal("[Branch: 50/1]")) - .containsExactly(entry(CoverageMetric.BRANCH, first)); - assertThat(converter.unmarshal("[Line: 3/4, Branch: 50/1]")) - .containsExactly(entry(CoverageMetric.LINE, CoveragePercentage.valueOf(3, 4)), entry(CoverageMetric.BRANCH, first)); - } - - @Test - void shouldConvertIntegerMap2String() { - NavigableMap map = new TreeMap<>(); - - HitsMapConverter converter = new HitsMapConverter(); - - assertThat(converter.marshal(map)).isEqualTo(EMPTY); - - map.put(10, 20); - assertThat(converter.marshal(map)).isEqualTo("[10: 20]"); - - map.put(15, 25); - assertThat(converter.marshal(map)).isEqualTo("[10: 20, 15: 25]"); - } - - @Test - void shouldConvertString2IntegerMap() { - HitsMapConverter converter = new HitsMapConverter(); - - assertThat(converter.unmarshal(EMPTY)).isEmpty(); - assertThat(converter.unmarshal("[15: 25]")).containsExactly(entry(15, 25)); - assertThat(converter.unmarshal("[15:25, 10: 20]")).containsExactly(entry(10, 20), entry(15, 25)); - } - - @Test - void shouldConvertIntegerSet2String() { - NavigableSet set = new TreeSet<>(); - - IntegerSetConverter converter = new IntegerSetConverter(); - - assertThat(converter.marshal(set)).isEqualTo(EMPTY); - - set.add(10); - assertThat(converter.marshal(set)).isEqualTo("[10]"); - - set.add(15); - assertThat(converter.marshal(set)).isEqualTo("[10, 15]"); - } - - @Test - void shouldConvertString2IntegerSet() { - IntegerSetConverter converter = new IntegerSetConverter(); - - assertThat(converter.unmarshal(EMPTY)).isEmpty(); - assertThat(converter.unmarshal("[15]")).containsExactly(15); - assertThat(converter.unmarshal("[15, 20]")).containsExactly(15, 20); - } -} diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeclarativePipelineSupportITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeclarativePipelineSupportITest.java index 0da3e6e07..a5bc25899 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeclarativePipelineSupportITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeclarativePipelineSupportITest.java @@ -2,6 +2,9 @@ import org.junit.jupiter.api.Test; +import edu.hm.hafner.metric.Coverage.CoverageBuilder; +import edu.hm.hafner.metric.Metric; + import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; import org.jenkinsci.plugins.workflow.job.WorkflowJob; import hudson.model.Run; @@ -38,7 +41,7 @@ void declarativePipelineSupportJacoco() { + "}", true)); Run build = buildSuccessfully(job); assertThat(build.getAction(CoverageBuildAction.class).getLineCoverage()) - .isEqualTo(new Coverage.CoverageBuilder().setCovered(6083).setMissed(6368 - 6083).build()); + .isEqualTo(new CoverageBuilder().setMetric(Metric.LINE).setCovered(6083).setMissed(6368 - 6083).build()); } } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeltaComputationVsReferenceBuildITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeltaComputationVsReferenceBuildITest.java index a7c0786f9..a94926577 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeltaComputationVsReferenceBuildITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeltaComputationVsReferenceBuildITest.java @@ -3,8 +3,12 @@ import java.util.AbstractMap.SimpleEntry; import java.util.Collections; +import org.apache.commons.lang3.math.Fraction; import org.junit.jupiter.api.Test; +import edu.hm.hafner.metric.FileNode; +import edu.hm.hafner.metric.Node; + import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; import org.jenkinsci.plugins.workflow.job.WorkflowJob; import hudson.model.FreeStyleProject; @@ -14,8 +18,8 @@ import io.jenkins.plugins.coverage.adapter.JacocoReportAdapter; import io.jenkins.plugins.util.IntegrationTestWithJenkinsPerSuite; +import static edu.hm.hafner.metric.Metric.*; import static io.jenkins.plugins.coverage.model.Assertions.*; -import static io.jenkins.plugins.coverage.model.CoverageMetric.*; /** * Integration test for delta computation of reference builds. @@ -89,11 +93,11 @@ private void verifyDeltaComputation(final Run firstBuild, final Run .isPresent() .satisfies(reference -> assertThat(reference.get()).isEqualTo(firstBuild)); - assertThat(coverageBuildAction.getDifference()).contains( - new SimpleEntry<>(LINE, CoveragePercentage.valueOf(-2_315_425, 514_216)), - new SimpleEntry<>(BRANCH, CoveragePercentage.valueOf(11_699, 2175)), - new SimpleEntry<>(INSTRUCTION, CoveragePercentage.valueOf(-235_580, 81_957)), - new SimpleEntry<>(METHOD, CoveragePercentage.valueOf(-217_450, 94_299)) + assertThat(coverageBuildAction.getDelta()).contains( + new SimpleEntry<>(LINE, Fraction.getFraction(-2_315_425, 514_216)), + new SimpleEntry<>(BRANCH, Fraction.getFraction(11_699, 2175)), + new SimpleEntry<>(INSTRUCTION, Fraction.getFraction(-235_580, 81_957)), + new SimpleEntry<>(METHOD, Fraction.getFraction(-217_450, 94_299)) ); verifyChangeCoverage(coverageBuildAction); @@ -120,12 +124,8 @@ private void verifyChangeCoverage(final CoverageBuildAction action) { * The created Jenkins action */ private void verifyCodeDelta(final CoverageBuildAction action) { - CoverageNode root = action.getResult(); + Node root = action.getResult(); assertThat(root).isNotNull(); - - assertThat(root.getAllFileCoverageNodes()).flatExtracting(FileCoverageNode::getChangedCodeLines).isEmpty(); - - assertThat(root).hasFileAmountWithChangedCoverage(0); - assertThat(root).hasLineAmountWithChangedCoverage(0); + assertThat(root.getAllFileNodes()).flatExtracting(FileNode::getChangedLines).isEmpty(); } } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerAndSourceCodeRenderingITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerAndSourceCodeRenderingITest.java index 3735b5873..6ef48aa3a 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerAndSourceCodeRenderingITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerAndSourceCodeRenderingITest.java @@ -8,6 +8,10 @@ import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; +import edu.hm.hafner.metric.Coverage.CoverageBuilder; +import edu.hm.hafner.metric.Metric; +import edu.hm.hafner.metric.ModuleNode; + import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; import org.jenkinsci.plugins.workflow.job.WorkflowJob; import hudson.model.FreeStyleProject; @@ -66,11 +70,11 @@ void shouldCopyAndRenderSourceCodeAndRenderingInFreestyleJobOnAgent() throws IOE CoverageBuildAction coverageResult = build.getAction(CoverageBuildAction.class); assertThat(coverageResult.getLineCoverage()) - .isEqualTo(new Coverage.CoverageBuilder().setCovered(6083).setMissed(6368 - 6083).build()); + .isEqualTo(new CoverageBuilder().setMetric(Metric.LINE).setCovered(6083).setMissed(6368 - 6083).build()); } private void verifySourceCode(final Run build) { - CoverageNode root = new CoverageNode(CoverageMetric.MODULE, "top-level"); + ModuleNode root = new ModuleNode("top-level"); CoverageBuildAction action = new CoverageBuildAction(build, root, new HealthReport(), "-", new TreeMap<>(), new TreeMap<>(), new TreeMap<>(), new TreeMap<>(), false); diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/FileChangesProcessorTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/FileChangesProcessorTest.java index 508384fd5..71464952b 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/FileChangesProcessorTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/FileChangesProcessorTest.java @@ -8,6 +8,9 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import edu.hm.hafner.metric.FileNode; +import edu.hm.hafner.metric.Node; + import io.jenkins.plugins.forensics.delta.model.Change; import io.jenkins.plugins.forensics.delta.model.ChangeEditType; import io.jenkins.plugins.forensics.delta.model.FileChanges; @@ -21,7 +24,6 @@ * @author Florian Orendi */ class FileChangesProcessorTest extends AbstractCoverageTest { - private static final String TEST_FILE_1 = "Test1.java"; private static final String TEST_FILE_2 = "Main.java"; private static final String TEST_FILE_1_PATH = "test/example/" + TEST_FILE_1; @@ -76,59 +78,34 @@ static void initFileChanges() { @Test void shouldAttachChangesCodeLines() { FileChangesProcessor fileChangesProcessor = createFileChangesProcessor(); - CoverageNode tree = readCoverageTree(TEST_REPORT_AFTER); + Node tree = readJacocoResult(TEST_REPORT_AFTER); fileChangesProcessor.attachChangedCodeLines(tree, CODE_CHANGES); assertThat(tree.findByHashCode(FILE, TEST_FILE_1_PATH.hashCode())) .isNotEmpty() - .satisfies(node -> { - assertThat(node.get()).isInstanceOf(FileCoverageNode.class); - assertThat(((FileCoverageNode) node.get()).getChangedCodeLines()).containsExactly( - 5, 6, 7, 8, 9, 14, 15, 16, 17, 18, 20, 21, 22, 33, 34, 35, 36 - ); - }); + .satisfies(node -> assertThat(node.get()) + .isInstanceOfSatisfying(FileNode.class, f -> assertThat(f.getChangedLines()) + .containsExactly( + 5, 6, 7, 8, 9, 14, 15, 16, 17, 18, 20, 21, 22, 33, 34, 35, 36))); assertThat(tree.findByHashCode(FILE, TEST_FILE_2.hashCode())) .isNotEmpty() - .satisfies(node -> { - assertThat(node.get()).isInstanceOf(FileCoverageNode.class); - assertThat(((FileCoverageNode) node.get()).getChangedCodeLines()).isEmpty(); - }); + .satisfies(node -> assertThat(node.get()) + .isInstanceOfSatisfying(FileNode.class, f -> assertThat(f.getChangedLines()) + .isEmpty())); } @Test void shouldAttachFileCoverageDelta() { FileChangesProcessor fileChangesProcessor = createFileChangesProcessor(); - CoverageNode reference = readCoverageTree(TEST_REPORT_BEFORE); - CoverageNode tree = readCoverageTree(TEST_REPORT_AFTER); + Node reference = readJacocoResult(TEST_REPORT_BEFORE); + Node tree = readJacocoResult(TEST_REPORT_AFTER); fileChangesProcessor.attachFileCoverageDeltas(tree, reference, OLD_PATH_MAPPING); assertThat(tree.findByHashCode(FILE, TEST_FILE_1_PATH.hashCode())) .isNotEmpty() .satisfies(node -> { - assertThat(node.get()).isInstanceOf(FileCoverageNode.class); - verifyFileCoverageDeltaOfTestFile1((FileCoverageNode) node.get()); - }); - } - - // test to prevent issue #487 https://github.com/jenkinsci/code-coverage-api-plugin/issues/487 - @Test - void shouldNotAttachFileCoverageDeltasWithMissingReferences() { - FileChangesProcessor fileChangesProcessor = createFileChangesProcessor(); - CoverageNode reference = readCoverageTree(TEST_REPORT_BEFORE); - CoverageNode tree = readCoverageTree(TEST_REPORT_AFTER); - - // simulating a file that has not been part of the reference coverage report, but is part of the code delta - assertThat(reference.findByHashCode(FILE, TEST_FILE_1_PATH_OLD.hashCode())).isNotEmpty(); - reference.findByHashCode(FILE, TEST_FILE_1_PATH_OLD.hashCode()).ifPresent(CoverageNode::remove); - assertThat(reference.findByHashCode(FILE, TEST_FILE_1_PATH_OLD.hashCode())).isEmpty(); - - // verifies that the method does not process the delta calculation for the missing reference node - fileChangesProcessor.attachFileCoverageDeltas(tree, reference, OLD_PATH_MAPPING); - assertThat(tree.findByHashCode(FILE, TEST_FILE_1_PATH.hashCode())) - .isNotEmpty() - .satisfies(node -> { - assertThat(node.get()).isInstanceOf(FileCoverageNode.class); - assertThat(((FileCoverageNode) node.get()).hasFileCoverageDelta(FILE)).isFalse(); + assertThat(node.get()).isInstanceOf(FileNode.class); + verifyFileCoverageDeltaOfTestFile1((FileNode) node.get()); }); } @@ -136,36 +113,30 @@ void shouldNotAttachFileCoverageDeltasWithMissingReferences() { * Verifies the file coverage delta of {@link #TEST_FILE_1}. * * @param file - * The referencing coverage tree {@link FileCoverageNode node} + * The referencing coverage tree {@link FileNode node} */ - private void verifyFileCoverageDeltaOfTestFile1(final FileCoverageNode file) { + private void verifyFileCoverageDeltaOfTestFile1(final FileNode file) { assertThat(file.getName()).isEqualTo(TEST_FILE_1); - assertThat(file.getFileCoverageDeltaForMetric(LINE)).isEqualTo( - CoveragePercentage.valueOf(Fraction.getFraction(3, 117))); - assertThat(file.getFileCoverageDeltaForMetric(BRANCH)).isEqualTo( - CoveragePercentage.valueOf(Fraction.getFraction(3, 24))); - assertThat(file.getFileCoverageDeltaForMetric(INSTRUCTION)).isEqualTo( - CoveragePercentage.valueOf(Fraction.getFraction(90, 999))); - assertThat(file.getFileCoverageDeltaForMetric(METHOD)).isEqualTo( - CoveragePercentage.valueOf(Fraction.getFraction(-4, 30))); - assertThat(file.getFileCoverageDeltaForMetric(CLASS)).isEqualTo( - CoveragePercentage.valueOf(Fraction.ZERO)); - assertThat(file.getFileCoverageDeltaForMetric(FILE)).isEqualTo( - CoveragePercentage.valueOf(Fraction.ZERO)); + assertThat(file.getChangeCoverage(LINE)).isEqualTo(Fraction.getFraction(3, 117)); + assertThat(file.getChangeCoverage(BRANCH)).isEqualTo(Fraction.getFraction(3, 24)); + assertThat(file.getChangeCoverage(INSTRUCTION)).isEqualTo(Fraction.getFraction(90, 999)); + assertThat(file.getChangeCoverage(METHOD)).isEqualTo(Fraction.getFraction(-4, 30)); + assertThat(file.getChangeCoverage(CLASS)).isEqualTo(Fraction.ZERO); + assertThat(file.getChangeCoverage(FILE)).isEqualTo(Fraction.ZERO); } @Test void shouldAttachIndirectCoverageChanges() { FileChangesProcessor fileChangesProcessor = createFileChangesProcessor(); - CoverageNode reference = readCoverageTree(TEST_REPORT_BEFORE); - CoverageNode tree = readCoverageTree(TEST_REPORT_AFTER); + Node reference = readJacocoResult(TEST_REPORT_BEFORE); + Node tree = readJacocoResult(TEST_REPORT_AFTER); fileChangesProcessor.attachIndirectCoveragesChanges(tree, reference, CODE_CHANGES, OLD_PATH_MAPPING); assertThat(tree.findByHashCode(FILE, TEST_FILE_1_PATH.hashCode())) .isNotEmpty() .satisfies(node -> { - assertThat(node.get()).isInstanceOf(FileCoverageNode.class); - FileCoverageNode file = (FileCoverageNode) node.get(); + assertThat(node.get()).isInstanceOf(FileNode.class); + FileNode file = (FileNode) node.get(); assertThat(file.getIndirectCoverageChanges()).containsExactly( new SimpleEntry<>(11, -1), new SimpleEntry<>(29, -1), @@ -174,20 +145,6 @@ void shouldAttachIndirectCoverageChanges() { }); } - /** - * Reads the coverage tree from a report. - * - * @param file - * The name of the report - * - * @return the {@link CoverageNode} root of the tree - */ - private CoverageNode readCoverageTree(final String file) { - CoverageNode root = readNode(file); - root.splitPackages(); - return root; - } - /** * Creates an instance of {@link FileChangesProcessor}. * diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/FileCoverageNodeTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/FileCoverageNodeTest.java deleted file mode 100644 index cfb09e256..000000000 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/FileCoverageNodeTest.java +++ /dev/null @@ -1,92 +0,0 @@ -package io.jenkins.plugins.coverage.model; - -import java.util.AbstractMap; -import java.util.AbstractMap.SimpleEntry; - -import org.junit.jupiter.api.Test; - -import nl.jqno.equalsverifier.EqualsVerifier; -import nl.jqno.equalsverifier.Warning; - -import static org.assertj.core.api.Assertions.*; - -/** - * Test class for {@link FileCoverageNode}. - * - * @author Florian Orendi - */ -class FileCoverageNodeTest { - - private static final String PATH = "path"; - private static final int LINE = 5; - private static final int HIT_DELTA = 10; - private static final Coverage COVERAGE = new Coverage.CoverageBuilder().setCovered(2).setMissed(3).build(); - private static final CoveragePercentage COVERAGE_DELTA = CoveragePercentage.valueOf(0.5); - private static final CoverageMetric COVERAGE_METRIC = CoverageMetric.LINE; - - @Test - void shouldHaveWorkingGetters() { - FileCoverageNode node = createFileCoverageNode(); - - assertThat(node.getPath()).isEqualTo(PATH); - - assertThat(node.hasFileCoverageDelta(COVERAGE_METRIC)).isTrue(); - assertThat(node.getFileCoverageDeltaForMetric(COVERAGE_METRIC)).isEqualTo(COVERAGE_DELTA); - - assertThat(node.getChangedCodeLines()).containsExactly(LINE); - assertThat(node.getCoveragePerLine()).containsExactly(new AbstractMap.SimpleEntry<>(LINE, COVERAGE)); - assertThat(node.getIndirectCoverageChanges()).containsExactly(new AbstractMap.SimpleEntry<>(LINE, HIT_DELTA)); - } - - @Test - void shouldCopyTree() { - FileCoverageNode node = createFileCoverageNode(); - CoverageNode root = node.getParent().copyTree(); - assertThat(root.getChildren()).containsExactly(node); - assertThat(root.getChildren().get(0)) - .isInstanceOf(FileCoverageNode.class) - .satisfies(n -> { - FileCoverageNode fileNode = (FileCoverageNode) n; - assertThat(fileNode.getPath()).isEqualTo(PATH); - assertThat(fileNode.getCoveragePerLine()).containsExactly(new SimpleEntry<>(LINE, COVERAGE)); - assertThat(fileNode.getFileCoverageDeltaForMetric(COVERAGE_METRIC)).isEqualTo(COVERAGE_DELTA); - assertThat(fileNode.getChangedCodeLines()).containsExactly(LINE); - assertThat(fileNode.getIndirectCoverageChanges()).containsExactly( - new SimpleEntry<>(LINE, HIT_DELTA)); - }); - } - - @Test - void shouldObeyEqualsContract() { - EqualsVerifier.forClass(FileCoverageNode.class) - .withPrefabValues(CoverageNode.class, - new FileCoverageNode("", "file.txt"), - new CoverageNode(CoverageMetric.LINE, "line")) - .suppress(Warning.NONFINAL_FIELDS) - .usingGetClass() - .withIgnoredFields("parent") - .verify(); - } - - /** - * Creates an instance of {@link FileCoverageNode}. - * - * @return the created instance - */ - private FileCoverageNode createFileCoverageNode() { - CoverageNode parent = new CoverageNode(CoverageMetric.MODULE, ""); - CoverageLeaf leaf = new CoverageLeaf(COVERAGE_METRIC, COVERAGE); - - FileCoverageNode fileCoverageNode = new FileCoverageNode("", PATH); - fileCoverageNode.setParent(parent); - fileCoverageNode.add(leaf); - parent.add(fileCoverageNode); - - fileCoverageNode.putCoveragePerLine(LINE, COVERAGE); - fileCoverageNode.putFileCoverageDelta(COVERAGE_METRIC, COVERAGE_DELTA); - fileCoverageNode.putIndirectCoverageChange(LINE, HIT_DELTA); - fileCoverageNode.addChangedCodeLine(LINE); - - return fileCoverageNode; - } -} diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/FilePathValidatorTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/FilePathValidatorTest.java index 36fe62263..99234150b 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/FilePathValidatorTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/FilePathValidatorTest.java @@ -2,6 +2,12 @@ import org.junit.jupiter.api.Test; +import edu.hm.hafner.metric.ContainerNode; +import edu.hm.hafner.metric.FileNode; +import edu.hm.hafner.metric.Metric; +import edu.hm.hafner.metric.ModuleNode; +import edu.hm.hafner.metric.Node; +import edu.hm.hafner.metric.PackageNode; import edu.hm.hafner.util.FilteredLog; import static io.jenkins.plugins.coverage.model.FilePathValidator.*; @@ -13,11 +19,10 @@ * @author Florian Orendi */ class FilePathValidatorTest extends AbstractCoverageTest { - @Test void shouldAcceptValidFileStructure() { - CoverageNode root = readExampleReport(); - CoverageNode rootCopy = root.copyTree(); + Node root = readExampleReport(); + Node rootCopy = root.copyTree(); FilteredLog log = createLog(); verifyPathUniqueness(root, log); @@ -28,17 +33,15 @@ void shouldAcceptValidFileStructure() { @Test void shouldCorrectInvalidFileStructure() { - CoverageNode root = createMultiModuleReportWithDuplicates(); + Node root = createMultiModuleReportWithDuplicates(); FilteredLog log = createLog(); verifyPathUniqueness(root, log); - assertThat(root.getAll(CoverageMetric.PACKAGE)) - .hasSize(1) - .satisfies(node -> assertThat(node.get(0).getName()).isEqualTo("package")); - assertThat(root.getAll(CoverageMetric.FILE)) - .hasSize(1) - .satisfies(node -> assertThat(node.get(0).getName()).isEqualTo("file2")); + assertThat(root.getAll(Metric.PACKAGE)) + .hasSize(2); + assertThat(root.getAll(Metric.FILE)) + .hasSize(3); assertThat(log.getErrorMessages()).contains( AMBIGUOUS_FILES_MESSAGE + System.lineSeparator() + "package/file", REMOVED_MESSAGE, @@ -58,24 +61,26 @@ private FilteredLog createLog() { /** * Creates a coverage tree for a multi-module project with files with duplicate fully qualified names. * - * @return the {@link CoverageNode root} of the tree + * @return the {@link Node root} of the tree */ - private CoverageNode createMultiModuleReportWithDuplicates() { - CoverageNode root = new CoverageNode(CoverageMetric.MODULE, "root"); - CoverageNode module1 = new CoverageNode(CoverageMetric.MODULE, "module1"); - CoverageNode module2 = new CoverageNode(CoverageMetric.MODULE, "module2"); - CoverageNode packageName = new PackageCoverageNode("package"); - CoverageNode packageCopy = packageName.copyEmpty(); - CoverageNode file = new FileCoverageNode("file", "file"); - CoverageNode file2 = new FileCoverageNode("file2", "file2"); - CoverageNode fileCopy = file.copyEmpty(); - root.add(module1); - root.add(module2); - module1.add(packageName); - module2.add(packageCopy); - packageName.add(file); - packageName.add(file2); - packageCopy.add(fileCopy); + private Node createMultiModuleReportWithDuplicates() { + Node root = new ContainerNode("root"); + + Node module1 = new ModuleNode("module1"); + root.addChild(module1); + Node packageName = new PackageNode("package"); + module1.addChild(packageName); + Node file = new FileNode("file"); + packageName.addChild(file); + Node file2 = new FileNode("file2"); + packageName.addChild(file2); + + Node module2 = new ModuleNode("module2"); + root.addChild(module2); + Node packageCopy = packageName.copy(); + module2.addChild(packageCopy); + Node fileCopy = file.copy(); + packageCopy.addChild(fileCopy); return root; } @@ -83,9 +88,9 @@ private CoverageNode createMultiModuleReportWithDuplicates() { /** * Reads the coverage tree from the report 'jacoco-codingstyle.xml'. * - * @return the {@link CoverageNode} root of the tree + * @return the {@link Node} root of the tree */ - private CoverageNode readExampleReport() { - return readNode("jacoco-codingstyle.xml"); + private Node readExampleReport() { + return readJacocoResult("jacoco-codingstyle.xml"); } } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/GitForensicsITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/GitForensicsITest.java index 082d01124..d3f1439da 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/GitForensicsITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/GitForensicsITest.java @@ -6,10 +6,14 @@ import java.util.List; import java.util.stream.Collectors; +import org.apache.commons.lang3.math.Fraction; import org.junit.jupiter.api.Test; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; +import edu.hm.hafner.metric.Coverage; +import edu.hm.hafner.metric.FileNode; + import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; import org.jenkinsci.plugins.workflow.flow.FlowDefinition; import org.jenkinsci.plugins.workflow.job.WorkflowJob; @@ -24,8 +28,8 @@ import io.jenkins.plugins.coverage.adapter.JacocoReportAdapter; import io.jenkins.plugins.util.IntegrationTestWithJenkinsPerSuite; +import static edu.hm.hafner.metric.Metric.*; import static io.jenkins.plugins.coverage.model.Assertions.*; -import static io.jenkins.plugins.coverage.model.CoverageMetric.*; import static org.assertj.core.api.Assumptions.*; /** @@ -151,16 +155,16 @@ private void verifyCoverage(final CoverageBuildAction action) { */ private void verifyOverallCoverage(final CoverageBuildAction action) { assertThat(action.getLineCoverage()).satisfies(coverage -> { - assertThat(coverage).hasCovered(546); - assertThat(coverage).hasMissed(461); + assertThat(coverage.getCovered()).isEqualTo(546); + assertThat(coverage.getMissed()).isEqualTo(461); }); assertThat(action.getBranchCoverage()).satisfies(coverage -> { - assertThat(coverage).hasCovered(136); - assertThat(coverage).hasMissed(94); + assertThat(coverage.getCovered()).isEqualTo(136); + assertThat(coverage.getMissed()).isEqualTo(94); }); - assertThat(action.getDifference()).contains( - new SimpleEntry<>(LINE, CoveragePercentage.valueOf(65_160, 103_721)), - new SimpleEntry<>(BRANCH, CoveragePercentage.valueOf(0, 1)) + assertThat(action.getDelta()).contains( + new SimpleEntry<>(LINE, Fraction.getFraction(65_160, 103_721)), + new SimpleEntry<>(BRANCH, Fraction.getFraction(0, 1)) ); } @@ -171,17 +175,17 @@ private void verifyOverallCoverage(final CoverageBuildAction action) { * The created Jenkins action */ private void verifyChangeCoverage(final CoverageBuildAction action) { - assertThat(action.getChangeCoverage(LINE)).satisfies(coverage -> { - assertThat(coverage).hasCovered(1); - assertThat(coverage).hasMissed(1); + assertThat(action.getChangeCoverage(LINE)).isInstanceOfSatisfying(Coverage.class, coverage -> { + assertThat(coverage.getCovered()).isEqualTo(1); + assertThat(coverage.getMissed()).isEqualTo(1); }); - assertThat(action.getChangeCoverage(BRANCH)).satisfies(coverage -> { - assertThat(coverage).hasCovered(0); - assertThat(coverage).hasMissed(0); + assertThat(action.getChangeCoverage(BRANCH)).isInstanceOfSatisfying(Coverage.class, coverage -> { + assertThat(coverage.getCovered()).isEqualTo(0); + assertThat(coverage.getMissed()).isEqualTo(0); }); assertThat(action.getChangeCoverageDifference(LINE)).satisfies(coverage -> { - assertThat(coverage).hasNumerator(-4250); - assertThat(coverage).hasDenominator(1007); + assertThat(coverage.getNumerator()).isEqualTo(-4250); + assertThat(coverage.getDenominator()).isEqualTo(1007); }); assertThat(action.hasChangeCoverageDifference(BRANCH)).isFalse(); } @@ -193,9 +197,9 @@ private void verifyChangeCoverage(final CoverageBuildAction action) { * The created Jenkins action */ private void verifyIndirectCoverageChanges(final CoverageBuildAction action) { - assertThat(action.getIndirectCoverageChanges(LINE)).satisfies(coverage -> { - assertThat(coverage).hasCovered(4); - assertThat(coverage).hasMissed(0); + assertThat(action.getIndirectCoverageChanges(LINE)).isInstanceOfSatisfying(Coverage.class, coverage -> { + assertThat(coverage.getCovered()).isEqualTo(4); + assertThat(coverage.getMissed()).isEqualTo(0); }); assertThat(action.hasIndirectCoverageChanges(BRANCH)).isFalse(); } @@ -207,23 +211,18 @@ private void verifyIndirectCoverageChanges(final CoverageBuildAction action) { * The created Jenkins action */ private void verifyCodeDelta(final CoverageBuildAction action) { - CoverageNode root = action.getResult(); + edu.hm.hafner.metric.Node root = action.getResult(); assertThat(root).isNotNull(); - List changedFiles = root.getAllFileCoverageNodes().stream() - .filter(fileNode -> !fileNode.getChangedCodeLines().isEmpty()) + List changedFiles = root.getAllFileNodes().stream() + .filter(FileNode::hasCodeChanges) .collect(Collectors.toList()); assertThat(changedFiles).hasSize(4); - assertThat(changedFiles).extracting(FileCoverageNode::getName) + assertThat(changedFiles).extracting(FileNode::getName) .containsExactly("MinerFactory.java", "RepositoryMinerStep.java", "SimpleReferenceRecorder.java", "CommitDecoratorFactory.java"); - assertThat(changedFiles).flatExtracting(FileCoverageNode::getChangedCodeLines) + assertThat(changedFiles).flatExtracting(FileNode::getChangedLines) .containsExactlyInAnyOrder(15, 17, 63, 68, 80, 90, 130); - - assertThat(root).hasFileAmountWithChangedCoverage(2); - assertThat(root).hasFileAmountWithIndirectCoverageChanges(1); - assertThat(root).hasLineAmountWithChangedCoverage(2); - assertThat(root).hasLineAmountWithIndirectCoverageChanges(4); } /** diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/JobDslITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/JobDslITest.java index 1f812e94b..7ae565bf2 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/JobDslITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/JobDslITest.java @@ -2,6 +2,8 @@ import org.junit.jupiter.api.Test; +import edu.hm.hafner.metric.Metric; + import hudson.model.View; import hudson.views.ListViewColumn; @@ -37,7 +39,7 @@ void shouldCreateFreestyleJobUsingJobDslAndVerifyIssueRecorderWithDefaultConfigu .isInstanceOfSatisfying(CoverageColumn.class, c -> { assertThat(c).hasColumnCaption(Messages.Coverage_Column()) - .hasCoverageMetric(CoverageMetric.LINE.getName()); + .hasCoverageMetric(Metric.LINE.name()); }); } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/MethodCoverageNodeTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/MethodCoverageNodeTest.java deleted file mode 100644 index 91c2afe36..000000000 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/MethodCoverageNodeTest.java +++ /dev/null @@ -1,68 +0,0 @@ -package io.jenkins.plugins.coverage.model; - -import org.junit.jupiter.api.Test; - -import nl.jqno.equalsverifier.EqualsVerifier; -import nl.jqno.equalsverifier.Warning; - -import io.jenkins.plugins.coverage.model.Coverage.CoverageBuilder; - -import static org.assertj.core.api.Assertions.*; - -/** - * Test class for {@link MethodCoverageNode}. - * - * @author Florian Orendi - */ -class MethodCoverageNodeTest { - - private static final String NAME = "METHOD"; - private static final int LINE = 5; - - @Test - void shouldGetValidLineNumberOfMethod() { - MethodCoverageNode node = createMethodCoverageNode(); - assertThat(node.hasValidLineNumber()).isTrue(); - assertThat(node.getLineNumber()).isEqualTo(5); - } - - @Test - void shouldRecognizeInvalidLineNumberOfMethod() { - MethodCoverageNode node = new MethodCoverageNode(NAME, 0); - assertThat(node.hasValidLineNumber()).isFalse(); - } - - @Test - void shouldCopyTree() { - MethodCoverageNode node = createMethodCoverageNode(); - CoverageNode root = node.getParent().copyTree(); - assertThat(root.getChildren()).containsExactly(node); - } - - @Test - void shouldObeyEqualsContract() { - EqualsVerifier.forClass(MethodCoverageNode.class) - .withPrefabValues(CoverageNode.class, - new MethodCoverageNode("", LINE), - new CoverageNode(CoverageMetric.LINE, "line")) - .suppress(Warning.NONFINAL_FIELDS) - .usingGetClass() - .withIgnoredFields("parent") - .verify(); - } - - /** - * Creates an instance of {@link MethodCoverageNode}. - * - * @return the created instance - */ - private MethodCoverageNode createMethodCoverageNode() { - CoverageNode parent = new CoverageNode(CoverageMetric.LINE, ""); - CoverageLeaf leaf = new CoverageLeaf(CoverageMetric.LINE, CoverageBuilder.NO_COVERAGE); - MethodCoverageNode node = new MethodCoverageNode(NAME, LINE); - node.setParent(parent); - parent.add(node); - node.add(leaf); - return node; - } -} diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/MultipleInvocationsOfStepITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/MultipleInvocationsOfStepITest.java index 98546ba33..e8ab97530 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/MultipleInvocationsOfStepITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/MultipleInvocationsOfStepITest.java @@ -5,6 +5,10 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import edu.hm.hafner.metric.Coverage; +import edu.hm.hafner.metric.Coverage.CoverageBuilder; +import edu.hm.hafner.metric.Metric; + import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; import org.jenkinsci.plugins.workflow.job.WorkflowJob; import hudson.model.Result; @@ -39,7 +43,7 @@ void withoutTagFirstHigherFile() { List buildAction = getCoverageBuildActions(job, 1); assertThat(buildAction.get(0).getBranchCoverage()) - .isEqualTo(new Coverage.CoverageBuilder().setCovered(JACOCO_LOWER_BRANCH_COVERAGE_COVERED_VALUE) + .isEqualTo(new CoverageBuilder().setMetric(Metric.BRANCH).setCovered(JACOCO_LOWER_BRANCH_COVERAGE_COVERED_VALUE) .setMissed(JACOCO_LOWER_BRANCH_COVERAGE_MISSED_VALUE) .build()); diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/PackageCoverageNodeTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/PackageCoverageNodeTest.java deleted file mode 100644 index 629cacdfe..000000000 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/PackageCoverageNodeTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package io.jenkins.plugins.coverage.model; - -import org.junit.jupiter.api.Test; - -import nl.jqno.equalsverifier.EqualsVerifier; -import nl.jqno.equalsverifier.Warning; - -import io.jenkins.plugins.coverage.model.Coverage.CoverageBuilder; - -import static org.assertj.core.api.Assertions.*; - -/** - * Test class for {@link PackageCoverageNode}. - * - * @author Florian Orendi - */ -class PackageCoverageNodeTest { - - private static final String PACKAGE = "test.path"; - private static final String PATH = "test/path"; - - @Test - void shouldGetPath() { - PackageCoverageNode node = createPackageCoverageNode(); - assertThat(node.getPath()).isEqualTo(PATH); - } - - @Test - void shouldCopyTree() { - PackageCoverageNode node = createPackageCoverageNode(); - CoverageNode root = node.getParent().copyTree(); - assertThat(root.getChildren()).containsExactly(node); - } - - @Test - void shouldObeyEqualsContract() { - EqualsVerifier.forClass(PackageCoverageNode.class) - .withPrefabValues(CoverageNode.class, - new PackageCoverageNode(PACKAGE), - new CoverageNode(CoverageMetric.FILE, "file.txt")) - .suppress(Warning.NONFINAL_FIELDS) - .usingGetClass() - .withIgnoredFields("parent") - .verify(); - } - - /** - * Creates an instance of {@link PackageCoverageNode}. - * - * @return the created instance - */ - private PackageCoverageNode createPackageCoverageNode() { - CoverageNode parent = new CoverageNode(CoverageMetric.MODULE, ""); - CoverageLeaf leaf = new CoverageLeaf(CoverageMetric.LINE, CoverageBuilder.NO_COVERAGE); - PackageCoverageNode node = new PackageCoverageNode(PACKAGE); - node.setParent(parent); - parent.add(node); - node.add(leaf); - return node; - } -} diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/SafeFractionTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/SafeFractionTest.java deleted file mode 100644 index 24f70fc09..000000000 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/SafeFractionTest.java +++ /dev/null @@ -1,43 +0,0 @@ -package io.jenkins.plugins.coverage.model; - -import org.apache.commons.lang3.math.Fraction; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.*; - -/** - * Tests the class {@link SafeFraction}. - * - * @author Ullrich Hafner - */ -class SafeFractionTest { - @Test - void shouldDelegateToFraction() { - Fraction ten = Fraction.getFraction(10, 1); - SafeFraction safeFraction = new SafeFraction(ten); - assertThat(safeFraction.multiplyBy(ten).doubleValue()).isEqualTo(100.0); - assertThat(safeFraction.subtract(ten).doubleValue()).isEqualTo(0); - assertThat(safeFraction.add(ten).doubleValue()).isEqualTo(20.0); - } - - @Test - void shouldHandleOverflowForMultiply() { - Fraction fraction = Fraction.getFraction(Integer.MAX_VALUE - 1, Integer.MAX_VALUE - 1); - SafeFraction safeFraction = new SafeFraction(fraction); - assertThat(safeFraction.multiplyBy(Fraction.getFraction("100.0")).doubleValue()).isEqualTo(100.0); - } - - @Test - void shouldHandleOverflowForSubtract() { - Fraction fraction = Fraction.getFraction(Integer.MAX_VALUE - 1, Integer.MAX_VALUE - 1); - SafeFraction safeFraction = new SafeFraction(fraction); - assertThat(safeFraction.subtract(Fraction.getFraction("100.0")).doubleValue()).isEqualTo(-99.0); - } - - @Test - void shouldHandleOverflowForAdd() { - Fraction fraction = Fraction.getFraction(Integer.MAX_VALUE - 1, Integer.MAX_VALUE - 1); - SafeFraction safeFraction = new SafeFraction(fraction); - assertThat(safeFraction.add(Fraction.getFraction("100.0")).doubleValue()).isEqualTo(101.0); - } -} diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/testutil/CoverageStubs.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/testutil/CoverageStubs.java index 6cfadafac..708058a27 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/testutil/CoverageStubs.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/testutil/CoverageStubs.java @@ -1,17 +1,21 @@ package io.jenkins.plugins.coverage.model.testutil; -import java.util.SortedMap; +import java.util.NavigableMap; +import java.util.Optional; import org.apache.commons.lang3.math.Fraction; import edu.hm.hafner.echarts.Build; import edu.hm.hafner.echarts.BuildResult; +import edu.hm.hafner.metric.Coverage; +import edu.hm.hafner.metric.Coverage.CoverageBuilder; +import edu.hm.hafner.metric.FileNode; +import edu.hm.hafner.metric.Metric; +import edu.hm.hafner.metric.ModuleNode; +import edu.hm.hafner.metric.Node; import edu.hm.hafner.util.VisibleForTesting; -import io.jenkins.plugins.coverage.model.Coverage; import io.jenkins.plugins.coverage.model.CoverageBuildAction; -import io.jenkins.plugins.coverage.model.CoverageMetric; -import io.jenkins.plugins.coverage.model.CoverageNode; import io.jenkins.plugins.coverage.model.CoveragePercentage; import static io.jenkins.plugins.coverage.model.CoverageBuildAction.*; @@ -65,19 +69,19 @@ public static BuildResult createResult(final int buildNumbe */ @VisibleForTesting public static CoverageBuildAction createCoverageBuildAction( - final CoverageMetric coverageMetric, final Fraction coverageValue) { + final Metric coverageMetric, final Fraction coverageValue) { CoverageBuildAction action = mock(CoverageBuildAction.class); Coverage coverage = createCoverage(coverageValue); CoveragePercentage percentage = CoveragePercentage.valueOf(coverageValue); - SortedMap deltas = mock(SortedMap.class); + NavigableMap deltas = mock(NavigableMap.class); when(deltas.size()).thenReturn(1); when(deltas.containsKey(coverageMetric)).thenReturn(true); - when(deltas.containsValue(percentage)).thenReturn(true); - when(deltas.get(coverageMetric)).thenReturn(percentage); + when(deltas.containsValue(coverageValue)).thenReturn(true); + when(deltas.get(coverageMetric)).thenReturn(coverageValue); when(action.hasDelta(coverageMetric)).thenReturn(true); - when(action.getDifference()).thenReturn(deltas); + when(action.getDelta()).thenReturn(deltas); when(action.hasCoverage(coverageMetric)).thenReturn(true); when(action.getCoverage(coverageMetric)).thenReturn(coverage); @@ -91,7 +95,7 @@ public static CoverageBuildAction createCoverageBuildAction( when(action.getIndirectCoverageChanges(coverageMetric)).thenReturn(coverage); when(action.hasChangeCoverageDifference(coverageMetric)).thenReturn(true); - when(action.getChangeCoverageDifference(coverageMetric)).thenReturn(percentage); + when(action.getChangeCoverageDifference(coverageMetric)).thenReturn(coverageValue); when(action.getUrlName()).thenReturn(DETAILS_URL); @@ -109,14 +113,13 @@ public static CoverageBuildAction createCoverageBuildAction( @VisibleForTesting public static Coverage createCoverage(final Fraction coverageFraction) { Coverage coverage = mock(Coverage.class); - when(coverage.getCoveredFraction()).thenReturn(coverageFraction); - when(coverage.getCoveredPercentage()).thenReturn(CoveragePercentage.valueOf(coverageFraction)); + when(coverage.getCoveredPercentage()).thenReturn(coverageFraction); when(coverage.isSet()).thenReturn(true); return coverage; } /** - * Creates a stub of {@link CoverageNode}, which provides the passed coverage percentage for the passed metric. + * Creates a stub of {@link Node}, which provides the passed coverage percentage for the passed metric. * * @param coverageFraction * The coverage fraction @@ -126,16 +129,15 @@ public static Coverage createCoverage(final Fraction coverageFraction) { * @return the created stub */ @VisibleForTesting - public static CoverageNode createCoverageNode(final Fraction coverageFraction, - final CoverageMetric coverageMetric) { - CoverageNode coverageNode = mock(CoverageNode.class); + public static FileNode createCoverageNode(final Fraction coverageFraction, final Metric coverageMetric) { + FileNode coverageNode = mock(FileNode.class); Coverage coverage = createCoverage(coverageFraction); - when(coverageNode.getCoverage(coverageMetric)).thenReturn(coverage); + when(coverageNode.getValue(coverageMetric)).thenReturn(Optional.of(coverage)); return coverageNode; } /** - * Creates a stub of {@link CoverageNode}, which represents the change coverage and provides information about it. + * Creates a stub of {@link Node}, which represents the change coverage and provides information about it. * * @param changeCoverage * The change coverage @@ -149,21 +151,25 @@ public static CoverageNode createCoverageNode(final Fraction coverageFraction, * @return the created stub */ @VisibleForTesting - public static CoverageNode createChangeCoverageNode(final Fraction changeCoverage, final CoverageMetric metric, + public static Node createChangeCoverageNode(final Fraction changeCoverage, final Metric metric, final int coverageFileChange, final long coverageLineChanges) { - CoverageNode coverageNode = createCoverageNode(changeCoverage, metric); - when(coverageNode.hasChangeCoverage()).thenReturn(true); - when(coverageNode.hasChangeCoverage(metric)).thenReturn(true); - when(coverageNode.getChangeCoverageTree()).thenReturn(coverageNode); - when(coverageNode.hasCodeChanges()).thenReturn(true); - when(coverageNode.getFileAmountWithChangedCoverage()).thenReturn(coverageFileChange); - when(coverageNode.getLineAmountWithChangedCoverage()).thenReturn(coverageLineChanges); - return coverageNode; + var root = new ModuleNode("root"); + var builder = new CoverageBuilder().setMetric(Metric.LINE); + for (int file = 0; file < 5; file++) { + var fileNode = new FileNode("File-" + file); + + for (int line = 0; line < 2; line++) { + fileNode.addLineCoverage(10 + line, builder.setCovered(1).setMissed(1).build()); + fileNode.addChangedCodeLine(10 + line); + } + root.addChild(fileNode); + } + + return root; } /** - * Creates a stub of {@link CoverageNode}, which represents indirect coverage changes and provides information about - * it. + * Creates a stub of {@link Node}, which represents indirect coverage changes and provides information about it. * * @param coverageChanges * The indirect coverage change @@ -177,14 +183,19 @@ public static CoverageNode createChangeCoverageNode(final Fraction changeCoverag * @return the created stub */ @VisibleForTesting - public static CoverageNode createIndirectCoverageChangesNode(final Fraction coverageChanges, - final CoverageMetric metric, final int coverageFileChange, final long coverageLineChanges) { - CoverageNode coverageNode = createCoverageNode(coverageChanges, metric); - when(coverageNode.hasIndirectCoverageChanges()).thenReturn(true); - when(coverageNode.hasIndirectCoverageChanges(metric)).thenReturn(true); - when(coverageNode.getIndirectCoverageChangesTree()).thenReturn(coverageNode); - when(coverageNode.getFileAmountWithIndirectCoverageChanges()).thenReturn(coverageFileChange); - when(coverageNode.getLineAmountWithIndirectCoverageChanges()).thenReturn(coverageLineChanges); - return coverageNode; + public static Node createIndirectCoverageChangesNode(final Fraction coverageChanges, + final Metric metric, final int coverageFileChange, final long coverageLineChanges) { + var root = new ModuleNode("root"); + var builder = new CoverageBuilder().setMetric(Metric.LINE); + for (int file = 0; file < 5; file++) { + var fileNode = new FileNode("File-" + file); + + for (int line = 0; line < 2; line++) { + fileNode.addLineCoverage(10 + line, builder.setCovered(1).setMissed(1).build()); + fileNode.addIndirectCoverageChange(10 + line, 2); + } + root.addChild(fileNode); + } + return root; } } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/charts/CoverageSeriesBuilderTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/charts/CoverageSeriesBuilderTest.java index f551ae708..50eb7d81f 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/charts/CoverageSeriesBuilderTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/charts/CoverageSeriesBuilderTest.java @@ -10,8 +10,9 @@ import edu.hm.hafner.echarts.ChartModelConfiguration.AxisType; import edu.hm.hafner.echarts.LinesChartModel; import edu.hm.hafner.echarts.LinesDataSet; +import edu.hm.hafner.metric.Coverage.CoverageBuilder; +import edu.hm.hafner.metric.Metric; -import io.jenkins.plugins.coverage.model.Coverage.CoverageBuilder; import io.jenkins.plugins.coverage.model.CoverageBuildAction; import static io.jenkins.plugins.coverage.model.testutil.CoverageStubs.*; @@ -39,16 +40,16 @@ void shouldCreateChart() { CoverageTrendChart trendChart = new CoverageTrendChart(); BuildResult smallLineCoverage = createResult(1, - new CoverageBuilder().setCovered(1).setMissed(1).build(), - new CoverageBuilder().setCovered(3).setMissed(1).build()); + new CoverageBuilder().setMetric(Metric.LINE).setCovered(1).setMissed(1).build(), + new CoverageBuilder().setMetric(Metric.BRANCH).setCovered(3).setMissed(1).build()); LinesChartModel lineCoverage = trendChart.create(Collections.singletonList(smallLineCoverage), createConfiguration()); verifySeriesDetails(lineCoverage); BuildResult smallBranchCoverage = createResult(1, - new CoverageBuilder().setCovered(3).setMissed(1).build(), - new CoverageBuilder().setCovered(1).setMissed(1).build()); + new CoverageBuilder().setMetric(Metric.LINE).setCovered(3).setMissed(1).build(), + new CoverageBuilder().setMetric(Metric.BRANCH).setCovered(1).setMissed(1).build()); LinesChartModel branchCoverage = trendChart.create(Collections.singletonList(smallBranchCoverage), createConfiguration()); @@ -67,8 +68,8 @@ void shouldHaveTwoValuesForSingleBuild() { CoverageSeriesBuilder builder = new CoverageSeriesBuilder(); BuildResult singleResult = createResult(1, - new CoverageBuilder().setCovered(1).setMissed(1).build(), - new CoverageBuilder().setCovered(3).setMissed(1).build()); + new CoverageBuilder().setMetric(Metric.LINE).setCovered(1).setMissed(1).build(), + new CoverageBuilder().setMetric(Metric.BRANCH).setCovered(3).setMissed(1).build()); LinesDataSet dataSet = builder.createDataSet(createConfiguration(), Collections.singletonList(singleResult)); diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodeFacadeTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodeFacadeTest.java index d3017da8a..9abb1b05e 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodeFacadeTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodeFacadeTest.java @@ -3,6 +3,7 @@ import java.io.IOException; import java.nio.file.Files; import java.util.Arrays; +import java.util.List; import java.util.SortedMap; import java.util.SortedSet; import java.util.TreeMap; @@ -13,12 +14,12 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import edu.hm.hafner.metric.Coverage; +import edu.hm.hafner.metric.Coverage.CoverageBuilder; +import edu.hm.hafner.metric.FileNode; +import edu.hm.hafner.metric.Metric; import edu.hm.hafner.util.ResourceTest; -import io.jenkins.plugins.coverage.model.Coverage; -import io.jenkins.plugins.coverage.model.Coverage.CoverageBuilder; -import io.jenkins.plugins.coverage.model.FileCoverageNode; - import static org.assertj.core.api.Assertions.*; /** @@ -53,6 +54,10 @@ class SourceCodeFacadeTest extends ResourceTest { * A dummy coverage per line mapping. */ private static final SortedMap COVERAGE_PER_LINE = new TreeMap<>(); + private static final Coverage COVERAGE = new CoverageBuilder().setMetric(Metric.LINE) + .setCovered(1) + .setMissed(0) + .build(); @BeforeAll static void init() { @@ -62,7 +67,7 @@ static void init() { INDIRECT_COVERAGE_CHANGES.put(14, 1); INDIRECT_COVERAGE_CHANGES.put(15, 1); for (int i = 1; i <= 25; i++) { - COVERAGE_PER_LINE.put(i, CoverageBuilder.NO_COVERAGE); + COVERAGE_PER_LINE.put(i, Coverage.nullObject(Metric.LINE)); } } @@ -70,7 +75,7 @@ static void init() { void shouldCalculateSourcecodeForChangeCoverage() throws IOException { SourceCodeFacade sourceCodeFacade = createSourceCodeFacade(); String originalHtml = readHtml(SOURCECODE); - FileCoverageNode node = createFileCoverageNode(); + FileNode node = createFileCoverageNode(); String requiredHtml = Jsoup.parse(readHtml(SOURCECODE_CC), Parser.xmlParser()).html(); @@ -82,7 +87,7 @@ void shouldCalculateSourcecodeForChangeCoverage() throws IOException { void shouldCalculateSourcecodeForIndirectCoverageChanges() throws IOException { SourceCodeFacade sourceCodeFacade = createSourceCodeFacade(); String originalHtml = readHtml(SOURCECODE); - FileCoverageNode node = createFileCoverageNode(); + FileNode node = createFileCoverageNode(); String requiredHtml = Jsoup.parse(readHtml(SOURCECODE_ICC), Parser.xmlParser()).html(); @@ -100,17 +105,25 @@ private SourceCodeFacade createSourceCodeFacade() { } /** - * Creates a {@link FileCoverageNode} which contains {@link #CODE_CHANGES}, {@link #INDIRECT_COVERAGE_CHANGES} and + * Creates a {@link FileNode} which contains {@link #CODE_CHANGES}, {@link #INDIRECT_COVERAGE_CHANGES} and * {@link #COVERAGE_PER_LINE}. * * @return the created node */ - private FileCoverageNode createFileCoverageNode() { - FileCoverageNode fileCoverageNode = new FileCoverageNode("", ""); - fileCoverageNode.setCoveragePerLine(COVERAGE_PER_LINE); - fileCoverageNode.setChangedCodeLines(CODE_CHANGES); - fileCoverageNode.setIndirectCoverageChanges(INDIRECT_COVERAGE_CHANGES); - return fileCoverageNode; + private FileNode createFileCoverageNode() { + FileNode file = new FileNode(""); + List lines = Arrays.asList(10, 11, 12, 16, 17, 18, 19); + for (Integer line : lines) { + file.addChangedCodeLine(line); + } + file.addIndirectCoverageChange(6, -1); + file.addIndirectCoverageChange(7, -1); + file.addIndirectCoverageChange(14, 1); + file.addIndirectCoverageChange(15, 1); + for (int i = 1; i <= 25; i++) { + file.addLineCoverage(i, COVERAGE); + } + return file; } /** diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ChangeCoverageDeltaTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ChangeCoverageDeltaTest.java index 54fa44f5a..597441d49 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ChangeCoverageDeltaTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ChangeCoverageDeltaTest.java @@ -4,8 +4,9 @@ import org.junit.jupiter.api.Test; +import edu.hm.hafner.metric.Metric; + import io.jenkins.plugins.coverage.model.CoverageBuildAction; -import io.jenkins.plugins.coverage.model.CoverageMetric; import io.jenkins.plugins.coverage.model.CoveragePercentage; import io.jenkins.plugins.coverage.model.visualization.colorization.ColorId; import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors; @@ -30,7 +31,7 @@ void shouldGetCoverage() { @Test void shouldNotReturnNonExistentCoverage() { CoverageBuildAction action = createCoverageBuildAction(); - Optional coverage = CHANGE_COVERAGE_DELTA.getCoverage(action, CoverageMetric.FILE); + Optional coverage = CHANGE_COVERAGE_DELTA.getCoverage(action, Metric.FILE); assertThat(coverage).isEmpty(); } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ChangeCoverageTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ChangeCoverageTest.java index 73bfac24b..a7e908c98 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ChangeCoverageTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ChangeCoverageTest.java @@ -4,8 +4,9 @@ import org.junit.jupiter.api.Test; +import edu.hm.hafner.metric.Metric; + import io.jenkins.plugins.coverage.model.CoverageBuildAction; -import io.jenkins.plugins.coverage.model.CoverageMetric; import io.jenkins.plugins.coverage.model.CoveragePercentage; import io.jenkins.plugins.coverage.model.visualization.colorization.ColorId; import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors; @@ -30,7 +31,7 @@ void shouldGetCoverage() { @Test void shouldNotReturnNonExistentCoverage() { CoverageBuildAction action = createCoverageBuildAction(); - Optional coverage = CHANGE_COVERAGE.getCoverage(action, CoverageMetric.FILE); + Optional coverage = CHANGE_COVERAGE.getCoverage(action, Metric.FILE); assertThat(coverage).isEmpty(); } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumnTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumnTest.java index b6edd2dde..e6d0448a9 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumnTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumnTest.java @@ -6,11 +6,12 @@ import org.junit.jupiter.api.Test; import org.junitpioneer.jupiter.DefaultLocale; +import edu.hm.hafner.metric.Metric; + import hudson.Functions; import hudson.model.Job; import io.jenkins.plugins.coverage.model.CoverageBuildAction; -import io.jenkins.plugins.coverage.model.CoverageMetric; import io.jenkins.plugins.coverage.model.CoveragePercentage; import io.jenkins.plugins.coverage.model.Messages; import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider; @@ -32,7 +33,7 @@ class CoverageColumnTest { private static final String COLUMN_NAME = "Test Column"; private static final ProjectCoverage PROJECT_COVERAGE = new ProjectCoverage(); private static final ProjectCoverageDelta PROJECT_COVERAGE_DELTA = new ProjectCoverageDelta(); - private static final CoverageMetric COVERAGE_METRIC = CoverageMetric.BRANCH; + private static final Metric COVERAGE_METRIC = Metric.BRANCH; private static final ColorProvider COLOR_PROVIDER = ColorProviderFactory.createDefaultColorProvider(); @@ -41,7 +42,7 @@ void shouldHaveWorkingDataGetters() { CoverageColumn column = createColumn(); assertThat(column.getColumnName()).isEqualTo(COLUMN_NAME); assertThat(column.getCoverageType()).isEqualTo(PROJECT_COVERAGE.getDisplayName()); - assertThat(column.getCoverageMetric()).isEqualTo(COVERAGE_METRIC.getName()); + assertThat(column.getCoverageMetric()).isEqualTo(COVERAGE_METRIC.name()); } @Test @@ -115,9 +116,9 @@ void shouldShowNoResultIfNoAction() { } @Test - void shouldShowNoResultForUnavailableCoverageMetric() { + void shouldShowNoResultForUnavailableMetric() { CoverageColumn column = createColumn(); - column.setCoverageMetric(CoverageMetric.CLASS.getName()); + column.setCoverageMetric(Metric.CLASS.name()); Job job = createJobWithCoverageAction(Fraction.ZERO); @@ -181,7 +182,7 @@ private CoverageColumn createColumn() { CoverageColumn column = new CoverageColumn(); column.setColumnName(COLUMN_NAME); column.setCoverageType(PROJECT_COVERAGE.getDisplayName()); - column.setCoverageMetric(COVERAGE_METRIC.getName()); + column.setCoverageMetric(COVERAGE_METRIC.name()); return column; } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumnTypeTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumnTypeTest.java index 6dc269d33..865cc31e4 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumnTypeTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumnTypeTest.java @@ -6,8 +6,9 @@ import org.apache.commons.lang3.math.Fraction; import org.junit.jupiter.api.Test; +import edu.hm.hafner.metric.Metric; + import io.jenkins.plugins.coverage.model.CoverageBuildAction; -import io.jenkins.plugins.coverage.model.CoverageMetric; import io.jenkins.plugins.coverage.model.CoveragePercentage; import io.jenkins.plugins.coverage.model.Messages; import io.jenkins.plugins.coverage.model.testutil.CoverageStubs; @@ -22,7 +23,6 @@ * @author Florian Orendi */ class CoverageColumnTypeTest { - protected static final String PROJECT_COVERAGE_NAME = Messages.Project_Coverage_Type(); protected static final String PROJECT_COVERAGE_DELTA_NAME = Messages.Project_Coverage_Delta_Type(); protected static final String CHANGE_COVERAGE_NAME = Messages.Change_Coverage_Type(); @@ -38,7 +38,7 @@ class CoverageColumnTypeTest { protected static final Fraction COVERAGE_FRACTION = Fraction.getFraction(0.5); protected static final CoveragePercentage COVERAGE_PERCENTAGE = CoveragePercentage.valueOf(COVERAGE_FRACTION); - protected static final CoverageMetric COVERAGE_METRIC = CoverageMetric.BRANCH; + protected static final Metric COVERAGE_METRIC = Metric.BRANCH; protected static final Locale LOCALE = Locale.GERMAN; protected static final ColorProvider COLOR_PROVIDER = ColorProviderFactory.createDefaultColorProvider(); diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/IndirectCoverageChangesTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/IndirectCoverageChangesTest.java index bb75785eb..0dff97b71 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/IndirectCoverageChangesTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/IndirectCoverageChangesTest.java @@ -4,8 +4,9 @@ import org.junit.jupiter.api.Test; +import edu.hm.hafner.metric.Metric; + import io.jenkins.plugins.coverage.model.CoverageBuildAction; -import io.jenkins.plugins.coverage.model.CoverageMetric; import io.jenkins.plugins.coverage.model.CoveragePercentage; import io.jenkins.plugins.coverage.model.visualization.colorization.ColorId; import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors; @@ -30,7 +31,7 @@ void shouldGetCoverage() { @Test void shouldNotReturnNonExistentCoverage() { CoverageBuildAction action = createCoverageBuildAction(); - Optional coverage = INDIRECT_COVERAGE_CHANGES.getCoverage(action, CoverageMetric.FILE); + Optional coverage = INDIRECT_COVERAGE_CHANGES.getCoverage(action, Metric.FILE); assertThat(coverage).isEmpty(); } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ProjectCoverageDeltaTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ProjectCoverageDeltaTest.java index ce9d25a06..16521bd74 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ProjectCoverageDeltaTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ProjectCoverageDeltaTest.java @@ -4,8 +4,9 @@ import org.junit.jupiter.api.Test; +import edu.hm.hafner.metric.Metric; + import io.jenkins.plugins.coverage.model.CoverageBuildAction; -import io.jenkins.plugins.coverage.model.CoverageMetric; import io.jenkins.plugins.coverage.model.CoveragePercentage; import io.jenkins.plugins.coverage.model.visualization.colorization.ColorId; import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors; @@ -30,7 +31,7 @@ void shouldGetCoverage() { @Test void shouldNotReturnNonExistentCoverage() { CoverageBuildAction action = createCoverageBuildAction(); - Optional coverage = PROJECT_COVERAGE_DELTA.getCoverage(action, CoverageMetric.FILE); + Optional coverage = PROJECT_COVERAGE_DELTA.getCoverage(action, Metric.FILE); assertThat(coverage).isEmpty(); } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ProjectCoverageTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ProjectCoverageTest.java index fa8a1befa..1cf2f26ff 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ProjectCoverageTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ProjectCoverageTest.java @@ -4,8 +4,9 @@ import org.junit.jupiter.api.Test; +import edu.hm.hafner.metric.Metric; + import io.jenkins.plugins.coverage.model.CoverageBuildAction; -import io.jenkins.plugins.coverage.model.CoverageMetric; import io.jenkins.plugins.coverage.model.CoveragePercentage; import io.jenkins.plugins.coverage.model.visualization.colorization.ColorId; import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors; @@ -30,7 +31,7 @@ void shouldGetCoverage() { @Test void shouldNotReturnNonExistentCoverage() { CoverageBuildAction action = createCoverageBuildAction(); - Optional coverage = PROJECT_COVERAGE.getCoverage(action, CoverageMetric.FILE); + Optional coverage = PROJECT_COVERAGE.getCoverage(action, Metric.FILE); assertThat(coverage).isEmpty(); } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/tree/TreeMapNodeConverterTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/tree/TreeMapNodeConverterTest.java index d582b55ed..aad37c9b1 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/tree/TreeMapNodeConverterTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/tree/TreeMapNodeConverterTest.java @@ -5,10 +5,10 @@ import org.junit.jupiter.api.Test; import edu.hm.hafner.echarts.TreeMapNode; +import edu.hm.hafner.metric.Metric; +import edu.hm.hafner.metric.Node; import io.jenkins.plugins.coverage.model.AbstractCoverageTest; -import io.jenkins.plugins.coverage.model.CoverageMetric; -import io.jenkins.plugins.coverage.model.CoverageNode; import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider; import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProviderFactory; import io.jenkins.plugins.coverage.model.visualization.colorization.CoverageLevel; @@ -26,15 +26,14 @@ class TreeMapNodeConverterTest extends AbstractCoverageTest { @Test void shouldConvertCodingStyleToTree() { - CoverageNode tree = readNode(Paths.get("..", "..", "jacoco-codingstyle.xml").toString()); - tree.splitPackages(); + Node tree = readJacocoResult(Paths.get("..", "..", "jacoco-codingstyle.xml").toString()); final double totalLines = 323.0; final double coveredLines = 294.0; final double coveredPercentage = coveredLines / totalLines * 100.0; - TreeMapNode root = new TreeMapNodeConverter().toTeeChartModel(tree, CoverageMetric.LINE, COLOR_PROVIDER); - assertThat(root.getName()).isEqualTo("Java coding style: jacoco-codingstyle.xml"); + TreeMapNode root = new TreeMapNodeConverter().toTeeChartModel(tree, Metric.LINE, COLOR_PROVIDER); + assertThat(root.getName()).isEqualTo("Java coding style"); assertThat(root.getValue()).containsExactly(totalLines, coveredLines); assertThat(root.getItemStyle().getColor()).isEqualTo(getNodeColorAsRGBHex(coveredPercentage)); @@ -49,16 +48,15 @@ void shouldConvertCodingStyleToTree() { @Test void shouldConvertAnalysisModelToTree() { - CoverageNode tree = readNode(Paths.get("..", "..", "jacoco-analysis-model.xml").toString()); - tree.splitPackages(); + Node tree = readJacocoResult(Paths.get("..", "..", "jacoco-analysis-model.xml").toString()); - TreeMapNode root = new TreeMapNodeConverter().toTeeChartModel(tree, CoverageMetric.LINE, COLOR_PROVIDER); + TreeMapNode root = new TreeMapNodeConverter().toTeeChartModel(tree, Metric.LINE, COLOR_PROVIDER); final double totalLines = 6368.0; final double coveredLines = 6083.0; final double coveredPercentage = coveredLines / totalLines * 100.0; - assertThat(root.getName()).isEqualTo("Static Analysis Model and Parsers: jacoco-analysis-model.xml"); + assertThat(root.getName()).isEqualTo("Static Analysis Model and Parsers"); assertThat(root.getValue()).containsExactly(totalLines, coveredLines); assertThat(root.getItemStyle().getColor()).isEqualTo(getNodeColorAsRGBHex(coveredPercentage)); assertThat(root.getChildren()).hasSize(1).element(0).satisfies( diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/model/visualization/code/SourcecodeTestCC.html b/plugin/src/test/resources/io/jenkins/plugins/coverage/model/visualization/code/SourcecodeTestCC.html index a98b581fe..e601f8dbe 100644 --- a/plugin/src/test/resources/io/jenkins/plugins/coverage/model/visualization/code/SourcecodeTestCC.html +++ b/plugin/src/test/resources/io/jenkins/plugins/coverage/model/visualization/code/SourcecodeTestCC.html @@ -1,17 +1,37 @@ - - .. + + 1 + + package test.example; + + + 2 - - - - - + + 3 + 1 + public class SourcecodeTest { + - 7 + 4 + + + + + 5 -     } +     public void first() { + + + 6 + 0 +         System.out.println(); + + + 7 + 0 +     } 8 @@ -21,17 +41,17 @@ 9 -     public void insertedTested() { +     public void insertedTested() { 10 1 -         System.out.println(); +         System.out.println(); 11 1 -     } +     } 12 @@ -41,17 +61,17 @@ 13 -     public void second() { +     public void second() { - + 14 - -         System.out.println(); + 1 +         System.out.println(); - + 15 - -     } + 1 +     } 16 @@ -61,17 +81,17 @@ 17 -     public void inserted() { +     public void inserted() { 18 0 -         System.out.println(); +         System.out.println(); 19 0 -     } +     } 20 @@ -81,16 +101,20 @@ 21 -     public void third() { +     public void third() { - + 22 - -         System.out.println(); + 0 +         System.out.println(); - - .. + + 23 + 0 +     } + + + 24 - + } - From bfbd00f58e813a7e629dc58a3f74ea63d4067132 Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Mon, 17 Oct 2022 12:04:34 +0200 Subject: [PATCH 05/29] Converted freestyle integration tests. Use the new parsers in freestyle tests: - JaCoCo one and two files - Cobertura single file without complexity - PIT single file --- .../coverage/metrics/FilesScanner.java | 5 +- .../coverage/model/CoveragePluginITest.java | 274 +- .../coverage/model/CoverageXmlStreamTest.java | 36 + .../plugins/coverage/model/mutations.xml | 3497 +++++++++++++++++ 4 files changed, 3702 insertions(+), 110 deletions(-) create mode 100644 plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageXmlStreamTest.java create mode 100644 plugin/src/test/resources/io/jenkins/plugins/coverage/model/mutations.xml diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/FilesScanner.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/FilesScanner.java index 8d1f7dd4d..1ce1d9f93 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/FilesScanner.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/FilesScanner.java @@ -6,7 +6,6 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import org.apache.commons.lang3.StringUtils; @@ -93,9 +92,7 @@ else if (isEmpty(file)) { nodes.add(aggregateIssuesOfFile(file, log)); } } - return nodes.stream() - .reduce(Node::combineWith) - .orElse(new ModuleNode("Empty results for " + Arrays.toString(fileNames))); + return Node.merge(nodes); } private boolean isEmpty(final Path file) { diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java index 909b06b1a..3dce6e5ad 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java @@ -10,6 +10,7 @@ import edu.hm.hafner.metric.Coverage; import edu.hm.hafner.metric.Coverage.CoverageBuilder; import edu.hm.hafner.metric.Metric; +import edu.hm.hafner.metric.MutationValue; import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; import org.jenkinsci.plugins.workflow.job.WorkflowJob; @@ -91,6 +92,170 @@ class CoveragePluginITest extends IntegrationTestWithJenkinsPerSuite { */ private static final String JACOCO_ADAPTER = "jacocoAdapter"; + // --------------------------------------------------------------------------------------- + // vv Converted tests vv + // --------------------------------------------------------------------------------------- + + @Test + void shouldRecordOneJacocoResultInFreestyleJob() { + FreeStyleProject project = createFreeStyleProject(); + copyFileToWorkspace(project, JACOCO_ANALYSIS_MODEL_FILE); + + CoverageRecorder recorder = new CoverageRecorder(); + registerJaCoCo(recorder); + project.getPublishersList().add(recorder); + + // FIXME: which parser is correct? + /* + Expected :LINE: 95.52% (6083/6368) + Actual :LINE: 95.41% (5588/5857) + */ + verifyOneJacocoResult(project); + } + + @Test + void shouldRecordTwoJacocoResultsInFreestyleJob() { + FreeStyleProject project = createFreeStyleProject(); + copyFilesToWorkspace(project, JACOCO_ANALYSIS_MODEL_FILE, JACOCO_CODINGSTYLE_FILE); + + CoverageRecorder recorder = new CoverageRecorder(); + registerJaCoCo(recorder); + project.getPublishersList().add(recorder); + + // FIXME: which parser is correct? + /* + Expected :LINE: 95.31% (6377/6691) + Actual :LINE: 95.18% (5882/6180) + */ + verifyTwoJacocoResults(project); + } + + private void registerJaCoCo(final CoverageRecorder recorder) { + var tool = new CoverageTool(); + tool.setParser(CoverageParser.JACOCO); + tool.setPattern("**/jacoco*xml"); + recorder.setTools(List.of(tool)); + } + + /** + * Verifies project with one jacoco file. + * + * @param project + * the project with added files + */ + private void verifyOneJacocoResult(final ParameterizedJob project) { + Run build = buildSuccessfully(project); + + CoverageBuildAction coverageResult = build.getAction(CoverageBuildAction.class); + assertThat(coverageResult.getLineCoverage()) + .isEqualTo(createLineCoverageBuilder() + .setCovered(JACOCO_COVERED_LINES) + .setMissed(JACOCO_ALL_LINES - JACOCO_COVERED_LINES) + .build()); + } + + /** + * Verifies project with two jacoco files. + * + * @param project + * the project with added files + */ + private void verifyTwoJacocoResults(final ParameterizedJob project) { + Run build = buildSuccessfully(project); + + CoverageBuildAction coverageResult = build.getAction(CoverageBuildAction.class); + assertThat(coverageResult.getLineCoverage()) + .isEqualTo(createLineCoverageBuilder() + .setCovered(BOTH_JACOCO_COVERED_LINES) + .setMissed(BOTH_JACOCO_ALL_LINES - BOTH_JACOCO_COVERED_LINES) + .build()); + } + + /** + * Freestyle integration test with one cobertura file. + */ + @Test + void shouldRecordOneCoberturaResultInFreestyleJob() { + FreeStyleProject project = createFreeStyleProject(); + + copyFilesToWorkspace(project, COBERTURA_HIGHER_COVERAGE_FILE); + + CoverageRecorder recorder = new CoverageRecorder(); + registerCobertura(recorder); + project.getPublishersList().add(recorder); + + // FIXME: all parsers should only fail for mandatory properties (complexity is only optional) + verifyOneCoberturaResult(project); + } + + private void registerCobertura(final CoverageRecorder recorder) { + var tool = new CoverageTool(); + tool.setParser(CoverageParser.COBERTURA); + tool.setPattern("**/cobertura*xml"); + recorder.setTools(List.of(tool)); + } + + /** + * Verifies project with one cobertura file. + * + * @param project + * the project with added files + */ + private void verifyOneCoberturaResult(final ParameterizedJob project) { + Run build = buildSuccessfully(project); + + CoverageBuildAction coverageResult = build.getAction(CoverageBuildAction.class); + assertThat(coverageResult.getLineCoverage()) + .isEqualTo(new CoverageBuilder().setMetric(Metric.LINE).setCovered(COBERTURA_COVERED_LINES) + .setMissed(COBERTURA_ALL_LINES - COBERTURA_COVERED_LINES) + .build()); + + } + + /** + * Freestyle integration test with one cobertura file. + */ + @Test + void shouldRecordOnePitResultInFreestyleJob() { + FreeStyleProject project = createFreeStyleProject(); + + copyFilesToWorkspace(project, "mutations.xml"); + + CoverageRecorder recorder = new CoverageRecorder(); + registerPit(recorder); + project.getPublishersList().add(recorder); + + verifyOnePitResult(project); + } + + private void registerPit(final CoverageRecorder recorder) { + var tool = new CoverageTool(); + tool.setParser(CoverageParser.PIT); + tool.setPattern("**/mutations*xml"); + recorder.setTools(List.of(tool)); + } + + private void verifyOnePitResult(final ParameterizedJob project) { + Run build = buildSuccessfully(project); + + CoverageBuildAction coverageResult = build.getAction(CoverageBuildAction.class); + assertThat(coverageResult.getCoverage(Metric.MUTATION)) + .isInstanceOfSatisfying(MutationValue.class, m -> { + assertThat(m.getKilled()).isEqualTo(222); + assertThat(m.getTotal()).isEqualTo(246); + }); + + } + + private static CoverageBuilder createLineCoverageBuilder() { + return new CoverageBuilder().setMetric(Metric.LINE); + } + + + // --------------------------------------------------------------------------------------- + // ^^ Converted tests ^^ + // --------------------------------------------------------------------------------------- + /** * Pipeline integration test with no adapter. */ @@ -134,7 +299,7 @@ void pipelineForOneJacoco() { WorkflowJob job = createPipelineWithWorkspaceFiles(JACOCO_ANALYSIS_MODEL_FILE); job.setDefinition(getCpsFlowDefinitionWithAdapter(JACOCO_ADAPTER)); - verifyForOneJacoco(job); + verifyOneJacocoResult(job); } /** @@ -145,7 +310,7 @@ void pipelineForTwoJacoco() { WorkflowJob job = createPipelineWithWorkspaceFiles(JACOCO_ANALYSIS_MODEL_FILE, JACOCO_CODINGSTYLE_FILE); job.setDefinition(getCpsFlowDefinitionWithAdapter(JACOCO_ADAPTER)); - verifyForTwoJacoco(job); + verifyTwoJacocoResults(job); } /** @@ -163,43 +328,6 @@ void freestyleForNoJacoco() { verifyForNoFile(project); } - /** - * Freestyle integration test with one jacoco file. - */ - @Test - void freestyleForOneJacoco() { - FreeStyleProject project = createFreeStyleProject(); - copyFileToWorkspace(project, JACOCO_ANALYSIS_MODEL_FILE, "jacoco.xml"); - CoverageRecorder recorder = new CoverageRecorder(); - var tool = new CoverageTool(); - tool.setParser(CoverageParser.JACOCO); - recorder.setTools(List.of(tool)); - project.getPublishersList().add(recorder); - - verifyForOneJacoco(project); - } - - /** - * Freestyle integration test with two jacoco files. - */ - @Test - void freestyleForTwoJacoco() { - - FreeStyleProject project = createFreeStyleProject(); - copyFilesToWorkspace(project, JACOCO_ANALYSIS_MODEL_FILE, JACOCO_CODINGSTYLE_FILE); - - CoveragePublisher coveragePublisher = new CoveragePublisher(); - JacocoReportAdapter jacocoReportAdapter = new JacocoReportAdapter(JACOCO_ANALYSIS_MODEL_FILE); - JacocoReportAdapter jacocoReportAdapter2 = new JacocoReportAdapter(JACOCO_CODINGSTYLE_FILE); - List reportAdapters = new ArrayList<>(); - reportAdapters.add(jacocoReportAdapter); - reportAdapters.add(jacocoReportAdapter2); - coveragePublisher.setAdapters(reportAdapters); - project.getPublishersList().add(coveragePublisher); - - verifyForTwoJacoco(project); - } - /** * Pipeline integration test with no cobertura file. */ @@ -219,7 +347,7 @@ void pipelineForOneCobertura() { WorkflowJob job = createPipelineWithWorkspaceFiles(COBERTURA_HIGHER_COVERAGE_FILE); job.setDefinition(getCpsFlowDefinitionWithAdapter(COBERTURA_ADAPTER)); - verifyForOneCobertura(job); + verifyOneCoberturaResult(job); } /** @@ -250,21 +378,6 @@ void freestyleForNoCobertura() { verifyForNoFile(project); } - /** - * Freestyle integration test with one cobertura file. - */ - @Test - void freestyleForOneCobertura() { - FreeStyleProject project = createFreeStyleProject(); - copyFilesToWorkspace(project, COBERTURA_HIGHER_COVERAGE_FILE); - CoveragePublisher coveragePublisher = new CoveragePublisher(); - CoberturaReportAdapter coberturaReportAdapter = new CoberturaReportAdapter(COBERTURA_HIGHER_COVERAGE_FILE); - coveragePublisher.setAdapters(Collections.singletonList(coberturaReportAdapter)); - project.getPublishersList().add(coveragePublisher); - - verifyForOneCobertura(project); - } - /** * Freestyle integration test with two cobertura files. */ @@ -339,40 +452,6 @@ private CpsFlowDefinition getCpsFlowDefinitionWithAdapter(final String adapter) + "}", true); } - /** - * Verifies project with one jacoco file. - * - * @param project - * the project with added files - */ - private void verifyForOneJacoco(final ParameterizedJob project) { - Run build = buildSuccessfully(project); - - CoverageBuildAction coverageResult = build.getAction(CoverageBuildAction.class); - assertThat(coverageResult.getLineCoverage()) - .isEqualTo(new Coverage.CoverageBuilder() - .setMetric(Metric.LINE) - .setCovered(JACOCO_COVERED_LINES) - .setMissed(JACOCO_ALL_LINES - JACOCO_COVERED_LINES) - .build()); - } - - /** - * Verifies project with two jacoco files. - * - * @param project - * the project with added files - */ - private void verifyForTwoJacoco(final ParameterizedJob project) { - Run build = buildSuccessfully(project); - - CoverageBuildAction coverageResult = build.getAction(CoverageBuildAction.class); - assertThat(coverageResult.getLineCoverage()) - .isEqualTo(new Coverage.CoverageBuilder().setCovered(BOTH_JACOCO_COVERED_LINES) - .setMissed(BOTH_JACOCO_ALL_LINES - BOTH_JACOCO_COVERED_LINES) - .build()); - } - /** * Verifies project with no adapter. * @@ -395,23 +474,6 @@ private void verifyForNoFile(final ParameterizedJob project) { assertThat(action).isEqualTo(null); } - /** - * Verifies project with one cobertura file. - * - * @param project - * the project with added files - */ - private void verifyForOneCobertura(final ParameterizedJob project) { - Run build = buildSuccessfully(project); - - CoverageBuildAction coverageResult = build.getAction(CoverageBuildAction.class); - assertThat(coverageResult.getLineCoverage()) - .isEqualTo(new CoverageBuilder().setMetric(Metric.LINE).setCovered(COBERTURA_COVERED_LINES) - .setMissed(COBERTURA_ALL_LINES - COBERTURA_COVERED_LINES) - .build()); - - } - /** * Verifies project with two cobertura files. * diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageXmlStreamTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageXmlStreamTest.java new file mode 100644 index 000000000..2c5414f2e --- /dev/null +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageXmlStreamTest.java @@ -0,0 +1,36 @@ +package io.jenkins.plugins.coverage.model; + +import java.io.InputStreamReader; +import java.nio.file.Path; + +import org.junit.jupiter.api.Test; + +import edu.hm.hafner.metric.Node; +import edu.hm.hafner.metric.parser.JacocoParser; +import edu.hm.hafner.util.SerializableTest; + +import static io.jenkins.plugins.coverage.model.Assertions.*; + +/** + * Tests the class {@link CoverageXmlStream}. + * + * @author Ullrich Hafner + */ +class CoverageXmlStreamTest extends SerializableTest { + @Override + protected Node createSerializable() { + return new JacocoParser().parse(new InputStreamReader(asInputStream("jacoco-codingstyle.xml"))); + } + + @Test + void shouldSaveAndRestoreTree() { + CoverageXmlStream xmlStream = new CoverageXmlStream(); + + Path saved = createTempFile(); + Node convertedNode = createSerializable(); + + xmlStream.write(saved, convertedNode); + Node restored = xmlStream.read(saved); + assertThat(restored).usingRecursiveComparison().isEqualTo(convertedNode); + } +} diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/model/mutations.xml b/plugin/src/test/resources/io/jenkins/plugins/coverage/model/mutations.xml new file mode 100644 index 000000000..a05447c52 --- /dev/null +++ b/plugin/src/test/resources/io/jenkins/plugins/coverage/model/mutations.xml @@ -0,0 +1,3497 @@ + + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + add + (Ledu/hm/hafner/coverage/CoverageNode;)V + 175 + NotExisting + 12 + 1 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldAddChildren()] + + removed call to edu/hm/hafner/coverage/CoverageNode::setParent + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + addAll + (Ljava/util/List;)V + 164 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 6 + 0 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldSplitPackagesWithMultipleDots()] + + removed call to java/util/List::forEach + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + aggregateChildren + (Ledu/hm/hafner/coverage/CoverageMetric;)Ledu/hm/hafner/coverage/Coverage; + 284 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 19 + 3 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldGetMetricsDistributionAndPercentages()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageNode::aggregateChildren + + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + computeDelta + (Ledu/hm/hafner/coverage/CoverageNode;)Ljava/util/SortedMap; + 301 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 24 + 3 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldComputeDeltaWithOverflow()] + + removed call to java/util/SortedMap::forEach + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + computeDelta + (Ledu/hm/hafner/coverage/CoverageNode;)Ljava/util/SortedMap; + 304 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 28 + 4 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldComputeDeltaWithOverflow()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageNode::computeDelta + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + copyEmpty + ()Ledu/hm/hafner/coverage/CoverageNode; + 483 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 10 + 1 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldCopyLeaf()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageNode::copyEmpty + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + copyTree + ()Ledu/hm/hafner/coverage/CoverageNode; + 452 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 6 + 1 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldCopyLeaf()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageNode::copyTree + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + copyTree + (Ledu/hm/hafner/coverage/CoverageNode;)Ledu/hm/hafner/coverage/CoverageNode; + 465 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 9 + 1 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldCopyLeaf()] + + negated conditional + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + copyTree + (Ledu/hm/hafner/coverage/CoverageNode;)Ledu/hm/hafner/coverage/CoverageNode; + 466 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 14 + 2 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldCopyWithChild()] + + removed call to edu/hm/hafner/coverage/CoverageNode::setParent + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + copyTree + (Ledu/hm/hafner/coverage/CoverageNode;)Ledu/hm/hafner/coverage/CoverageNode; + 471 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 33 + 7 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldCopyWithChild()] + + removed call to java/util/stream/Stream::forEach + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + copyTree + (Ledu/hm/hafner/coverage/CoverageNode;)Ledu/hm/hafner/coverage/CoverageNode; + 472 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 43 + 10 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldCopyLeaf()] + + removed call to java/util/List::forEach + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + copyTree + (Ledu/hm/hafner/coverage/CoverageNode;)Ledu/hm/hafner/coverage/CoverageNode; + 474 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 47 + 11 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldCopyLeaf()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageNode::copyTree + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + createChild + (Ljava/lang/String;)Ledu/hm/hafner/coverage/CoverageNode; + 499 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 22 + 7 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldSplitPackagesWithMultipleDots()] + + negated conditional + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + createChild + (Ljava/lang/String;)Ledu/hm/hafner/coverage/CoverageNode; + 505 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 43 + 11 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldSplitPackagesWithMultipleDots()] + + removed call to edu/hm/hafner/coverage/CoverageNode::add + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + createChild + (Ljava/lang/String;)Ledu/hm/hafner/coverage/CoverageNode; + 500 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 26 + 8 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldSplitPackagesWithMultipleDots()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageNode::createChild + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + createChild + (Ljava/lang/String;)Ledu/hm/hafner/coverage/CoverageNode; + 506 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 47 + 12 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldSplitPackagesWithMultipleDots()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageNode::createChild + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + find + (Ledu/hm/hafner/coverage/CoverageMetric;Ljava/lang/String;)Ljava/util/Optional; + + 362 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 7 + 1 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldFindMetric()] + + negated conditional + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + find + (Ledu/hm/hafner/coverage/CoverageMetric;Ljava/lang/String;)Ljava/util/Optional; + + 363 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 12 + 3 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldFindMetric()] + + replaced return value with Optional.empty for edu/hm/hafner/coverage/CoverageNode::find + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + find + (Ledu/hm/hafner/coverage/CoverageMetric;Ljava/lang/String;)Ljava/util/Optional; + + 365 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 34 + 8 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldFindMetric()] + + replaced return value with Optional.empty for edu/hm/hafner/coverage/CoverageNode::find + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + findByHashCode + (Ledu/hm/hafner/coverage/CoverageMetric;I)Ljava/util/Optional; + 382 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 7 + 1 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldFindMetric()] + + negated conditional + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + findByHashCode + (Ledu/hm/hafner/coverage/CoverageMetric;I)Ljava/util/Optional; + 383 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 12 + 3 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldFindMetric()] + + replaced return value with Optional.empty for edu/hm/hafner/coverage/CoverageNode::findByHashCode + + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + findByHashCode + (Ledu/hm/hafner/coverage/CoverageMetric;I)Ljava/util/Optional; + 385 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 34 + 8 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldFindMetric()] + + replaced return value with Optional.empty for edu/hm/hafner/coverage/CoverageNode::findByHashCode + + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + getAll + (Ledu/hm/hafner/coverage/CoverageMetric;)Ljava/util/List; + 345 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 40 + 9 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldSplitPackagesWithSingleDot()] + + negated conditional + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + getAll + (Ledu/hm/hafner/coverage/CoverageMetric;)Ljava/util/List; + 340 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 15 + 2 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldThrowExceptionWithLeafMetric()] + + removed call to edu/hm/hafner/util/Ensure$BooleanCondition::isFalse + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + getAll + (Ledu/hm/hafner/coverage/CoverageMetric;)Ljava/util/List; + 348 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 51 + 12 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldSplitPackagesWithSingleDot()] + + replaced return value with Collections.emptyList for edu/hm/hafner/coverage/CoverageNode::getAll + + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + getChildren + ()Ljava/util/List; + 156 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 5 + 0 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldAddChildAndLeaf()] + + replaced return value with Collections.emptyList for edu/hm/hafner/coverage/CoverageNode::getChildren + + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + getCoverage + (Ledu/hm/hafner/coverage/CoverageMetric;)Ledu/hm/hafner/coverage/Coverage; + 272 + org.pitest.mutationtest.engine.gregor.mutators.ConditionalsBoundaryMutator + 45 + 11 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldGetModuleCoverage()] + + changed conditional boundary + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + getCoverage + (Ledu/hm/hafner/coverage/CoverageMetric;)Ledu/hm/hafner/coverage/Coverage; + 265 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 11 + 2 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldGetMetricsDistributionAndPercentages()] + + negated conditional + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + getCoverage + (Ledu/hm/hafner/coverage/CoverageMetric;)Ledu/hm/hafner/coverage/Coverage; + 271 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 38 + 8 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldGetMetricsDistributionAndPercentages()] + + negated conditional + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + getCoverage + (Ledu/hm/hafner/coverage/CoverageMetric;)Ledu/hm/hafner/coverage/Coverage; + 272 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 45 + 11 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldGetMetricsDistributionAndPercentages()] + + negated conditional + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + getCoverage + (Ledu/hm/hafner/coverage/CoverageMetric;)Ledu/hm/hafner/coverage/Coverage; + 266 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 30 + 6 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldGetMetricsDistributionAndPercentages()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageNode::getCoverage + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + getCoverage + (Ledu/hm/hafner/coverage/CoverageMetric;)Ledu/hm/hafner/coverage/Coverage; + 273 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 51 + 13 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldGetMetricsDistributionAndPercentages()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageNode::getCoverage + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + getCoverage + (Ledu/hm/hafner/coverage/CoverageMetric;)Ledu/hm/hafner/coverage/Coverage; + 276 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 58 + 15 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldGetModuleCoverage()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageNode::getCoverage + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + getCoverage + (Ledu/hm/hafner/coverage/CoverageMetric;)Ledu/hm/hafner/coverage/Coverage; + 279 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 63 + 16 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldGetModuleCoverage()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageNode::getCoverage + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + getLeaves + ()Ljava/util/List; + 160 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 5 + 0 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldAddChildAndLeaf()] + + replaced return value with Collections.emptyList for edu/hm/hafner/coverage/CoverageNode::getLeaves + + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + getMetric + ()Ledu/hm/hafner/coverage/CoverageMetric; + 113 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 5 + 0 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldTextuallyRepresent()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageNode::getMetric + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + getMetrics + ()Ljava/util/NavigableSet; + 128 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 40 + 10 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldGetMetricsDistributionAndPercentages()] + + removed call to java/util/stream/Stream::forEach + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + getMetrics + ()Ljava/util/NavigableSet; + 130 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 44 + 11 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldGetMetrics()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageNode::getMetrics + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + getMetricsDistribution + ()Ljava/util/NavigableMap; + 139 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 18 + 5 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldGetMetricsDistributionAndPercentages()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageNode::getMetricsDistribution + + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + getMetricsPercentages + ()Ljava/util/NavigableMap; + 144 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 18 + 5 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldGetMetricsDistributionAndPercentages()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageNode::getMetricsPercentages + + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + getName + ()Ljava/lang/String; + 152 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 5 + 0 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldTextuallyRepresent()] + + replaced return value with "" for edu/hm/hafner/coverage/CoverageNode::getName + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + getParent + ()Ledu/hm/hafner/coverage/CoverageNode; + 71 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 5 + 0 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldThrowExceptionWithoutParent()] + + negated conditional + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + getParent + ()Ledu/hm/hafner/coverage/CoverageNode; + 74 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 18 + 3 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldAddChildren()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageNode::getParent + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + getParentName + ()Ljava/lang/String; + 216 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 5 + 0 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldAddChildren()] + + negated conditional + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + getParentName + ()Ljava/lang/String; + 222 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 31 + 5 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldAddChildren()] + + negated conditional + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + getParentName + ()Ljava/lang/String; + 222 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 36 + 8 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldAddChildren()] + + negated conditional + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + getParentName + ()Ljava/lang/String; + 223 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 43 + 10 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldAddChildren()] + + removed call to java/util/List::add + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + getParentName + ()Ljava/lang/String; + 217 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 9 + 1 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldAddChildren()] + + replaced return value with "" for edu/hm/hafner/coverage/CoverageNode::getParentName + + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + getParentName + ()Ljava/lang/String; + 225 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 56 + 13 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldAddChildren()] + + replaced return value with "" for edu/hm/hafner/coverage/CoverageNode::getParentName + + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + hasParent + ()Z + 203 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 5 + 1 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldAddChildren()] + + negated conditional + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + hasParent + ()Z + 203 + org.pitest.mutationtest.engine.gregor.mutators.returns.BooleanTrueReturnValsMutator + 13 + 4 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldAddChildren()] + + replaced boolean return with true for edu/hm/hafner/coverage/CoverageNode::hasParent + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + insertPackage + (Ledu/hm/hafner/coverage/CoverageNode;Ljava/util/Deque;)V + 489 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 17 + 3 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldSplitPackagesWithMultipleDots()] + + negated conditional + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + insertPackage + (Ledu/hm/hafner/coverage/CoverageNode;Ljava/util/Deque;)V + 490 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 23 + 4 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldSplitPackagesWithMultipleDots()] + + removed call to edu/hm/hafner/coverage/CoverageNode::addAll + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + insertPackage + (Ledu/hm/hafner/coverage/CoverageNode;Ljava/util/Deque;)V + 493 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 31 + 6 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldSplitPackagesWithMultipleDots()] + + removed call to edu/hm/hafner/coverage/CoverageNode::insertPackage + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + isRoot + ()Z + 194 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 5 + 0 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldAddChildren()] + + negated conditional + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + isRoot + ()Z + 194 + org.pitest.mutationtest.engine.gregor.mutators.returns.BooleanTrueReturnValsMutator + 13 + 3 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldAddChildren()] + + replaced boolean return with true for edu/hm/hafner/coverage/CoverageNode::isRoot + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + lambda$aggregateChildren$4 + + (Ledu/hm/hafner/coverage/CoverageMetric;Ledu/hm/hafner/coverage/CoverageNode;)Ledu/hm/hafner/coverage/Coverage; + + 285 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 6 + 1 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldGetModuleCoverage()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageNode::lambda$aggregateChildren$4 + + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + lambda$copyTree$12 + (Ledu/hm/hafner/coverage/CoverageNode;)Ledu/hm/hafner/coverage/CoverageNode; + 470 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 6 + 1 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldCopyWithChild()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageNode::lambda$copyTree$12 + + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + lambda$find$7 + + (Ledu/hm/hafner/coverage/CoverageMetric;Ljava/lang/String;Ledu/hm/hafner/coverage/CoverageNode;)Ljava/util/Optional; + + 366 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 7 + 1 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldFindMetric()] + + replaced return value with Optional.empty for edu/hm/hafner/coverage/CoverageNode::lambda$find$7 + + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + lambda$find$8 + (Ljava/util/Optional;)Ljava/util/stream/Stream; + 367 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 9 + 2 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldFindMetric()] + + replaced return value with Stream.empty for edu/hm/hafner/coverage/CoverageNode::lambda$find$8 + + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + lambda$findByHashCode$10 + (Ljava/util/Optional;)Ljava/util/stream/Stream; + 387 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 9 + 2 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldFindMetric()] + + replaced return value with Stream.empty for + edu/hm/hafner/coverage/CoverageNode::lambda$findByHashCode$10 + + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + lambda$findByHashCode$9 + + (Ledu/hm/hafner/coverage/CoverageMetric;ILedu/hm/hafner/coverage/CoverageNode;)Ljava/util/Optional; + + 386 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 7 + 1 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldFindMetric()] + + replaced return value with Optional.empty for + edu/hm/hafner/coverage/CoverageNode::lambda$findByHashCode$9 + + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + lambda$getAll$6 + (Ledu/hm/hafner/coverage/CoverageMetric;Ledu/hm/hafner/coverage/CoverageNode;)Ljava/util/List; + + 343 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 6 + 1 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldSplitPackagesWithSingleDot()] + + replaced return value with Collections.emptyList for + edu/hm/hafner/coverage/CoverageNode::lambda$getAll$6 + + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + lambda$getCoverage$3 + + (Ledu/hm/hafner/coverage/CoverageMetric;Ledu/hm/hafner/coverage/CoverageLeaf;)Ledu/hm/hafner/coverage/Coverage; + + 267 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 6 + 1 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldGetMetricsDistributionAndPercentages()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageNode::lambda$getCoverage$3 + + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + lambda$getMetricsDistribution$0 + + (Ledu/hm/hafner/coverage/Coverage;Ledu/hm/hafner/coverage/Coverage;)Ledu/hm/hafner/coverage/Coverage; + + 140 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 4 + 0 + + replaced return value with null for + edu/hm/hafner/coverage/CoverageNode::lambda$getMetricsDistribution$0 + + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + lambda$getMetricsPercentages$1 + (Ledu/hm/hafner/coverage/CoverageMetric;)Lorg/apache/commons/lang3/math/Fraction; + + 146 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 7 + 2 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldGetMetricsDistributionAndPercentages()] + + replaced return value with null for + edu/hm/hafner/coverage/CoverageNode::lambda$getMetricsPercentages$1 + + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + lambda$getMetricsPercentages$2 + + (Lorg/apache/commons/lang3/math/Fraction;Lorg/apache/commons/lang3/math/Fraction;)Lorg/apache/commons/lang3/math/Fraction; + + 147 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 4 + 0 + + replaced return value with null for + edu/hm/hafner/coverage/CoverageNode::lambda$getMetricsPercentages$2 + + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + lambda$splitPackages$11 + (Ledu/hm/hafner/coverage/CoverageNode;)Z + 428 + org.pitest.mutationtest.engine.gregor.mutators.returns.BooleanFalseReturnValsMutator + 7 + 2 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldSplitPackagesWithMultipleDots()] + + replaced boolean return with false for edu/hm/hafner/coverage/CoverageNode::lambda$splitPackages$11 + + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + lambda$splitPackages$11 + (Ledu/hm/hafner/coverage/CoverageNode;)Z + 428 + org.pitest.mutationtest.engine.gregor.mutators.returns.BooleanTrueReturnValsMutator + 7 + 2 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldSplitPackagesWithoutPackageNodes()] + + replaced boolean return with true for edu/hm/hafner/coverage/CoverageNode::lambda$splitPackages$11 + + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + matches + (Ledu/hm/hafner/coverage/CoverageMetric;I)Z + 416 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 7 + 1 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldFindMetric()] + + negated conditional + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + matches + (Ledu/hm/hafner/coverage/CoverageMetric;I)Z + 419 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 19 + 4 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldFindMetric()] + + negated conditional + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + matches + (Ledu/hm/hafner/coverage/CoverageMetric;I)Z + 419 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 24 + 7 + + edu.hm.hafner.coverage.PackageCoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.PackageCoverageNodeTest]/[method:shouldMatchPath()] + + negated conditional + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + matches + (Ledu/hm/hafner/coverage/CoverageMetric;I)Z + 417 + org.pitest.mutationtest.engine.gregor.mutators.returns.BooleanTrueReturnValsMutator + 11 + 2 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldFindMetric()] + + replaced boolean return with true for edu/hm/hafner/coverage/CoverageNode::matches + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + matches + (Ledu/hm/hafner/coverage/CoverageMetric;I)Z + 419 + org.pitest.mutationtest.engine.gregor.mutators.returns.BooleanTrueReturnValsMutator + 34 + 10 + + edu.hm.hafner.coverage.PackageCoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.PackageCoverageNodeTest]/[method:shouldMatchPath()] + + replaced boolean return with true for edu/hm/hafner/coverage/CoverageNode::matches + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + matches + (Ledu/hm/hafner/coverage/CoverageMetric;Ljava/lang/String;)Z + 402 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 7 + 1 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldFindMetric()] + + negated conditional + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + matches + (Ledu/hm/hafner/coverage/CoverageMetric;Ljava/lang/String;)Z + 402 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 12 + 3 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldFindMetric()] + + negated conditional + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + matches + (Ledu/hm/hafner/coverage/CoverageMetric;Ljava/lang/String;)Z + 402 + org.pitest.mutationtest.engine.gregor.mutators.returns.BooleanTrueReturnValsMutator + 20 + 6 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldFindMetric()] + + replaced boolean return with true for edu/hm/hafner/coverage/CoverageNode::matches + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + mergePath + (Ljava/lang/String;)Ljava/lang/String; + 88 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 6 + 1 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldMergePath()] + + negated conditional + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + mergePath + (Ljava/lang/String;)Ljava/lang/String; + 92 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 16 + 4 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldMergePath()] + + negated conditional + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + mergePath + (Ljava/lang/String;)Ljava/lang/String; + 95 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 27 + 8 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldMergePath()] + + negated conditional + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + mergePath + (Ljava/lang/String;)Ljava/lang/String; + 98 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 37 + 11 + + edu.hm.hafner.coverage.PackageCoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.PackageCoverageNodeTest]/[method:shouldMergePath()] + + negated conditional + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + mergePath + (Ljava/lang/String;)Ljava/lang/String; + 96 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 31 + 9 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldMergePath()] + + replaced return value with "" for edu/hm/hafner/coverage/CoverageNode::mergePath + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + mergePath + (Ljava/lang/String;)Ljava/lang/String; + 99 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 41 + 12 + + edu.hm.hafner.coverage.PackageCoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.PackageCoverageNodeTest]/[method:shouldMergePath()] + + replaced return value with "" for edu/hm/hafner/coverage/CoverageNode::mergePath + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + mergePath + (Ljava/lang/String;)Ljava/lang/String; + 101 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 55 + 18 + + edu.hm.hafner.coverage.PackageCoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.PackageCoverageNodeTest]/[method:shouldMergePath()] + + replaced return value with "" for edu/hm/hafner/coverage/CoverageNode::mergePath + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + mergePath + (Ljava/lang/String;)Ljava/lang/String; + 104 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 60 + 19 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldMergePath()] + + replaced return value with "" for edu/hm/hafner/coverage/CoverageNode::mergePath + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + printCoverageFor + (Ledu/hm/hafner/coverage/CoverageMetric;)Ljava/lang/String; + 238 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 7 + 2 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldGetAndPrintLineCoverage()] + + replaced return value with "" for edu/hm/hafner/coverage/CoverageNode::printCoverageFor + + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + printCoverageFor + (Ledu/hm/hafner/coverage/CoverageMetric;Ljava/util/Locale;)Ljava/lang/String; + 252 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 8 + 2 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldGetAndPrintLineCoverage()] + + replaced return value with "" for edu/hm/hafner/coverage/CoverageNode::printCoverageFor + + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + saveSubtractFraction + + (Lorg/apache/commons/lang3/math/Fraction;Lorg/apache/commons/lang3/math/Fraction;)Lorg/apache/commons/lang3/math/Fraction; + + 323 + org.pitest.mutationtest.engine.gregor.mutators.MathMutator + 18 + 4 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldComputeDeltaWithOverflow()] + + Replaced double subtraction with addition + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + saveSubtractFraction + + (Lorg/apache/commons/lang3/math/Fraction;Lorg/apache/commons/lang3/math/Fraction;)Lorg/apache/commons/lang3/math/Fraction; + + 320 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 7 + 1 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldComputeDeltaWithOverflow()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageNode::saveSubtractFraction + + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + saveSubtractFraction + + (Lorg/apache/commons/lang3/math/Fraction;Lorg/apache/commons/lang3/math/Fraction;)Lorg/apache/commons/lang3/math/Fraction; + + 324 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 24 + 5 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldComputeDeltaWithOverflow()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageNode::saveSubtractFraction + + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + splitPackages + ()V + 434 + org.pitest.mutationtest.engine.gregor.mutators.ConditionalsBoundaryMutator + 59 + 16 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldKeepChildAfterSplit()] + + changed conditional boundary + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + splitPackages + ()V + 426 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 7 + 1 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldSplitPackagesWithMultipleDots()] + + negated conditional + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + splitPackages + ()V + 430 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 27 + 7 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldSplitPackagesWithMultipleDots()] + + negated conditional + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + splitPackages + ()V + 434 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 59 + 16 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldSplitPackagesWithSingleDot()] + + negated conditional + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + splitPackages + ()V + 431 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 32 + 8 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldSplitPackagesWithSingleDot()] + + removed call to java/util/List::clear + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + splitPackages + ()V + 436 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 73 + 19 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldSplitPackagesWithMultipleDots()] + + removed call to edu/hm/hafner/coverage/CoverageNode::insertPackage + + + CoverageNode.java + edu.hm.hafner.coverage.CoverageNode + splitPackages + ()V + 439 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 82 + 21 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldSplitPackagesWithSingleDot()] + + removed call to edu/hm/hafner/coverage/CoverageNode::add + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + <init> + (Ljava/lang/String;)V + 54 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 29 + 1 + + edu.hm.hafner.parser.CoberturaParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.CoberturaParserTest]/[method:shouldConvertCoberturaBigToTree()] + + removed call to edu/hm/hafner/parser/XmlParser::parseFile + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + endElement + (Ljavax/xml/stream/events/EndElement;)V + 251 + org.pitest.mutationtest.engine.gregor.mutators.ConditionalsBoundaryMutator + 75 + 16 + + edu.hm.hafner.parser.CoberturaParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.CoberturaParserTest]/[method:shouldConvertCoberturaBigToTree()] + + changed conditional boundary + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + endElement + (Ljavax/xml/stream/events/EndElement;)V + 251 + org.pitest.mutationtest.engine.gregor.mutators.MathMutator + 74 + 16 + + Replaced integer addition with subtraction + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + endElement + (Ljavax/xml/stream/events/EndElement;)V + 251 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 75 + 16 + + edu.hm.hafner.parser.CoberturaParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.CoberturaParserTest]/[method:shouldConvertCoberturaBigToTree()] + + negated conditional + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + endElement + (Ljavax/xml/stream/events/EndElement;)V + 249 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 67 + 15 + + edu.hm.hafner.parser.CoberturaParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.CoberturaParserTest]/[method:shouldConvertCoberturaBigToTree()] + + removed call to edu/hm/hafner/coverage/CoverageNode::add + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + endElement + (Ljavax/xml/stream/events/EndElement;)V + 254 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 99 + 19 + + removed call to edu/hm/hafner/coverage/CoverageNode::add + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + handleClassElement + (Ljavax/xml/stream/events/StartElement;)V + 131 + org.pitest.mutationtest.engine.gregor.mutators.MathMutator + 32 + 6 + + edu.hm.hafner.parser.CoberturaParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.CoberturaParserTest]/[method:shouldConvertCoberturaBigToTree()] + + Replaced integer subtraction with addition + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + handleClassElement + (Ljavax/xml/stream/events/StartElement;)V + 137 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 52 + 9 + + negated conditional + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + handleClassElement + (Ljavax/xml/stream/events/StartElement;)V + 148 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 67 + 12 + + edu.hm.hafner.parser.CoberturaParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.CoberturaParserTest]/[method:shouldConvertCoberturaBigToTree()] + + negated conditional + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + handleClassElement + (Ljavax/xml/stream/events/StartElement;)V + 138 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 61 + 10 + + removed call to java/util/List::forEach + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + handleClassElement + (Ljavax/xml/stream/events/StartElement;)V + 150 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 79 + 14 + + edu.hm.hafner.parser.CoberturaParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.CoberturaParserTest]/[method:shouldConvertCoberturaBigToTree()] + + removed call to edu/hm/hafner/coverage/FileCoverageNode::add + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + handleClassElement + (Ljavax/xml/stream/events/StartElement;)V + 151 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 85 + 15 + + edu.hm.hafner.parser.CoberturaParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.CoberturaParserTest]/[method:shouldConvertCoberturaBigToTree()] + + removed call to edu/hm/hafner/coverage/PackageCoverageNode::add + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + handleLineElement + (Ljavax/xml/stream/events/StartElement;)V + 181 + org.pitest.mutationtest.engine.gregor.mutators.ConditionalsBoundaryMutator + 53 + 15 + + edu.hm.hafner.parser.CoberturaParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.CoberturaParserTest]/[method:testAmountOfLinenumberTolines()] + + changed conditional boundary + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + handleLineElement + (Ljavax/xml/stream/events/StartElement;)V + 195 + org.pitest.mutationtest.engine.gregor.mutators.ConditionalsBoundaryMutator + 112 + 28 + + changed conditional boundary + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + handleLineElement + (Ljavax/xml/stream/events/StartElement;)V + 209 + org.pitest.mutationtest.engine.gregor.mutators.ConditionalsBoundaryMutator + 163 + 40 + + edu.hm.hafner.parser.CoberturaParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.CoberturaParserTest]/[method:shouldConvertCoberturaBigToTree()] + + changed conditional boundary + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + handleLineElement + (Ljavax/xml/stream/events/StartElement;)V + 221 + org.pitest.mutationtest.engine.gregor.mutators.ConditionalsBoundaryMutator + 210 + 48 + + changed conditional boundary + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + handleLineElement + (Ljavax/xml/stream/events/StartElement;)V + 210 + org.pitest.mutationtest.engine.gregor.mutators.MathMutator + 170 + 41 + + edu.hm.hafner.parser.CoberturaParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.CoberturaParserTest]/[method:shouldConvertCoberturaBigToTree()] + + Replaced integer addition with subtraction + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + handleLineElement + (Ljavax/xml/stream/events/StartElement;)V + 213 + org.pitest.mutationtest.engine.gregor.mutators.MathMutator + 180 + 42 + + edu.hm.hafner.parser.CoberturaParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.CoberturaParserTest]/[method:shouldConvertCoberturaBigToTree()] + + Replaced integer addition with subtraction + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + handleLineElement + (Ljavax/xml/stream/events/StartElement;)V + 222 + org.pitest.mutationtest.engine.gregor.mutators.MathMutator + 217 + 49 + + Replaced integer addition with subtraction + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + handleLineElement + (Ljavax/xml/stream/events/StartElement;)V + 225 + org.pitest.mutationtest.engine.gregor.mutators.MathMutator + 228 + 50 + + Replaced integer subtraction with addition + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + handleLineElement + (Ljavax/xml/stream/events/StartElement;)V + 225 + org.pitest.mutationtest.engine.gregor.mutators.MathMutator + 229 + 50 + + Replaced integer addition with subtraction + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + handleLineElement + (Ljavax/xml/stream/events/StartElement;)V + 171 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 30 + 7 + + negated conditional + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + handleLineElement + (Ljavax/xml/stream/events/StartElement;)V + 176 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 45 + 13 + + edu.hm.hafner.parser.CoberturaParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.CoberturaParserTest]/[method:shouldConvertCoberturaBigToTree()] + + negated conditional + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + handleLineElement + (Ljavax/xml/stream/events/StartElement;)V + 180 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 49 + 14 + + edu.hm.hafner.parser.CoberturaParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.CoberturaParserTest]/[method:shouldConvertCoberturaBigToTree()] + + negated conditional + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + handleLineElement + (Ljavax/xml/stream/events/StartElement;)V + 181 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 53 + 15 + + edu.hm.hafner.parser.CoberturaParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.CoberturaParserTest]/[method:testAmountOfLinenumberTolines()] + + negated conditional + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + handleLineElement + (Ljavax/xml/stream/events/StartElement;)V + 195 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 112 + 28 + + negated conditional + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + handleLineElement + (Ljavax/xml/stream/events/StartElement;)V + 208 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 159 + 39 + + edu.hm.hafner.parser.CoberturaParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.CoberturaParserTest]/[method:shouldConvertCoberturaBigToTree()] + + negated conditional + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + handleLineElement + (Ljavax/xml/stream/events/StartElement;)V + 209 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 163 + 40 + + edu.hm.hafner.parser.CoberturaParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.CoberturaParserTest]/[method:shouldConvertCoberturaBigToTree()] + + negated conditional + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + handleLineElement + (Ljavax/xml/stream/events/StartElement;)V + 221 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 210 + 48 + + negated conditional + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + lambda$handleClassElement$0 + + (Ljava/lang/String;Ledu/hm/hafner/coverage/CoverageNode;Ljava/util/concurrent/atomic/AtomicBoolean;Ledu/hm/hafner/coverage/CoverageNode;)V + + 139 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 7 + 2 + + negated conditional + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + lambda$handleClassElement$0 + + (Ljava/lang/String;Ledu/hm/hafner/coverage/CoverageNode;Ljava/util/concurrent/atomic/AtomicBoolean;Ledu/hm/hafner/coverage/CoverageNode;)V + + 140 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 12 + 3 + + removed call to edu/hm/hafner/coverage/CoverageNode::add + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + lambda$handleClassElement$0 + + (Ljava/lang/String;Ledu/hm/hafner/coverage/CoverageNode;Ljava/util/concurrent/atomic/AtomicBoolean;Ledu/hm/hafner/coverage/CoverageNode;)V + + 141 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 17 + 4 + + removed call to java/util/concurrent/atomic/AtomicBoolean::set + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + parseConditionCoverage + (Ljavax/xml/stream/events/StartElement;)[Ljava/lang/String; + 272 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 23 + 4 + + replaced return value with null for edu/hm/hafner/parser/CoberturaParser::parseConditionCoverage + + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + startDocument + (Ljavax/xml/stream/events/XMLEvent;)V + 67 + org.pitest.mutationtest.engine.gregor.mutators.MathMutator + 20 + 3 + + edu.hm.hafner.parser.CoberturaParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.CoberturaParserTest]/[method:shouldConvertCoberturaBigToTree()] + + Replaced integer subtraction with addition + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + startDocument + (Ljavax/xml/stream/events/XMLEvent;)V + 69 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 31 + 4 + + edu.hm.hafner.parser.CoberturaParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.CoberturaParserTest]/[method:shouldConvertCoberturaBigToTree()] + + removed call to edu/hm/hafner/parser/CoberturaParser::setRootNode + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + startElement + (Ljavax/xml/stream/events/StartElement;)V + 87 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 75 + 21 + + edu.hm.hafner.parser.CoberturaParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.CoberturaParserTest]/[method:shouldConvertCoberturaBigToTree()] + + removed call to edu/hm/hafner/coverage/CoverageNode::add + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + startElement + (Ljavax/xml/stream/events/StartElement;)V + 94 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 94 + 23 + + edu.hm.hafner.parser.CoberturaParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.CoberturaParserTest]/[method:shouldConvertCoberturaBigToTree()] + + removed call to edu/hm/hafner/parser/CoberturaParser::handleClassElement + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + startElement + (Ljavax/xml/stream/events/StartElement;)V + 105 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 137 + 33 + + edu.hm.hafner.parser.CoberturaParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.CoberturaParserTest]/[method:shouldConvertCoberturaBigToTree()] + + removed call to edu/hm/hafner/coverage/CoverageNode::add + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + startElement + (Ljavax/xml/stream/events/StartElement;)V + 107 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 143 + 34 + + edu.hm.hafner.parser.CoberturaParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.CoberturaParserTest]/[method:shouldConvertCoberturaBigToTree()] + + removed call to edu/hm/hafner/coverage/CoverageNode::add + + + CoberturaParser.java + edu.hm.hafner.metric.parser.CoberturaParser + startElement + (Ljavax/xml/stream/events/StartElement;)V + 112 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 157 + 36 + + edu.hm.hafner.parser.CoberturaParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.CoberturaParserTest]/[method:shouldConvertCoberturaBigToTree()] + + removed call to edu/hm/hafner/parser/CoberturaParser::handleLineElement + + + CoverageMetric.java + edu.hm.hafner.metric.Metric + <init> + (Ljava/lang/String;ILedu/hm/hafner/coverage/CoverageMetric$MetricType;)V + 152 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 20 + 1 + + edu.hm.hafner.coverage.CoverageMetricTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageMetricTest]/[method:shouldCreateNewMetric()] + + negated conditional + + + CoverageMetric.java + edu.hm.hafner.metric.Metric + compareTo + (Ledu/hm/hafner/coverage/CoverageMetric;)I + 170 + org.pitest.mutationtest.engine.gregor.mutators.MathMutator + 7 + 0 + + edu.hm.hafner.coverage.CoverageMetricTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageMetricTest]/[method:shouldSortMetrics()] + + Replaced integer subtraction with addition + + + CoverageMetric.java + edu.hm.hafner.metric.Metric + compareTo + (Ledu/hm/hafner/coverage/CoverageMetric;)I + 170 + org.pitest.mutationtest.engine.gregor.mutators.returns.PrimitiveReturnsMutator + 8 + 0 + + edu.hm.hafner.coverage.CoverageMetricTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageMetricTest]/[method:shouldSortMetrics()] + + replaced int return with 0 for edu/hm/hafner/coverage/CoverageMetric::compareTo + + + CoverageMetric.java + edu.hm.hafner.metric.Metric + equalsIgnoreCase + (Ljava/lang/String;)Z + 108 + org.pitest.mutationtest.engine.gregor.mutators.returns.BooleanFalseReturnValsMutator + 7 + 2 + + edu.hm.hafner.coverage.CoverageMetricTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageMetricTest]/[method:shouldCreatePredefinedMetrics()] + + replaced boolean return with false for edu/hm/hafner/coverage/CoverageMetric::equalsIgnoreCase + + + + CoverageMetric.java + edu.hm.hafner.metric.Metric + equalsIgnoreCase + (Ljava/lang/String;)Z + 108 + org.pitest.mutationtest.engine.gregor.mutators.returns.BooleanTrueReturnValsMutator + 7 + 2 + + edu.hm.hafner.coverage.CoverageMetricTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageMetricTest]/[method:shouldCreateNewMetric()] + + replaced boolean return with true for edu/hm/hafner/coverage/CoverageMetric::equalsIgnoreCase + + + + CoverageMetric.java + edu.hm.hafner.metric.Metric + equalsIgnoreCase + (Ljava/lang/String;Ljava/lang/String;)Z + 134 + org.pitest.mutationtest.engine.gregor.mutators.returns.BooleanFalseReturnValsMutator + 8 + 3 + + edu.hm.hafner.coverage.CoverageMetricTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageMetricTest]/[method:shouldCreatePredefinedMetrics()] + + replaced boolean return with false for edu/hm/hafner/coverage/CoverageMetric::equalsIgnoreCase + + + + CoverageMetric.java + edu.hm.hafner.metric.Metric + equalsIgnoreCase + (Ljava/lang/String;Ljava/lang/String;)Z + 134 + org.pitest.mutationtest.engine.gregor.mutators.returns.BooleanTrueReturnValsMutator + 8 + 3 + + edu.hm.hafner.coverage.CoverageMetricTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageMetricTest]/[method:shouldCreateNewMetric()] + + replaced boolean return with true for edu/hm/hafner/coverage/CoverageMetric::equalsIgnoreCase + + + + CoverageMetric.java + edu.hm.hafner.metric.Metric + getAvailableCoverageMetrics + ()Ljava/util/List; + 96 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 42 + 1 + + edu.hm.hafner.coverage.CoverageMetricTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageMetricTest]/[method:shouldGetAvailableMetrics()] + + replaced return value with Collections.emptyList for + edu/hm/hafner/coverage/CoverageMetric::getAvailableCoverageMetrics + + + + CoverageMetric.java + edu.hm.hafner.metric.Metric + getName + ()Ljava/lang/String; + 156 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 5 + 0 + + edu.hm.hafner.coverage.CoverageMetricTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageMetricTest]/[method:shouldCreateNewMetric()] + + replaced return value with "" for edu/hm/hafner/coverage/CoverageMetric::getName + + + CoverageMetric.java + edu.hm.hafner.metric.Metric + isLeaf + ()Z + 160 + org.pitest.mutationtest.engine.gregor.mutators.returns.BooleanFalseReturnValsMutator + 5 + 0 + + edu.hm.hafner.coverage.CoverageMetricTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageMetricTest]/[method:shouldCreatePredefinedMetrics()] + + replaced boolean return with false for edu/hm/hafner/coverage/CoverageMetric::isLeaf + + + CoverageMetric.java + edu.hm.hafner.metric.Metric + isLeaf + ()Z + 160 + org.pitest.mutationtest.engine.gregor.mutators.returns.BooleanTrueReturnValsMutator + 5 + 0 + + edu.hm.hafner.coverage.CoverageMetricTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageMetricTest]/[method:shouldCreateNewMetric()] + + replaced boolean return with true for edu/hm/hafner/coverage/CoverageMetric::isLeaf + + + CoverageMetric.java + edu.hm.hafner.metric.Metric + normalize + (Ljava/lang/String;)Ljava/lang/String; + 138 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 7 + 2 + + edu.hm.hafner.coverage.CoverageMetricTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageMetricTest]/[method:shouldCreateNewMetric()] + + replaced return value with "" for edu/hm/hafner/coverage/CoverageMetric::normalize + + + + CoverageMetric.java + edu.hm.hafner.metric.Metric + valueOf + (Ljava/lang/String;)Ledu/hm/hafner/coverage/CoverageMetric; + 60 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 6 + 1 + + edu.hm.hafner.coverage.CoverageMetricTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageMetricTest]/[method:shouldCreateNewMetric()] + + negated conditional + + + CoverageMetric.java + edu.hm.hafner.metric.Metric + valueOf + (Ljava/lang/String;)Ledu/hm/hafner/coverage/CoverageMetric; + 60 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 10 + 3 + + edu.hm.hafner.coverage.CoverageMetricTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageMetricTest]/[method:shouldCreateNewMetric()] + + negated conditional + + + CoverageMetric.java + edu.hm.hafner.metric.Metric + valueOf + (Ljava/lang/String;)Ledu/hm/hafner/coverage/CoverageMetric; + 63 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 22 + 6 + + edu.hm.hafner.coverage.CoverageMetricTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageMetricTest]/[method:shouldCreateNewMetric()] + + negated conditional + + + CoverageMetric.java + edu.hm.hafner.metric.Metric + valueOf + (Ljava/lang/String;)Ledu/hm/hafner/coverage/CoverageMetric; + 66 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 33 + 9 + + edu.hm.hafner.coverage.CoverageMetricTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageMetricTest]/[method:shouldCreateNewMetric()] + + negated conditional + + + CoverageMetric.java + edu.hm.hafner.metric.Metric + valueOf + (Ljava/lang/String;)Ledu/hm/hafner/coverage/CoverageMetric; + 69 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 44 + 12 + + edu.hm.hafner.coverage.CoverageMetricTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageMetricTest]/[method:shouldCreateNewMetric()] + + negated conditional + + + CoverageMetric.java + edu.hm.hafner.metric.Metric + valueOf + (Ljava/lang/String;)Ledu/hm/hafner/coverage/CoverageMetric; + 72 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 55 + 15 + + edu.hm.hafner.coverage.CoverageMetricTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageMetricTest]/[method:shouldCreateNewMetric()] + + negated conditional + + + CoverageMetric.java + edu.hm.hafner.metric.Metric + valueOf + (Ljava/lang/String;)Ledu/hm/hafner/coverage/CoverageMetric; + 75 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 66 + 18 + + edu.hm.hafner.coverage.CoverageMetricTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageMetricTest]/[method:shouldCreateNewMetric()] + + negated conditional + + + CoverageMetric.java + edu.hm.hafner.metric.Metric + valueOf + (Ljava/lang/String;)Ledu/hm/hafner/coverage/CoverageMetric; + 78 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 77 + 21 + + edu.hm.hafner.coverage.CoverageMetricTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageMetricTest]/[method:shouldCreateNewMetric()] + + negated conditional + + + CoverageMetric.java + edu.hm.hafner.metric.Metric + valueOf + (Ljava/lang/String;)Ledu/hm/hafner/coverage/CoverageMetric; + 81 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 88 + 24 + + edu.hm.hafner.coverage.CoverageMetricTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageMetricTest]/[method:shouldCreateNewMetric()] + + negated conditional + + + CoverageMetric.java + edu.hm.hafner.metric.Metric + valueOf + (Ljava/lang/String;)Ledu/hm/hafner/coverage/CoverageMetric; + 81 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 92 + 26 + + edu.hm.hafner.coverage.CoverageMetricTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageMetricTest]/[method:shouldCreateNewMetric()] + + negated conditional + + + CoverageMetric.java + edu.hm.hafner.metric.Metric + valueOf + (Ljava/lang/String;)Ledu/hm/hafner/coverage/CoverageMetric; + 84 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 104 + 29 + + edu.hm.hafner.coverage.CoverageMetricTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageMetricTest]/[method:shouldCreateNewMetric()] + + negated conditional + + + CoverageMetric.java + edu.hm.hafner.metric.Metric + valueOf + (Ljava/lang/String;)Ledu/hm/hafner/coverage/CoverageMetric; + 61 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 15 + 4 + + edu.hm.hafner.coverage.CoverageMetricTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageMetricTest]/[method:shouldCreatePredefinedMetrics()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageMetric::valueOf + + + CoverageMetric.java + edu.hm.hafner.metric.Metric + valueOf + (Ljava/lang/String;)Ledu/hm/hafner/coverage/CoverageMetric; + 64 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 26 + 7 + + edu.hm.hafner.coverage.CoverageMetricTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageMetricTest]/[method:shouldCreatePredefinedMetrics()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageMetric::valueOf + + + CoverageMetric.java + edu.hm.hafner.metric.Metric + valueOf + (Ljava/lang/String;)Ledu/hm/hafner/coverage/CoverageMetric; + 67 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 37 + 10 + + edu.hm.hafner.coverage.CoverageMetricTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageMetricTest]/[method:shouldCreatePredefinedMetrics()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageMetric::valueOf + + + CoverageMetric.java + edu.hm.hafner.metric.Metric + valueOf + (Ljava/lang/String;)Ledu/hm/hafner/coverage/CoverageMetric; + 70 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 48 + 13 + + edu.hm.hafner.coverage.CoverageMetricTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageMetricTest]/[method:shouldCreatePredefinedMetrics()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageMetric::valueOf + + + CoverageMetric.java + edu.hm.hafner.metric.Metric + valueOf + (Ljava/lang/String;)Ledu/hm/hafner/coverage/CoverageMetric; + 73 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 59 + 16 + + edu.hm.hafner.coverage.CoverageMetricTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageMetricTest]/[method:shouldCreatePredefinedMetrics()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageMetric::valueOf + + + CoverageMetric.java + edu.hm.hafner.metric.Metric + valueOf + (Ljava/lang/String;)Ledu/hm/hafner/coverage/CoverageMetric; + 76 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 70 + 19 + + edu.hm.hafner.coverage.CoverageMetricTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageMetricTest]/[method:shouldCreatePredefinedMetrics()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageMetric::valueOf + + + CoverageMetric.java + edu.hm.hafner.metric.Metric + valueOf + (Ljava/lang/String;)Ledu/hm/hafner/coverage/CoverageMetric; + 79 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 81 + 22 + + edu.hm.hafner.coverage.CoverageMetricTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageMetricTest]/[method:shouldCreatePredefinedMetrics()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageMetric::valueOf + + + CoverageMetric.java + edu.hm.hafner.metric.Metric + valueOf + (Ljava/lang/String;)Ledu/hm/hafner/coverage/CoverageMetric; + 82 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 97 + 27 + + edu.hm.hafner.coverage.CoverageMetricTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageMetricTest]/[method:shouldCreatePredefinedMetrics()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageMetric::valueOf + + + CoverageMetric.java + edu.hm.hafner.metric.Metric + valueOf + (Ljava/lang/String;)Ledu/hm/hafner/coverage/CoverageMetric; + 85 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 108 + 30 + + edu.hm.hafner.coverage.CoverageMetricTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageMetricTest]/[method:shouldCreatePredefinedMetrics()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageMetric::valueOf + + + CoverageMetric.java + edu.hm.hafner.metric.Metric + valueOf + (Ljava/lang/String;)Ledu/hm/hafner/coverage/CoverageMetric; + 87 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 117 + 32 + + edu.hm.hafner.coverage.CoverageMetricTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageMetricTest]/[method:shouldCreateNewMetric()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageMetric::valueOf + + + Coverage.java + edu.hm.hafner.metric.Coverage + add + (Ledu/hm/hafner/coverage/Coverage;)Ledu/hm/hafner/coverage/Coverage; + 148 + org.pitest.mutationtest.engine.gregor.mutators.MathMutator + 9 + 1 + + edu.hm.hafner.coverage.CoverageTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageTest]/[method:shouldCreatePercentages()] + + Replaced integer addition with subtraction + + + Coverage.java + edu.hm.hafner.metric.Coverage + add + (Ledu/hm/hafner/coverage/Coverage;)Ledu/hm/hafner/coverage/Coverage; + 149 + org.pitest.mutationtest.engine.gregor.mutators.MathMutator + 16 + 2 + + edu.hm.hafner.coverage.CoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageNodeTest]/[method:shouldGetMetricsDistributionAndPercentages()] + + Replaced integer addition with subtraction + + + Coverage.java + edu.hm.hafner.metric.Coverage + add + (Ledu/hm/hafner/coverage/Coverage;)Ledu/hm/hafner/coverage/Coverage; + 148 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 20 + 3 + + edu.hm.hafner.coverage.CoverageTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageTest]/[method:shouldProvideNullObject()] + + replaced return value with null for edu/hm/hafner/coverage/Coverage::add + + + Coverage.java + edu.hm.hafner.metric.Coverage + formatCoveredPercentage + ()Ljava/lang/String; + 74 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 6 + 2 + + edu.hm.hafner.coverage.CoverageTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageTest]/[method:shouldProvideNullObject()] + + replaced return value with "" for edu/hm/hafner/coverage/Coverage::formatCoveredPercentage + + + + Coverage.java + edu.hm.hafner.metric.Coverage + formatCoveredPercentage + (Ljava/util/Locale;)Ljava/lang/String; + 86 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 8 + 2 + + edu.hm.hafner.coverage.CoverageTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageTest]/[method:shouldProvideNullObject()] + + replaced return value with "" for edu/hm/hafner/coverage/Coverage::formatCoveredPercentage + + + + Coverage.java + edu.hm.hafner.metric.Coverage + formatMissedPercentage + ()Ljava/lang/String; + 117 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 6 + 2 + + edu.hm.hafner.coverage.CoverageTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageTest]/[method:shouldProvideNullObject()] + + replaced return value with "" for edu/hm/hafner/coverage/Coverage::formatMissedPercentage + + + + Coverage.java + edu.hm.hafner.metric.Coverage + formatMissedPercentage + (Ljava/util/Locale;)Ljava/lang/String; + 129 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 8 + 2 + + edu.hm.hafner.coverage.CoverageTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageTest]/[method:shouldProvideNullObject()] + + replaced return value with "" for edu/hm/hafner/coverage/Coverage::formatMissedPercentage + + + + Coverage.java + edu.hm.hafner.metric.Coverage + getCovered + ()I + 51 + org.pitest.mutationtest.engine.gregor.mutators.returns.PrimitiveReturnsMutator + 5 + 0 + + edu.hm.hafner.coverage.CoverageTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageTest]/[method:shouldCreatePercentages()] + + replaced int return with 0 for edu/hm/hafner/coverage/Coverage::getCovered + + + Coverage.java + edu.hm.hafner.metric.Coverage + getCoveredPercentage + ()Lorg/apache/commons/lang3/math/Fraction; + 60 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 5 + 1 + + edu.hm.hafner.coverage.CoverageTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageTest]/[method:shouldProvideNullObject()] + + negated conditional + + + Coverage.java + edu.hm.hafner.metric.Coverage + getCoveredPercentage + ()Lorg/apache/commons/lang3/math/Fraction; + 61 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 9 + 2 + + edu.hm.hafner.coverage.CoverageTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageTest]/[method:shouldProvideNullObject()] + + replaced return value with null for edu/hm/hafner/coverage/Coverage::getCoveredPercentage + + + Coverage.java + edu.hm.hafner.metric.Coverage + getCoveredPercentage + ()Lorg/apache/commons/lang3/math/Fraction; + 63 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 18 + 5 + + edu.hm.hafner.coverage.CoverageTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageTest]/[method:shouldCreatePercentages()] + + replaced return value with null for edu/hm/hafner/coverage/Coverage::getCoveredPercentage + + + Coverage.java + edu.hm.hafner.metric.Coverage + getMissed + ()I + 95 + org.pitest.mutationtest.engine.gregor.mutators.returns.PrimitiveReturnsMutator + 5 + 0 + + edu.hm.hafner.coverage.CoverageTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageTest]/[method:shouldCreatePercentages()] + + replaced int return with 0 for edu/hm/hafner/coverage/Coverage::getMissed + + + Coverage.java + edu.hm.hafner.metric.Coverage + getMissedPercentage + ()Lorg/apache/commons/lang3/math/Fraction; + 104 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 5 + 1 + + edu.hm.hafner.coverage.CoverageTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageTest]/[method:shouldProvideNullObject()] + + negated conditional + + + Coverage.java + edu.hm.hafner.metric.Coverage + getMissedPercentage + ()Lorg/apache/commons/lang3/math/Fraction; + 105 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 9 + 2 + + edu.hm.hafner.coverage.CoverageTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageTest]/[method:shouldProvideNullObject()] + + replaced return value with null for edu/hm/hafner/coverage/Coverage::getMissedPercentage + + + Coverage.java + edu.hm.hafner.metric.Coverage + getMissedPercentage + ()Lorg/apache/commons/lang3/math/Fraction; + 107 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 17 + 5 + + edu.hm.hafner.coverage.CoverageTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageTest]/[method:shouldCreatePercentages()] + + replaced return value with null for edu/hm/hafner/coverage/Coverage::getMissedPercentage + + + Coverage.java + edu.hm.hafner.metric.Coverage + getTotal + ()I + 162 + org.pitest.mutationtest.engine.gregor.mutators.MathMutator + 7 + 0 + + edu.hm.hafner.coverage.CoverageTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageTest]/[method:shouldCreatePercentages()] + + Replaced integer addition with subtraction + + + Coverage.java + edu.hm.hafner.metric.Coverage + getTotal + ()I + 162 + org.pitest.mutationtest.engine.gregor.mutators.returns.PrimitiveReturnsMutator + 8 + 0 + + edu.hm.hafner.coverage.CoverageTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageTest]/[method:shouldCreatePercentages()] + + replaced int return with 0 for edu/hm/hafner/coverage/Coverage::getTotal + + + Coverage.java + edu.hm.hafner.metric.Coverage + isSet + ()Z + 166 + org.pitest.mutationtest.engine.gregor.mutators.ConditionalsBoundaryMutator + 5 + 1 + + edu.hm.hafner.coverage.CoverageTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageTest]/[method:shouldProvideNullObject()] + + changed conditional boundary + + + Coverage.java + edu.hm.hafner.metric.Coverage + isSet + ()Z + 166 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 5 + 1 + + edu.hm.hafner.coverage.CoverageTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageTest]/[method:shouldProvideNullObject()] + + negated conditional + + + Coverage.java + edu.hm.hafner.metric.Coverage + isSet + ()Z + 166 + org.pitest.mutationtest.engine.gregor.mutators.returns.BooleanTrueReturnValsMutator + 13 + 4 + + edu.hm.hafner.coverage.CoverageTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageTest]/[method:shouldProvideNullObject()] + + replaced boolean return with true for edu/hm/hafner/coverage/Coverage::isSet + + + Coverage.java + edu.hm.hafner.metric.Coverage + printPercentage + (Ljava/util/Locale;Lorg/apache/commons/lang3/math/Fraction;)Ljava/lang/String; + + 133 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 5 + 1 + + edu.hm.hafner.coverage.CoverageTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageTest]/[method:shouldProvideNullObject()] + + negated conditional + + + Coverage.java + edu.hm.hafner.metric.Coverage + printPercentage + (Ljava/util/Locale;Lorg/apache/commons/lang3/math/Fraction;)Ljava/lang/String; + + 134 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 21 + 6 + + edu.hm.hafner.coverage.CoverageTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageTest]/[method:shouldCreatePercentages()] + + replaced return value with "" for edu/hm/hafner/coverage/Coverage::printPercentage + + + + Coverage.java + edu.hm.hafner.metric.Coverage + printPercentage + (Ljava/util/Locale;Lorg/apache/commons/lang3/math/Fraction;)Ljava/lang/String; + + 136 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 26 + 7 + + edu.hm.hafner.coverage.CoverageTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageTest]/[method:shouldProvideNullObject()] + + replaced return value with "" for edu/hm/hafner/coverage/Coverage::printPercentage + + + + JacocoParser.java + edu.hm.hafner.metric.parser.JacocoParser + <init> + (Ljava/lang/String;)V + 47 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 16 + 2 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldCreatePackageName()] + + removed call to edu/hm/hafner/parser/JacocoParser::parseFile + + + JacocoParser.java + edu.hm.hafner.metric.parser.JacocoParser + handleClassElement + (Ljavax/xml/stream/events/StartElement;)V + 137 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 25 + 6 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldThrowExceptionWhenObtainingAllBasicBlocks()] + + negated conditional + + + JacocoParser.java + edu.hm.hafner.metric.parser.JacocoParser + handleCounterElement + (Ljavax/xml/stream/events/StartElement;)V + 158 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 15 + 4 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldSplitPackages()] + + negated conditional + + + JacocoParser.java + edu.hm.hafner.metric.parser.JacocoParser + handleCounterElement + (Ljavax/xml/stream/events/StartElement;)V + 184 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 134 + 34 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldSplitPackages()] + + removed call to edu/hm/hafner/coverage/CoverageNode::add + + + JacocoParser.java + edu.hm.hafner.metric.parser.JacocoParser + handleLineElement + (Ljavax/xml/stream/events/StartElement;)V + 199 + org.pitest.mutationtest.engine.gregor.mutators.ConditionalsBoundaryMutator + 44 + 15 + + changed conditional boundary + + + JacocoParser.java + edu.hm.hafner.metric.parser.JacocoParser + handleLineElement + (Ljavax/xml/stream/events/StartElement;)V + 199 + org.pitest.mutationtest.engine.gregor.mutators.ConditionalsBoundaryMutator + 46 + 16 + + changed conditional boundary + + + JacocoParser.java + edu.hm.hafner.metric.parser.JacocoParser + handleLineElement + (Ljavax/xml/stream/events/StartElement;)V + 205 + org.pitest.mutationtest.engine.gregor.mutators.ConditionalsBoundaryMutator + 75 + 23 + + changed conditional boundary + + + JacocoParser.java + edu.hm.hafner.metric.parser.JacocoParser + handleLineElement + (Ljavax/xml/stream/events/StartElement;)V + 205 + org.pitest.mutationtest.engine.gregor.mutators.ConditionalsBoundaryMutator + 77 + 24 + + changed conditional boundary + + + JacocoParser.java + edu.hm.hafner.metric.parser.JacocoParser + handleLineElement + (Ljavax/xml/stream/events/StartElement;)V + 199 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 44 + 15 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldSplitPackages()] + + negated conditional + + + JacocoParser.java + edu.hm.hafner.metric.parser.JacocoParser + handleLineElement + (Ljavax/xml/stream/events/StartElement;)V + 199 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 46 + 16 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldSplitPackages()] + + negated conditional + + + JacocoParser.java + edu.hm.hafner.metric.parser.JacocoParser + handleLineElement + (Ljavax/xml/stream/events/StartElement;)V + 205 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 75 + 23 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldSplitPackages()] + + negated conditional + + + JacocoParser.java + edu.hm.hafner.metric.parser.JacocoParser + handleLineElement + (Ljavax/xml/stream/events/StartElement;)V + 205 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 77 + 24 + + negated conditional + + + JacocoParser.java + edu.hm.hafner.metric.parser.JacocoParser + startDocument + (Ljavax/xml/stream/events/XMLEvent;)V + 54 + org.pitest.mutationtest.engine.gregor.mutators.MathMutator + 21 + 3 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldThrowExceptionWhenObtainingAllBasicBlocks()] + + Replaced integer subtraction with addition + + + JacocoParser.java + edu.hm.hafner.metric.parser.JacocoParser + startElement + (Ljavax/xml/stream/events/StartElement;)V + 72 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 108 + 34 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldNotSplitPackagesIfOnWrongHierarchyNode()] + + removed call to edu/hm/hafner/parser/JacocoParser::setRootNode + + + JacocoParser.java + edu.hm.hafner.metric.parser.JacocoParser + startElement + (Ljavax/xml/stream/events/StartElement;)V + 79 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 141 + 42 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldNotSplitPackagesIfOnWrongHierarchyNode()] + + removed call to edu/hm/hafner/coverage/CoverageNode::add + + + JacocoParser.java + edu.hm.hafner.metric.parser.JacocoParser + startElement + (Ljavax/xml/stream/events/StartElement;)V + 86 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 160 + 44 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldThrowExceptionWhenObtainingAllBasicBlocks()] + + removed call to edu/hm/hafner/parser/JacocoParser::handleClassElement + + + JacocoParser.java + edu.hm.hafner.metric.parser.JacocoParser + startElement + (Ljavax/xml/stream/events/StartElement;)V + 93 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 183 + 49 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldThrowExceptionWhenObtainingAllBasicBlocks()] + + removed call to edu/hm/hafner/coverage/CoverageNode::add + + + JacocoParser.java + edu.hm.hafner.metric.parser.JacocoParser + startElement + (Ljavax/xml/stream/events/StartElement;)V + 98 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 197 + 51 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldSplitPackages()] + + removed call to edu/hm/hafner/parser/JacocoParser::handleCounterElement + + + JacocoParser.java + edu.hm.hafner.metric.parser.JacocoParser + startElement + (Ljavax/xml/stream/events/StartElement;)V + 108 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 242 + 62 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldSplitPackages()] + + removed call to edu/hm/hafner/coverage/CoverageNode::add + + + JacocoParser.java + edu.hm.hafner.metric.parser.JacocoParser + startElement + (Ljavax/xml/stream/events/StartElement;)V + 111 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 252 + 64 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldCreatePackageName()] + + removed call to edu/hm/hafner/coverage/CoverageNode::add + + + JacocoParser.java + edu.hm.hafner.metric.parser.JacocoParser + startElement + (Ljavax/xml/stream/events/StartElement;)V + 116 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 266 + 66 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldSplitPackages()] + + removed call to edu/hm/hafner/parser/JacocoParser::handleLineElement + + + FileCoverageNode.java + edu.hm.hafner.coverage.FileCoverageNode + copyEmpty + ()Ledu/hm/hafner/coverage/CoverageNode; + 86 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 8 + 2 + + edu.hm.hafner.metric.FileNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.metric.FileNodeTest]/[method:shouldCopyEmpty()] + + replaced return value with null for edu/hm/hafner/coverage/FileCoverageNode::copyEmpty + + + FileCoverageNode.java + edu.hm.hafner.coverage.FileCoverageNode + getCoveredBranchesCount + ()J + 74 + org.pitest.mutationtest.engine.gregor.mutators.returns.PrimitiveReturnsMutator + 17 + 4 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldSplitPackages()] + + replaced long return with 0 for edu/hm/hafner/coverage/FileCoverageNode::getCoveredBranchesCount + + + + FileCoverageNode.java + edu.hm.hafner.coverage.FileCoverageNode + getCoveredInstructionsCount + ()J + 52 + org.pitest.mutationtest.engine.gregor.mutators.returns.PrimitiveReturnsMutator + 17 + 4 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldSplitPackages()] + + replaced long return with 0 for edu/hm/hafner/coverage/FileCoverageNode::getCoveredInstructionsCount + + + + FileCoverageNode.java + edu.hm.hafner.coverage.FileCoverageNode + getLineNumberToBranchCoverage + ()Ljava/util/Map; + 28 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 5 + 0 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldThrowExceptionWhenObtainingAllBasicBlocks()] + + replaced return value with Collections.emptyMap for + edu/hm/hafner/coverage/FileCoverageNode::getLineNumberToBranchCoverage + + + + FileCoverageNode.java + edu.hm.hafner.coverage.FileCoverageNode + getLineNumberToInstructionCoverage + ()Ljava/util/Map; + 32 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 5 + 0 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldThrowExceptionWhenObtainingAllBasicBlocks()] + + replaced return value with Collections.emptyMap for + edu/hm/hafner/coverage/FileCoverageNode::getLineNumberToInstructionCoverage + + + + FileCoverageNode.java + edu.hm.hafner.coverage.FileCoverageNode + getMissedBranchesCount + ()J + 63 + org.pitest.mutationtest.engine.gregor.mutators.returns.PrimitiveReturnsMutator + 17 + 4 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldSplitPackages()] + + replaced long return with 0 for edu/hm/hafner/coverage/FileCoverageNode::getMissedBranchesCount + + + + FileCoverageNode.java + edu.hm.hafner.coverage.FileCoverageNode + getMissedInstructionsCount + ()J + 41 + org.pitest.mutationtest.engine.gregor.mutators.returns.PrimitiveReturnsMutator + 17 + 4 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldSplitPackages()] + + replaced long return with 0 for edu/hm/hafner/coverage/FileCoverageNode::getMissedInstructionsCount + + + + FileCoverageNode.java + edu.hm.hafner.coverage.FileCoverageNode + getPath + ()Ljava/lang/String; + 81 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 7 + 2 + + edu.hm.hafner.metric.FileNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.metric.FileNodeTest]/[method:shouldGetPath()] + + replaced return value with "" for edu/hm/hafner/coverage/FileCoverageNode::getPath + + + + FileCoverageNode.java + edu.hm.hafner.coverage.FileCoverageNode + lambda$getCoveredBranchesCount$3 + (Ledu/hm/hafner/coverage/CoverageLeaf;)I + 75 + org.pitest.mutationtest.engine.gregor.mutators.returns.PrimitiveReturnsMutator + 6 + 2 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldSplitPackages()] + + replaced int return with 0 for + edu/hm/hafner/coverage/FileCoverageNode::lambda$getCoveredBranchesCount$3 + + + + FileCoverageNode.java + edu.hm.hafner.coverage.FileCoverageNode + lambda$getCoveredInstructionsCount$1 + (Ledu/hm/hafner/coverage/CoverageLeaf;)I + 53 + org.pitest.mutationtest.engine.gregor.mutators.returns.PrimitiveReturnsMutator + 6 + 2 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldSplitPackages()] + + replaced int return with 0 for + edu/hm/hafner/coverage/FileCoverageNode::lambda$getCoveredInstructionsCount$1 + + + + FileCoverageNode.java + edu.hm.hafner.coverage.FileCoverageNode + lambda$getMissedBranchesCount$2 + (Ledu/hm/hafner/coverage/CoverageLeaf;)I + 64 + org.pitest.mutationtest.engine.gregor.mutators.returns.PrimitiveReturnsMutator + 6 + 2 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldSplitPackages()] + + replaced int return with 0 for + edu/hm/hafner/coverage/FileCoverageNode::lambda$getMissedBranchesCount$2 + + + + FileCoverageNode.java + edu.hm.hafner.coverage.FileCoverageNode + lambda$getMissedInstructionsCount$0 + (Ledu/hm/hafner/coverage/CoverageLeaf;)I + 42 + org.pitest.mutationtest.engine.gregor.mutators.returns.PrimitiveReturnsMutator + 6 + 2 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldSplitPackages()] + + replaced int return with 0 for + edu/hm/hafner/coverage/FileCoverageNode::lambda$getMissedInstructionsCount$0 + + + + XmlParser.java + edu.hm.hafner.metric.parser.XmlParser + getRootNode + ()Ledu/hm/hafner/coverage/CoverageNode; + 24 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 4 + 0 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldThrowExceptionWhenObtainingAllBasicBlocks()] + + replaced return value with null for edu/hm/hafner/parser/XmlParser::getRootNode + + + XmlParser.java + edu.hm.hafner.metric.parser.XmlParser + parseFile + (Ljava/lang/String;)V + 42 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 20 + 5 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldNotSplitPackagesIfOnWrongHierarchyNode()] + + negated conditional + + + XmlParser.java + edu.hm.hafner.metric.parser.XmlParser + parseFile + (Ljava/lang/String;)V + 45 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 30 + 8 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldThrowExceptionWhenObtainingAllBasicBlocks()] + + negated conditional + + + XmlParser.java + edu.hm.hafner.metric.parser.XmlParser + parseFile + (Ljava/lang/String;)V + 49 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 41 + 11 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldThrowExceptionWhenObtainingAllBasicBlocks()] + + negated conditional + + + XmlParser.java + edu.hm.hafner.metric.parser.XmlParser + parseFile + (Ljava/lang/String;)V + 53 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 53 + 15 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldThrowExceptionWhenObtainingAllBasicBlocks()] + + negated conditional + + + XmlParser.java + edu.hm.hafner.metric.parser.XmlParser + parseFile + (Ljava/lang/String;)V + 46 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 35 + 9 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldSplitPackages()] + + removed call to edu/hm/hafner/parser/XmlParser::startDocument + + + XmlParser.java + edu.hm.hafner.metric.parser.XmlParser + parseFile + (Ljava/lang/String;)V + 50 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 47 + 13 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldThrowExceptionWhenObtainingAllBasicBlocks()] + + removed call to edu/hm/hafner/parser/XmlParser::startElement + + + XmlParser.java + edu.hm.hafner.metric.parser.XmlParser + parseFile + (Ljava/lang/String;)V + 54 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 59 + 17 + + edu.hm.hafner.parser.JacocoParserTest.[engine:junit-jupiter]/[class:edu.hm.hafner.parser.JacocoParserTest]/[method:shouldSplitPackages()] + + removed call to edu/hm/hafner/parser/XmlParser::endElement + + + XmlParser.java + edu.hm.hafner.metric.parser.XmlParser + parseFile + (Ljava/lang/String;)V + 59 + org.pitest.mutationtest.engine.gregor.mutators.VoidMethodCallMutator + 75 + 20 + + removed call to java/lang/Exception::printStackTrace + + + CoverageLeaf.java + edu.hm.hafner.coverage.CoverageLeaf + getCoverage + ()Ledu/hm/hafner/coverage/Coverage; + 38 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 5 + 0 + + edu.hm.hafner.coverage.CoverageLeafTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageLeafTest]/[method:shouldCreateLeaf()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageLeaf::getCoverage + + + CoverageLeaf.java + edu.hm.hafner.coverage.CoverageLeaf + getCoverage + (Ledu/hm/hafner/coverage/CoverageMetric;)Ledu/hm/hafner/coverage/Coverage; + 50 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 7 + 1 + + edu.hm.hafner.coverage.CoverageLeafTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageLeafTest]/[method:shouldCreateLeaf()] + + negated conditional + + + CoverageLeaf.java + edu.hm.hafner.coverage.CoverageLeaf + getCoverage + (Ledu/hm/hafner/coverage/CoverageMetric;)Ledu/hm/hafner/coverage/Coverage; + 51 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 12 + 2 + + edu.hm.hafner.coverage.CoverageLeafTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageLeafTest]/[method:shouldCreateLeaf()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageLeaf::getCoverage + + + CoverageLeaf.java + edu.hm.hafner.coverage.CoverageLeaf + getCoverage + (Ledu/hm/hafner/coverage/CoverageMetric;)Ledu/hm/hafner/coverage/Coverage; + 53 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 17 + 3 + + edu.hm.hafner.coverage.CoverageLeafTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageLeafTest]/[method:shouldCreateLeaf()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageLeaf::getCoverage + + + CoverageLeaf.java + edu.hm.hafner.coverage.CoverageLeaf + getMetric + ()Ledu/hm/hafner/coverage/CoverageMetric; + 34 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 5 + 0 + + edu.hm.hafner.coverage.CoverageLeafTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.CoverageLeafTest]/[method:shouldCreateLeaf()] + + replaced return value with null for edu/hm/hafner/coverage/CoverageLeaf::getMetric + + + MethodCoverageNode.java + edu.hm.hafner.coverage.MethodCoverageNode + getLineNumber + ()I + 44 + org.pitest.mutationtest.engine.gregor.mutators.returns.PrimitiveReturnsMutator + 5 + 0 + + edu.hm.hafner.coverage.MethodCoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.MethodCoverageNodeTest]/[method:shouldGetValidLineNumber()] + + replaced int return with 0 for edu/hm/hafner/coverage/MethodCoverageNode::getLineNumber + + + MethodCoverageNode.java + edu.hm.hafner.coverage.MethodCoverageNode + hasValidLineNumber + ()Z + 40 + org.pitest.mutationtest.engine.gregor.mutators.ConditionalsBoundaryMutator + 5 + 0 + + edu.hm.hafner.coverage.MethodCoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.MethodCoverageNodeTest]/[method:shouldCheckInvalidLineNumber()] + + changed conditional boundary + + + MethodCoverageNode.java + edu.hm.hafner.coverage.MethodCoverageNode + hasValidLineNumber + ()Z + 40 + org.pitest.mutationtest.engine.gregor.mutators.NegateConditionalsMutator + 5 + 0 + + edu.hm.hafner.coverage.MethodCoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.MethodCoverageNodeTest]/[method:shouldCheckInvalidLineNumber()] + + negated conditional + + + MethodCoverageNode.java + edu.hm.hafner.coverage.MethodCoverageNode + hasValidLineNumber + ()Z + 40 + org.pitest.mutationtest.engine.gregor.mutators.returns.BooleanTrueReturnValsMutator + 13 + 3 + + edu.hm.hafner.coverage.MethodCoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.MethodCoverageNodeTest]/[method:shouldCheckInvalidLineNumber()] + + replaced boolean return with true for edu/hm/hafner/coverage/MethodCoverageNode::hasValidLineNumber + + + + PackageCoverageNode.java + edu.hm.hafner.coverage.PackageCoverageNode + copyEmpty + ()Ledu/hm/hafner/coverage/CoverageNode; + 28 + org.pitest.mutationtest.engine.gregor.mutators.returns.NullReturnValsMutator + 8 + 2 + + edu.hm.hafner.coverage.PackageCoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.PackageCoverageNodeTest]/[method:shouldCopyEmpty()] + + replaced return value with null for edu/hm/hafner/coverage/PackageCoverageNode::copyEmpty + + + PackageCoverageNode.java + edu.hm.hafner.coverage.PackageCoverageNode + getPath + ()Ljava/lang/String; + 23 + org.pitest.mutationtest.engine.gregor.mutators.returns.EmptyObjectReturnValsMutator + 10 + 3 + + edu.hm.hafner.coverage.PackageCoverageNodeTest.[engine:junit-jupiter]/[class:edu.hm.hafner.coverage.PackageCoverageNodeTest]/[method:shouldMatchPath()] + + replaced return value with "" for edu/hm/hafner/coverage/PackageCoverageNode::getPath + + + From 7637f6ae55f178acd293f2e07850eeda29733a35 Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Mon, 17 Oct 2022 23:33:38 +0200 Subject: [PATCH 06/29] Convert some more pipelines and freestyle jobs. --- .../metrics/CoverageRecorderTest.java | 12 - .../coverage/model/CoveragePluginITest.java | 281 +++++++----------- 2 files changed, 107 insertions(+), 186 deletions(-) diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CoverageRecorderTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CoverageRecorderTest.java index c4d836a8f..dadef104d 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CoverageRecorderTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CoverageRecorderTest.java @@ -17,18 +17,6 @@ * @author Ullrich Hafner */ class CoverageRecorderTest extends IntegrationTestWithJenkinsPerSuite { - @Test - void shouldFailWithoutAdapter() { - WorkflowJob job = createPipeline(); - job.setDefinition(new CpsFlowDefinition( - "node {\n" - + " recordCoverage()\n" - + " }\n", true)); - - Run run = buildWithResult(job, Result.FAILURE); - - assertThat(getConsoleLog(run)).contains("[-ERROR-] No tools defined that will record the coverage files"); - } @Test void shouldIgnoreEmptyListOfFiles() { diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java index 3dce6e5ad..c61bc3142 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java @@ -83,66 +83,82 @@ class CoveragePluginITest extends IntegrationTestWithJenkinsPerSuite { * Another cobertura file for testing. */ private static final String COBERTURA_WITH_LOTS_OF_DATA_FILE = "cobertura-lots-of-data.xml"; - /** - * Symbol of cobertura adapter in pipeline. - */ - private static final String COBERTURA_ADAPTER = "istanbulCoberturaAdapter"; - /** - * Symbol of jacoco adapter in pipeline. - */ - private static final String JACOCO_ADAPTER = "jacocoAdapter"; // --------------------------------------------------------------------------------------- // vv Converted tests vv // --------------------------------------------------------------------------------------- @Test - void shouldRecordOneJacocoResultInFreestyleJob() { + void shouldFailWithoutParserInFreestyleJob() { FreeStyleProject project = createFreeStyleProject(); - copyFileToWorkspace(project, JACOCO_ANALYSIS_MODEL_FILE); - CoverageRecorder recorder = new CoverageRecorder(); - registerJaCoCo(recorder); - project.getPublishersList().add(recorder); + project.getPublishersList().add(new CoverageRecorder()); - // FIXME: which parser is correct? - /* - Expected :LINE: 95.52% (6083/6368) - Actual :LINE: 95.41% (5588/5857) - */ - verifyOneJacocoResult(project); + verifyNoParserError(project); } @Test - void shouldRecordTwoJacocoResultsInFreestyleJob() { + void shouldFailWithoutParserInPipeline() { + WorkflowJob job = createPipeline(); + + setPipelineScript(job, "recordCoverage()"); + + verifyNoParserError(job); + } + + private void verifyNoParserError(final ParameterizedJob project) { + Run run = buildWithResult(project, Result.FAILURE); + + assertThat(getConsoleLog(run)).contains("[-ERROR-] No tools defined that will record the coverage files"); + } + + @Test + void shouldReportErrorWhenNoFilesHaveBeenFoundInFreestyleJob() { FreeStyleProject project = createFreeStyleProject(); - copyFilesToWorkspace(project, JACOCO_ANALYSIS_MODEL_FILE, JACOCO_CODINGSTYLE_FILE); - CoverageRecorder recorder = new CoverageRecorder(); - registerJaCoCo(recorder); - project.getPublishersList().add(recorder); + createRecorder(project, CoverageParser.JACOCO); + + verifyNoFilesFound(project); + } + + @Test + void shouldReportErrorWhenNoFilesHaveBeenFoundInPipeline() { + WorkflowJob job = createPipeline(); + + createPipelineFor(job, CoverageParser.JACOCO); + + verifyNoFilesFound(job); + } + + private void verifyNoFilesFound(final ParameterizedJob project) { + Run run = buildWithResult(project, Result.SUCCESS); + + assertThat(getConsoleLog(run)).contains("[-ERROR-] No files found for pattern '**/*xml'. Configuration error?"); + } + + @Test + void shouldRecordOneJacocoResultInFreestyleJob() { + FreeStyleProject project = createFreeStyleProjectWithWorkspaceFiles(JACOCO_ANALYSIS_MODEL_FILE); + + createRecorder(project, CoverageParser.JACOCO); // FIXME: which parser is correct? /* - Expected :LINE: 95.31% (6377/6691) - Actual :LINE: 95.18% (5882/6180) + Expected :LINE: 95.52% (6083/6368) + Actual :LINE: 95.41% (5588/5857) */ - verifyTwoJacocoResults(project); + verifyOneJacocoResult(project); } - private void registerJaCoCo(final CoverageRecorder recorder) { - var tool = new CoverageTool(); - tool.setParser(CoverageParser.JACOCO); - tool.setPattern("**/jacoco*xml"); - recorder.setTools(List.of(tool)); + @Test + void shouldRecordOneJacocoResultInPipeline() { + WorkflowJob job = createPipelineWithWorkspaceFiles(JACOCO_ANALYSIS_MODEL_FILE); + + createPipelineFor(job, CoverageParser.JACOCO); + + verifyOneJacocoResult(job); } - /** - * Verifies project with one jacoco file. - * - * @param project - * the project with added files - */ private void verifyOneJacocoResult(final ParameterizedJob project) { Run build = buildSuccessfully(project); @@ -154,12 +170,29 @@ private void verifyOneJacocoResult(final ParameterizedJob project) { .build()); } - /** - * Verifies project with two jacoco files. - * - * @param project - * the project with added files - */ + @Test + void shouldRecordTwoJacocoResultsInFreestyleJob() { + FreeStyleProject project = createFreeStyleProjectWithWorkspaceFiles(JACOCO_ANALYSIS_MODEL_FILE, JACOCO_CODINGSTYLE_FILE); + + createRecorder(project, CoverageParser.JACOCO); + + // FIXME: which parser is correct? + /* + Expected :LINE: 95.31% (6377/6691) + Actual :LINE: 95.18% (5882/6180) + */ + verifyTwoJacocoResults(project); + } + + @Test + void shouldRecordTwoJacocoResultsInPipeline() { + WorkflowJob job = createPipelineWithWorkspaceFiles(JACOCO_ANALYSIS_MODEL_FILE, JACOCO_CODINGSTYLE_FILE); + + createPipelineFor(job, CoverageParser.JACOCO); + + verifyTwoJacocoResults(job); + } + private void verifyTwoJacocoResults(final ParameterizedJob project) { Run build = buildSuccessfully(project); @@ -171,36 +204,36 @@ private void verifyTwoJacocoResults(final ParameterizedJob project) { .build()); } - /** - * Freestyle integration test with one cobertura file. - */ - @Test - void shouldRecordOneCoberturaResultInFreestyleJob() { - FreeStyleProject project = createFreeStyleProject(); + private void setPipelineScript(final WorkflowJob job, final String recorderSnippet) { + job.setDefinition(new CpsFlowDefinition( + "node {\n" + + recorderSnippet + "\n" + + " }\n", true)); + } - copyFilesToWorkspace(project, COBERTURA_HIGHER_COVERAGE_FILE); + private void createPipelineFor(final WorkflowJob job, final CoverageParser parser) { + setPipelineScript(job, "recordCoverage tools: [[parser: '" + parser.name() + "', pattern: '**/*xml']]"); + } + private void createRecorder(final FreeStyleProject project, final CoverageParser parser) { CoverageRecorder recorder = new CoverageRecorder(); - registerCobertura(recorder); + var tool = new CoverageTool(); + tool.setParser(parser); + tool.setPattern("**/*xml"); + recorder.setTools(List.of(tool)); project.getPublishersList().add(recorder); + } + + @Test + void shouldRecordOneCoberturaResultInFreestyleJob() { + FreeStyleProject project = createFreeStyleProjectWithWorkspaceFiles(COBERTURA_HIGHER_COVERAGE_FILE); + + createRecorder(project, CoverageParser.COBERTURA); // FIXME: all parsers should only fail for mandatory properties (complexity is only optional) verifyOneCoberturaResult(project); } - private void registerCobertura(final CoverageRecorder recorder) { - var tool = new CoverageTool(); - tool.setParser(CoverageParser.COBERTURA); - tool.setPattern("**/cobertura*xml"); - recorder.setTools(List.of(tool)); - } - - /** - * Verifies project with one cobertura file. - * - * @param project - * the project with added files - */ private void verifyOneCoberturaResult(final ParameterizedJob project) { Run build = buildSuccessfully(project); @@ -209,32 +242,17 @@ private void verifyOneCoberturaResult(final ParameterizedJob project) { .isEqualTo(new CoverageBuilder().setMetric(Metric.LINE).setCovered(COBERTURA_COVERED_LINES) .setMissed(COBERTURA_ALL_LINES - COBERTURA_COVERED_LINES) .build()); - } - /** - * Freestyle integration test with one cobertura file. - */ @Test void shouldRecordOnePitResultInFreestyleJob() { - FreeStyleProject project = createFreeStyleProject(); - - copyFilesToWorkspace(project, "mutations.xml"); + FreeStyleProject project = createFreeStyleProjectWithWorkspaceFiles("mutations.xml"); - CoverageRecorder recorder = new CoverageRecorder(); - registerPit(recorder); - project.getPublishersList().add(recorder); + createRecorder(project, CoverageParser.PIT); verifyOnePitResult(project); } - private void registerPit(final CoverageRecorder recorder) { - var tool = new CoverageTool(); - tool.setParser(CoverageParser.PIT); - tool.setPattern("**/mutations*xml"); - recorder.setTools(List.of(tool)); - } - private void verifyOnePitResult(final ParameterizedJob project) { Run build = buildSuccessfully(project); @@ -251,90 +269,18 @@ private static CoverageBuilder createLineCoverageBuilder() { return new CoverageBuilder().setMetric(Metric.LINE); } - // --------------------------------------------------------------------------------------- // ^^ Converted tests ^^ // --------------------------------------------------------------------------------------- - /** - * Pipeline integration test with no adapter. - */ - @Disabled("Bug") - @Test - void pipelineForNoAdapter() { - WorkflowJob job = createPipeline(); - job.setDefinition(new CpsFlowDefinition("node {" - + " publishCoverage adapters: []" - + "}", true)); - - verifyForNoAdapter(job); - } - - /** - * Freestyle integration test with no adapter. - */ - @Disabled("Bug") - @Test - void freestyleForNoAdapter() { - FreeStyleProject project = createFreeStyleProject(); - CoveragePublisher coveragePublisher = new CoveragePublisher(); - project.getPublishersList().add(coveragePublisher); - verifyForNoAdapter(project); - } - - /** - * Pipeline integration test with no file. - */ - @Test - void pipelineForNoJacoco() { - WorkflowJob job = createPipeline(); - job.setDefinition(getCpsFlowDefinitionWithAdapter(JACOCO_ADAPTER)); - - verifyForNoFile(job); - } - - /** Example integration test for a pipeline with code coverage. */ - @Test - void pipelineForOneJacoco() { - WorkflowJob job = createPipelineWithWorkspaceFiles(JACOCO_ANALYSIS_MODEL_FILE); - job.setDefinition(getCpsFlowDefinitionWithAdapter(JACOCO_ADAPTER)); - - verifyOneJacocoResult(job); - } - - /** - * Pipeline integration test with two jacoco files. - */ - @Test - void pipelineForTwoJacoco() { - WorkflowJob job = createPipelineWithWorkspaceFiles(JACOCO_ANALYSIS_MODEL_FILE, JACOCO_CODINGSTYLE_FILE); - job.setDefinition(getCpsFlowDefinitionWithAdapter(JACOCO_ADAPTER)); - - verifyTwoJacocoResults(job); - } - - /** - * Freestyle integration test with no jacoco file. - */ - @Test - void freestyleForNoJacoco() { - FreeStyleProject project = createFreeStyleProject(); - - CoveragePublisher coveragePublisher = new CoveragePublisher(); - JacocoReportAdapter jacocoReportAdapter = new JacocoReportAdapter(""); - coveragePublisher.setAdapters(Collections.singletonList(jacocoReportAdapter)); - project.getPublishersList().add(coveragePublisher); - - verifyForNoFile(project); - } - /** * Pipeline integration test with no cobertura file. */ @Test void pipelineForNoCobertura() { WorkflowJob job = createPipeline(); - job.setDefinition(getCpsFlowDefinitionWithAdapter(COBERTURA_ADAPTER)); + + createPipelineFor(job, CoverageParser.COBERTURA); verifyForNoFile(job); } @@ -345,7 +291,8 @@ void pipelineForNoCobertura() { @Test void pipelineForOneCobertura() { WorkflowJob job = createPipelineWithWorkspaceFiles(COBERTURA_HIGHER_COVERAGE_FILE); - job.setDefinition(getCpsFlowDefinitionWithAdapter(COBERTURA_ADAPTER)); + + createPipelineFor(job, CoverageParser.COBERTURA); verifyOneCoberturaResult(job); } @@ -358,7 +305,7 @@ void pipelineForTwoCobertura() { WorkflowJob job = createPipelineWithWorkspaceFiles(COBERTURA_HIGHER_COVERAGE_FILE, COBERTURA_WITH_LOTS_OF_DATA_FILE); - job.setDefinition(getCpsFlowDefinitionWithAdapter(COBERTURA_ADAPTER)); + createPipelineFor(job, CoverageParser.COBERTURA); verifyForTwoCobertura(job); } @@ -407,9 +354,9 @@ void freestyleForTwoCobertura() { @Test void pipelineForOneCoberturaAndOneJacoco() { WorkflowJob job = createPipelineWithWorkspaceFiles(JACOCO_ANALYSIS_MODEL_FILE, COBERTURA_HIGHER_COVERAGE_FILE); - job.setDefinition(new CpsFlowDefinition("node {" + setPipelineScript(job, "node {" + " publishCoverage adapters: [jacocoAdapter('**/*.xml'), istanbulCoberturaAdapter('**/*.xml')]" - + "}", true)); + + "}"); verifyForOneCoberturaAndOneJacoco(job); } @@ -438,20 +385,6 @@ void freestyleForOneCoberturaAndOneJacoco() { verifyForOneCoberturaAndOneJacoco(project); } - /** - * Creates a script with adapter set to wildcard. - * - * @param adapter - * publish coverage adapter - * - * @return {@link CpsFlowDefinition} with set jacoco adapter - */ - private CpsFlowDefinition getCpsFlowDefinitionWithAdapter(final String adapter) { - return new CpsFlowDefinition("node {" - + " publishCoverage adapters: [" + adapter + "('**/*.xml')]" - + "}", true); - } - /** * Verifies project with no adapter. * From a41509143387c3593d514939cc9189b270a40654 Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Mon, 17 Oct 2022 23:43:05 +0200 Subject: [PATCH 07/29] Add a parameterized test for all parsers that have no input files. --- .../coverage/model/CoveragePluginITest.java | 69 ++++--------------- 1 file changed, 13 insertions(+), 56 deletions(-) diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java index c61bc3142..9d0cfc0c8 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java @@ -1,11 +1,13 @@ package io.jenkins.plugins.coverage.model; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import edu.hm.hafner.metric.Coverage; import edu.hm.hafner.metric.Coverage.CoverageBuilder; @@ -112,20 +114,24 @@ private void verifyNoParserError(final ParameterizedJob project) { assertThat(getConsoleLog(run)).contains("[-ERROR-] No tools defined that will record the coverage files"); } - @Test - void shouldReportErrorWhenNoFilesHaveBeenFoundInFreestyleJob() { + @EnumSource + @ParameterizedTest(name = "{index} => Freestyle job with parser {0}") + @DisplayName("Report error but do not fail build in freestyle job when no input files are found") + void shouldReportErrorWhenNoFilesHaveBeenFoundInFreestyleJob(final CoverageParser parser) { FreeStyleProject project = createFreeStyleProject(); - createRecorder(project, CoverageParser.JACOCO); + createRecorder(project, parser); verifyNoFilesFound(project); } - @Test - void shouldReportErrorWhenNoFilesHaveBeenFoundInPipeline() { + @EnumSource + @ParameterizedTest(name = "{index} => Pipeline with parser {0}") + @DisplayName("Report error but do not fail build in pipeline when no input files are found") + void shouldReportErrorWhenNoFilesHaveBeenFoundInPipeline(final CoverageParser parser) { WorkflowJob job = createPipeline(); - createPipelineFor(job, CoverageParser.JACOCO); + createPipelineFor(job, parser); verifyNoFilesFound(job); } @@ -273,18 +279,6 @@ private static CoverageBuilder createLineCoverageBuilder() { // ^^ Converted tests ^^ // --------------------------------------------------------------------------------------- - /** - * Pipeline integration test with no cobertura file. - */ - @Test - void pipelineForNoCobertura() { - WorkflowJob job = createPipeline(); - - createPipelineFor(job, CoverageParser.COBERTURA); - - verifyForNoFile(job); - } - /** * Pipeline integration test with one cobertura file. */ @@ -310,21 +304,6 @@ void pipelineForTwoCobertura() { verifyForTwoCobertura(job); } - /** - * Freestyle integration test with no cobertura file. - */ - @Test - void freestyleForNoCobertura() { - FreeStyleProject project = createFreeStyleProject(); - - CoveragePublisher coveragePublisher = new CoveragePublisher(); - CoberturaReportAdapter coberturaReportAdapter = new CoberturaReportAdapter(""); - coveragePublisher.setAdapters(Collections.singletonList(coberturaReportAdapter)); - project.getPublishersList().add(coveragePublisher); - - verifyForNoFile(project); - } - /** * Freestyle integration test with two cobertura files. */ @@ -385,28 +364,6 @@ void freestyleForOneCoberturaAndOneJacoco() { verifyForOneCoberturaAndOneJacoco(project); } - /** - * Verifies project with no adapter. - * - * @param project - * the project with no adapter - */ - private void verifyForNoAdapter(final ParameterizedJob project) { - buildWithResult(project, Result.FAILURE); - } - - /** - * Verifies project with no files. - * - * @param project - * the project with no files - */ - private void verifyForNoFile(final ParameterizedJob project) { - Run build = buildWithResult(project, Result.SUCCESS); - CoverageBuildAction action = build.getAction(CoverageBuildAction.class); - assertThat(action).isEqualTo(null); - } - /** * Verifies project with two cobertura files. * From 12268d040f21d4d1db7d3eb6157f985e409d4991 Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Mon, 17 Oct 2022 23:58:01 +0200 Subject: [PATCH 08/29] Simplify creation of jobs. --- .../coverage/model/CoveragePluginITest.java | 90 ++++++++----------- 1 file changed, 39 insertions(+), 51 deletions(-) diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java index 9d0cfc0c8..b39d6cae5 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java @@ -118,9 +118,7 @@ private void verifyNoParserError(final ParameterizedJob project) { @ParameterizedTest(name = "{index} => Freestyle job with parser {0}") @DisplayName("Report error but do not fail build in freestyle job when no input files are found") void shouldReportErrorWhenNoFilesHaveBeenFoundInFreestyleJob(final CoverageParser parser) { - FreeStyleProject project = createFreeStyleProject(); - - createRecorder(project, parser); + FreeStyleProject project = createFreestyleJob(parser); verifyNoFilesFound(project); } @@ -129,9 +127,7 @@ void shouldReportErrorWhenNoFilesHaveBeenFoundInFreestyleJob(final CoverageParse @ParameterizedTest(name = "{index} => Pipeline with parser {0}") @DisplayName("Report error but do not fail build in pipeline when no input files are found") void shouldReportErrorWhenNoFilesHaveBeenFoundInPipeline(final CoverageParser parser) { - WorkflowJob job = createPipeline(); - - createPipelineFor(job, parser); + WorkflowJob job = createPipeline(parser); verifyNoFilesFound(job); } @@ -144,9 +140,7 @@ private void verifyNoFilesFound(final ParameterizedJob project) { @Test void shouldRecordOneJacocoResultInFreestyleJob() { - FreeStyleProject project = createFreeStyleProjectWithWorkspaceFiles(JACOCO_ANALYSIS_MODEL_FILE); - - createRecorder(project, CoverageParser.JACOCO); + FreeStyleProject project = createFreestyleJob(CoverageParser.JACOCO, JACOCO_ANALYSIS_MODEL_FILE); // FIXME: which parser is correct? /* @@ -158,9 +152,7 @@ void shouldRecordOneJacocoResultInFreestyleJob() { @Test void shouldRecordOneJacocoResultInPipeline() { - WorkflowJob job = createPipelineWithWorkspaceFiles(JACOCO_ANALYSIS_MODEL_FILE); - - createPipelineFor(job, CoverageParser.JACOCO); + WorkflowJob job = createPipeline(CoverageParser.JACOCO, JACOCO_ANALYSIS_MODEL_FILE); verifyOneJacocoResult(job); } @@ -178,9 +170,8 @@ private void verifyOneJacocoResult(final ParameterizedJob project) { @Test void shouldRecordTwoJacocoResultsInFreestyleJob() { - FreeStyleProject project = createFreeStyleProjectWithWorkspaceFiles(JACOCO_ANALYSIS_MODEL_FILE, JACOCO_CODINGSTYLE_FILE); - - createRecorder(project, CoverageParser.JACOCO); + FreeStyleProject project = createFreestyleJob(CoverageParser.JACOCO, + JACOCO_ANALYSIS_MODEL_FILE, JACOCO_CODINGSTYLE_FILE); // FIXME: which parser is correct? /* @@ -192,9 +183,8 @@ void shouldRecordTwoJacocoResultsInFreestyleJob() { @Test void shouldRecordTwoJacocoResultsInPipeline() { - WorkflowJob job = createPipelineWithWorkspaceFiles(JACOCO_ANALYSIS_MODEL_FILE, JACOCO_CODINGSTYLE_FILE); - - createPipelineFor(job, CoverageParser.JACOCO); + WorkflowJob job = createPipeline(CoverageParser.JACOCO, + JACOCO_ANALYSIS_MODEL_FILE, JACOCO_CODINGSTYLE_FILE); verifyTwoJacocoResults(job); } @@ -210,36 +200,50 @@ private void verifyTwoJacocoResults(final ParameterizedJob project) { .build()); } - private void setPipelineScript(final WorkflowJob job, final String recorderSnippet) { - job.setDefinition(new CpsFlowDefinition( - "node {\n" - + recorderSnippet + "\n" - + " }\n", true)); - } - - private void createPipelineFor(final WorkflowJob job, final CoverageParser parser) { - setPipelineScript(job, "recordCoverage tools: [[parser: '" + parser.name() + "', pattern: '**/*xml']]"); - } + private FreeStyleProject createFreestyleJob(final CoverageParser parser, final String... fileNames) { + FreeStyleProject project = createFreeStyleProjectWithWorkspaceFiles(fileNames); - private void createRecorder(final FreeStyleProject project, final CoverageParser parser) { CoverageRecorder recorder = new CoverageRecorder(); var tool = new CoverageTool(); tool.setParser(parser); tool.setPattern("**/*xml"); recorder.setTools(List.of(tool)); project.getPublishersList().add(recorder); + + return project; + } + + private WorkflowJob createPipeline(final CoverageParser parser, final String... fileNames) { + WorkflowJob job = createPipelineWithWorkspaceFiles(fileNames); + + setPipelineScript(job, + "recordCoverage tools: [[parser: '" + parser.name() + "', pattern: '**/*xml']]"); + + return job; + } + + private void setPipelineScript(final WorkflowJob job, final String recorderSnippet) { + job.setDefinition(new CpsFlowDefinition( + "node {\n" + + recorderSnippet + "\n" + + " }\n", true)); } @Test void shouldRecordOneCoberturaResultInFreestyleJob() { - FreeStyleProject project = createFreeStyleProjectWithWorkspaceFiles(COBERTURA_HIGHER_COVERAGE_FILE); - - createRecorder(project, CoverageParser.COBERTURA); + FreeStyleProject project = createFreestyleJob(CoverageParser.COBERTURA, COBERTURA_HIGHER_COVERAGE_FILE); // FIXME: all parsers should only fail for mandatory properties (complexity is only optional) verifyOneCoberturaResult(project); } + @Test + void shouldRecordOneCoberturaResultInPipeline() { + WorkflowJob job = createPipeline(CoverageParser.COBERTURA, COBERTURA_HIGHER_COVERAGE_FILE); + + verifyOneCoberturaResult(job); + } + private void verifyOneCoberturaResult(final ParameterizedJob project) { Run build = buildSuccessfully(project); @@ -252,9 +256,7 @@ private void verifyOneCoberturaResult(final ParameterizedJob project) { @Test void shouldRecordOnePitResultInFreestyleJob() { - FreeStyleProject project = createFreeStyleProjectWithWorkspaceFiles("mutations.xml"); - - createRecorder(project, CoverageParser.PIT); + FreeStyleProject project = createFreestyleJob(CoverageParser.PIT, "mutations.xml"); verifyOnePitResult(project); } @@ -279,27 +281,13 @@ private static CoverageBuilder createLineCoverageBuilder() { // ^^ Converted tests ^^ // --------------------------------------------------------------------------------------- - /** - * Pipeline integration test with one cobertura file. - */ - @Test - void pipelineForOneCobertura() { - WorkflowJob job = createPipelineWithWorkspaceFiles(COBERTURA_HIGHER_COVERAGE_FILE); - - createPipelineFor(job, CoverageParser.COBERTURA); - - verifyOneCoberturaResult(job); - } - /** * Pipeline integration test with two cobertura files. */ @Test void pipelineForTwoCobertura() { - WorkflowJob job = createPipelineWithWorkspaceFiles(COBERTURA_HIGHER_COVERAGE_FILE, - COBERTURA_WITH_LOTS_OF_DATA_FILE); - - createPipelineFor(job, CoverageParser.COBERTURA); + WorkflowJob job = createPipeline(CoverageParser.COBERTURA, + COBERTURA_HIGHER_COVERAGE_FILE, COBERTURA_WITH_LOTS_OF_DATA_FILE); verifyForTwoCobertura(job); } From 6194b959b681fb8f689b8cac2cec4dfc80d71811 Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Thu, 20 Oct 2022 16:19:21 +0200 Subject: [PATCH 09/29] Introduce a label provider for metrics. --- .../coverage/model/CoverageViewModel.java | 121 ++++++++++++------ .../coverage/model/Messages.properties | 13 ++ .../coverage/model/CoverageViewModelTest.java | 58 ++++----- 3 files changed, 115 insertions(+), 77 deletions(-) diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageViewModel.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageViewModel.java index fe9d58486..c1a8ee6df 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageViewModel.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageViewModel.java @@ -2,10 +2,10 @@ import java.io.File; import java.io.IOException; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.NavigableMap; import java.util.NoSuchElementException; import java.util.Optional; import java.util.Set; @@ -28,7 +28,6 @@ import edu.hm.hafner.metric.FileNode; import edu.hm.hafner.metric.Metric; import edu.hm.hafner.metric.Node; -import edu.hm.hafner.metric.Value; import edu.umd.cs.findbugs.annotations.CheckForNull; import org.kohsuke.stapler.StaplerRequest; @@ -40,6 +39,7 @@ import io.jenkins.plugins.coverage.model.CoverageTableModel.InlineRowRenderer; import io.jenkins.plugins.coverage.model.CoverageTableModel.LinkedRowRenderer; +import io.jenkins.plugins.coverage.model.CoverageTableModel.RowRenderer; import io.jenkins.plugins.coverage.model.visualization.code.SourceCodeFacade; import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider; import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProviderFactory; @@ -90,12 +90,12 @@ public CoverageViewModel(final Run owner, final Node node) { this.owner = owner; this.node = node; - this.id = "coverage"; // TODO: this needs to be a parameter + id = "coverage"; // TODO: this needs to be a parameter // initialize filtered coverage trees so that they will not be calculated multiple times CoverageTreeCreator treeCreator = new CoverageTreeCreator(); - this.changeCoverageTreeRoot = treeCreator.createChangeCoverageTree(node); - this.indirectCoverageChangesTreeRoot = treeCreator.createIndirectCoverageChangesTree(node); + changeCoverageTreeRoot = treeCreator.createChangeCoverageTree(node); + indirectCoverageChangesTreeRoot = treeCreator.createIndirectCoverageChangesTree(node); } public String getId() { @@ -238,8 +238,8 @@ public TreeMapNode getCoverageChangesTree(final String coverageMetric) { } /** - * Gets the {@link Metric} from a String representation used in the frontend. Only 'Line' and 'Branch' is - * possible. 'Line' is used as a default. + * Gets the {@link Metric} from a String representation used in the frontend. Only 'Line' and 'Branch' is possible. + * 'Line' is used as a default. * * @param text * The coverage metric as String @@ -264,16 +264,9 @@ private Metric getCoverageMetricFromText(final String text) { */ @Override public TableModel getTableModel(final String tableId) { - CoverageTableModel.RowRenderer renderer; - String actualId; - if (tableId.endsWith(INLINE_SUFFIX) && hasSourceCode()) { - renderer = new InlineRowRenderer(); - } - else { - renderer = new LinkedRowRenderer(getOwner().getRootDir(), getId()); - } - actualId = tableId.replace(INLINE_SUFFIX, StringUtils.EMPTY); + RowRenderer renderer = createRenderer(tableId); + String actualId = tableId.replace(INLINE_SUFFIX, StringUtils.EMPTY); switch (actualId) { case ABSOLUTE_COVERAGE_TABLE_ID: return new CoverageTableModel(tableId, getNode(), renderer, colorProvider); @@ -288,6 +281,17 @@ public TableModel getTableModel(final String tableId) { } } + private RowRenderer createRenderer(final String tableId) { + RowRenderer renderer; + if (tableId.endsWith(INLINE_SUFFIX) && hasSourceCode()) { + renderer = new InlineRowRenderer(); + } + else { + renderer = new LinkedRowRenderer(getOwner().getRootDir(), getId()); + } + return renderer; + } + /** * Returns the URL for coverage results of the selected build. Based on the current URL, the new URL will be * composed by replacing the current build number with the selected build number. @@ -334,8 +338,8 @@ public String getSourceCode(final String fileHash, final String tableId) { } /** - * Reads the sourcecode corresponding to the passed {@link Node node} and filters the code dependent on the - * table ID. + * Reads the sourcecode corresponding to the passed {@link Node node} and filters the code dependent on the table + * ID. * * @param sourceNode * The node @@ -501,31 +505,46 @@ public SourceViewModel getDynamic(final String link, final StaplerRequest reques /** * UI model for the coverage overview bar chart. Shows the coverage results for the different coverage metrics. */ - // FIXME: In the new model we do not only have coverages public static class CoverageOverview { private final Node coverage; + private static final ValueLabelProvider LABEL_PROVIDER = new ValueLabelProvider(); CoverageOverview(final Node coverage) { this.coverage = coverage; } public List getMetrics() { - return getMetricsDistribution().keySet().stream() - .skip(1) // ignore the root of the tree as the coverage is always 1 of 1 - .map(Metric::name) // FIXME: we need to create localized names + return sortCoverages() + .map(Coverage::getMetric) + .map(LABEL_PROVIDER::getDisplayName) .collect(Collectors.toList()); } - public List getCovered() { - return streamCoverages().map(Coverage::getCovered).collect(Collectors.toList()); + private Stream sortCoverages() { + return coverage.getMetrics() + .stream() + .map(m -> m.getValueFor(coverage)) + .flatMap(Optional::stream) + .filter(value -> value instanceof Coverage) + .map(Coverage.class::cast) + .filter(c -> c.getTotal() > 1) // ignore elements that have a total of 1 + .sorted(Comparator.comparing(Coverage::getMetric)); } - public List getCoveredPercentages() { - return getPercentages(Coverage::getCoveredPercentage); + public List getCovered() { + return getCoverageCounter(Coverage::getCovered); } public List getMissed() { - return streamCoverages().map(Coverage::getMissed).collect(Collectors.toList()); + return getCoverageCounter(Coverage::getMissed); + } + + private List getCoverageCounter(final Function property) { + return sortCoverages().map(property).collect(Collectors.toList()); + } + + public List getCoveredPercentages() { + return getPercentages(Coverage::getCoveredPercentage); } public List getMissedPercentages() { @@ -533,23 +552,10 @@ public List getMissedPercentages() { } private List getPercentages(final Function displayType) { - return streamCoverages().map(displayType) + return sortCoverages().map(displayType) .map(Fraction::doubleValue) - .map(p -> p * 100.0) .collect(Collectors.toList()); } - - private Stream streamCoverages() { - return getMetricsDistribution().values() - .stream() - .skip(1) - .filter(v -> v instanceof Coverage) - .map(Coverage.class::cast); - } - - private NavigableMap getMetricsDistribution() { - return coverage.getMetricsDistribution(); - } } /** @@ -557,4 +563,37 @@ private NavigableMap getMetricsDistribution() { */ private static final class ColorMappingType extends TypeReference> { } + + public static final class ValueLabelProvider { + public String getDisplayName(final Metric metric) { + switch (metric) { + case CONTAINER: + return Messages.Metric_CONTAINER(); + case MODULE: + return Messages.Metric_MODULE(); + case PACKAGE: + return Messages.Metric_PACKAGE(); + case FILE: + return Messages.Metric_FILE(); + case CLASS: + return Messages.Metric_CLASS(); + case METHOD: + return Messages.Metric_METHOD(); + case LINE: + return Messages.Metric_LINE(); + case BRANCH: + return Messages.Metric_BRANCH(); + case INSTRUCTION: + return Messages.Metric_INSTRUCTION(); + case MUTATION: + return Messages.Metric_MUTATION(); + case COMPLEXITY: + return Messages.Metric_COMPLEXITY(); + case LOC: + return Messages.Metric_LOC(); + default: + throw new NoSuchElementException("No display name found for metric " + metric); + } + } + } } diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/Messages.properties b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/Messages.properties index d37256021..95bf8cb03 100644 --- a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/Messages.properties +++ b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/Messages.properties @@ -15,3 +15,16 @@ Column.DeltaLineCoverage=Line {0} Column.BranchCoverage=Branch Column.DeltaBranchCoverage=Branch {0} Column.LinesOfCode=LOC + +Metric.CONTAINER=Container +Metric.MODULE=Module +Metric.PACKAGE=Package +Metric.FILE=File +Metric.CLASS=Class +Metric.METHOD=Method +Metric.INSTRUCTION=Instruction +Metric.LINE=Line +Metric.MUTATION=Mutation +Metric.BRANCH=Branch +Metric.COMPLEXITY=Cyclomatic Complexity +Metric.LOC=Line of code diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageViewModelTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageViewModelTest.java index 28792f2f0..0da2fcb71 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageViewModelTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageViewModelTest.java @@ -1,19 +1,16 @@ package io.jenkins.plugins.coverage.model; -import java.util.NavigableMap; import java.util.NoSuchElementException; -import java.util.TreeMap; import org.apache.commons.lang3.math.Fraction; import org.junit.jupiter.api.Test; -import edu.hm.hafner.metric.Coverage; -import edu.hm.hafner.metric.Metric; import edu.hm.hafner.metric.Node; -import edu.hm.hafner.metric.Value; import hudson.model.Run; +import io.vavr.collection.List; + import static io.jenkins.plugins.coverage.model.Assertions.*; import static io.jenkins.plugins.coverage.model.CoverageViewModel.*; import static io.jenkins.plugins.coverage.model.testutil.CoverageStubs.*; @@ -28,7 +25,6 @@ */ @SuppressWarnings("PMD.TooManyStaticImports") class CoverageViewModelTest extends AbstractCoverageTest { - @Test void shouldReturnEmptySourceViewForExistingLinkButMissingSourceFile() { CoverageViewModel model = createModel(); @@ -43,24 +39,32 @@ void shouldReturnEmptySourceViewForExistingLinkButMissingSourceFile() { void shouldReportOverview() { CoverageViewModel model = createModel(); - assertThat(model.getDisplayName()).contains("Java coding style: jacoco-codingstyle.xml"); + assertThat(model.getDisplayName()).contains("'Java coding style'"); CoverageOverview overview = model.getOverview(); - assertThatJson(overview).node("metrics").isArray().containsExactly( - "Package", "File", "Class", "Method", "Line", "Instruction", "Branch" - ); - assertThatJson(overview).node("covered").isArray().containsExactly( - 1, 7, 15, 97, 294, 1260, 109 - ); - assertThatJson(overview).node("missed").isArray().containsExactly( - 0, 3, 3, 5, 29, 90, 7 - ); + + var expectedMetrics = new String[] {"File", "Class", "Method", "Line", "Instruction", "Branch"}; + assertThat(overview.getMetrics()).containsExactly(expectedMetrics); + + var expectedCovered = List.ofAll(7, 15, 97, 294, 1260, 109); + assertThat(overview.getCovered()).containsExactlyElementsOf(expectedCovered); + assertThat(overview.getCoveredPercentages()).allSatisfy(d -> assertThat(d).isStrictlyBetween(0.0, 1.0)); + + var expectedMissed = List.ofAll(3, 3, 5, 29, 90, 7); + assertThat(overview.getMissed()).containsExactlyElementsOf(expectedMissed); + assertThat(overview.getMissedPercentages()).allSatisfy(d -> assertThat(d).isStrictlyBetween(0.0, 1.0)); + + assertThatJson(overview).node("metrics").isArray().containsExactly(expectedMetrics); + assertThatJson(overview).node("covered").isArray().containsExactlyElementsOf(expectedCovered); + assertThatJson(overview).node("missed").isArray().containsExactlyElementsOf(expectedMissed); } @Test void shouldProvideIndirectCoverageChanges() { Node node = createIndirectCoverageChangesNode(Fraction.ZERO, LINE, 1, 1); - CoverageViewModel model = createModelFromMock(node); + + CoverageViewModel model = new CoverageViewModel(mock(Run.class), node); + assertThat(model.hasIndirectCoverageChanges()).isTrue(); } @@ -68,7 +72,7 @@ void shouldProvideIndirectCoverageChanges() { void shouldProvideRightTableModelById() { CoverageViewModel model = createModel(); assertThat(model.getTableModel(CHANGE_COVERAGE_TABLE_ID)).isInstanceOf(ChangeCoverageTableModel.class); - assertThat(model.getTableModel(INDIRECT_COVERAGE_TABLE_ID)).isInstanceOf(CoverageTableModel.class); + assertThat(model.getTableModel(INDIRECT_COVERAGE_TABLE_ID)).isInstanceOf(IndirectCoverageChangesTable.class); assertThat(model.getTableModel(ABSOLUTE_COVERAGE_TABLE_ID)).isInstanceOf(CoverageTableModel.class); assertThatExceptionOfType(NoSuchElementException.class) @@ -79,22 +83,4 @@ private CoverageViewModel createModel() { return new CoverageViewModel(mock(Run.class), readJacocoResult("jacoco-codingstyle.xml")); } - /** - * Creates a {@link CoverageViewModel} which represents a mocked {@link Node}. - * - * @param mock - * The mocked node - * - * @return the created model - */ - private CoverageViewModel createModelFromMock(final Node mock) { - NavigableMap changeMetricsDistribution = new TreeMap<>(); - changeMetricsDistribution.put(LINE, Coverage.nullObject(Metric.LINE)); - changeMetricsDistribution.put(BRANCH, Coverage.nullObject(Metric.BRANCH)); - changeMetricsDistribution.put(FILE, Coverage.nullObject(Metric.FILE)); - changeMetricsDistribution.put(PACKAGE, Coverage.nullObject(Metric.PACKAGE)); - when(mock.getMetricsDistribution()).thenReturn(changeMetricsDistribution); - - return new CoverageViewModel(mock(Run.class), mock); - } } From c4123fcc0e0d483c92da568f8be936e9788c798b Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Thu, 20 Oct 2022 23:22:34 +0200 Subject: [PATCH 10/29] Fix serialization of delta values (using Fraction instances). --- .../coverage/metrics/CoverageRecorder.java | 2 +- .../coverage/model/CoverageBuildAction.java | 7 ++ .../coverage/model/CoverageXmlStream.java | 32 +++++++++ .../META-INF/hudson.remoting.ClassFilter | 2 + .../coverage/model/AbstractCoverageITest.java | 48 ++++++++++++++ .../coverage/model/CoveragePluginITest.java | 34 +--------- ...DeltaComputationVsReferenceBuildITest.java | 65 +++++++++---------- 7 files changed, 122 insertions(+), 68 deletions(-) create mode 100644 plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageITest.java diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageRecorder.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageRecorder.java index f31078fe6..365677917 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageRecorder.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageRecorder.java @@ -69,7 +69,7 @@ public class CoverageRecorder extends Recorder implements SimpleBuildStep { private boolean enabledForFailure; private int healthy; private int unhealthy; - private String scm; + private String scm = StringUtils.EMPTY; /** * Creates a new instance of {@link CoverageRecorder}. diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageBuildAction.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageBuildAction.java index 7ace659bc..7a116b6dd 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageBuildAction.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageBuildAction.java @@ -25,11 +25,14 @@ import hudson.model.HealthReportingAction; import hudson.model.Run; +import io.jenkins.plugins.coverage.model.CoverageXmlStream.FractionConverter; import io.jenkins.plugins.forensics.reference.ReferenceBuild; import io.jenkins.plugins.util.AbstractXmlStream; import io.jenkins.plugins.util.BuildAction; import io.jenkins.plugins.util.JenkinsFacade; +import static hudson.model.Run.*; + /** * Controls the life cycle of the coverage results in a job. This action persists the results of a build and displays a * summary on the build page. The actual visualization of the results is defined in the matching {@code summary.jelly} @@ -69,6 +72,10 @@ public class CoverageBuildAction extends BuildAction implements HealthRepo /** The indirect coverage changes of the associated change request with respect to the reference build. */ private final NavigableMap indirectCoverageChanges; + static { + XSTREAM.registerConverter(new FractionConverter()); + } + /** * Creates a new instance of {@link CoverageBuildAction}. * diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageXmlStream.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageXmlStream.java index 826ba2389..06713dd72 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageXmlStream.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageXmlStream.java @@ -1,5 +1,13 @@ package io.jenkins.plugins.coverage.model; +import org.apache.commons.lang3.math.Fraction; + +import com.thoughtworks.xstream.converters.Converter; +import com.thoughtworks.xstream.converters.MarshallingContext; +import com.thoughtworks.xstream.converters.UnmarshallingContext; +import com.thoughtworks.xstream.io.HierarchicalStreamReader; +import com.thoughtworks.xstream.io.HierarchicalStreamWriter; + import edu.hm.hafner.metric.ClassNode; import edu.hm.hafner.metric.ContainerNode; import edu.hm.hafner.metric.Coverage; @@ -45,6 +53,8 @@ protected void configureXStream(final XStream2 xStream) { xStream.addImmutableType(Coverage.class, false); xStream.addImmutableType(LinesOfCode.class, false); xStream.addImmutableType(CyclomaticComplexity.class, false); + xStream.registerConverter(new FractionConverter()); + /* FIXME: restore converters xStream.addImmutableType(CoveragePercentageConverter.class, false); xStream.registerConverter(new CoverageMetricConverter()); @@ -61,4 +71,26 @@ protected void configureXStream(final XStream2 xStream) { protected Node createDefaultValue() { return new ModuleNode("Empty"); } + /** + * {@link Converter} for {@link Fraction} instances so that only the values will be serialized. After reading the + * values back from the stream, the string representation will be converted to an actual instance again. + */ + static final class FractionConverter implements Converter { + @SuppressWarnings("PMD.NullAssignment") + @Override + public void marshal(final Object source, final HierarchicalStreamWriter writer, + final MarshallingContext context) { + writer.setValue(source instanceof Fraction ? ((Fraction) source).toProperString() : null); + } + + @Override + public Object unmarshal(final HierarchicalStreamReader reader, final UnmarshallingContext context) { + return Fraction.getFraction(reader.getValue()); + } + + @Override + public boolean canConvert(final Class type) { + return type == Fraction.class; + } + } } diff --git a/plugin/src/main/resources/META-INF/hudson.remoting.ClassFilter b/plugin/src/main/resources/META-INF/hudson.remoting.ClassFilter index bf3363b1d..035d4d0ce 100644 --- a/plugin/src/main/resources/META-INF/hudson.remoting.ClassFilter +++ b/plugin/src/main/resources/META-INF/hudson.remoting.ClassFilter @@ -2,3 +2,5 @@ gnu.trove.impl.hash.THash gnu.trove.impl.hash.TIntHash gnu.trove.impl.hash.TPrimitiveHash gnu.trove.map.hash.TIntObjectHashMap + +org.apache.commons.lang3.math.Fraction diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageITest.java new file mode 100644 index 000000000..06fc8ebae --- /dev/null +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageITest.java @@ -0,0 +1,48 @@ +package io.jenkins.plugins.coverage.model; + +import java.util.List; + +import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; +import org.jenkinsci.plugins.workflow.job.WorkflowJob; +import hudson.model.FreeStyleProject; + +import io.jenkins.plugins.coverage.metrics.CoverageRecorder; +import io.jenkins.plugins.coverage.metrics.CoverageTool; +import io.jenkins.plugins.coverage.metrics.CoverageTool.CoverageParser; +import io.jenkins.plugins.util.IntegrationTestWithJenkinsPerSuite; + +/** + * Provides some helper methods to create different job types that will record code coverage results. + * + * @author Ullrich Hafner + */ +public abstract class AbstractCoverageITest extends IntegrationTestWithJenkinsPerSuite { + protected FreeStyleProject createFreestyleJob(final CoverageParser parser, final String... fileNames) { + FreeStyleProject project = createFreeStyleProjectWithWorkspaceFiles(fileNames); + + CoverageRecorder recorder = new CoverageRecorder(); + var tool = new CoverageTool(); + tool.setParser(parser); + tool.setPattern("**/*xml"); + recorder.setTools(List.of(tool)); + project.getPublishersList().add(recorder); + + return project; + } + + protected WorkflowJob createPipeline(final CoverageParser parser, final String... fileNames) { + WorkflowJob job = createPipelineWithWorkspaceFiles(fileNames); + + setPipelineScript(job, + "recordCoverage tools: [[parser: '" + parser.name() + "', pattern: '**/*xml']]"); + + return job; + } + + protected void setPipelineScript(final WorkflowJob job, final String recorderSnippet) { + job.setDefinition(new CpsFlowDefinition( + "node {\n" + + recorderSnippet + "\n" + + " }\n", true)); + } +} diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java index b39d6cae5..f3b1ad905 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java @@ -14,7 +14,6 @@ import edu.hm.hafner.metric.Metric; import edu.hm.hafner.metric.MutationValue; -import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; import org.jenkinsci.plugins.workflow.job.WorkflowJob; import hudson.model.FreeStyleProject; import hudson.model.Result; @@ -26,16 +25,14 @@ import io.jenkins.plugins.coverage.adapter.CoverageAdapter; import io.jenkins.plugins.coverage.adapter.JacocoReportAdapter; import io.jenkins.plugins.coverage.metrics.CoverageRecorder; -import io.jenkins.plugins.coverage.metrics.CoverageTool; import io.jenkins.plugins.coverage.metrics.CoverageTool.CoverageParser; -import io.jenkins.plugins.util.IntegrationTestWithJenkinsPerSuite; import static org.assertj.core.api.Assertions.*; /** * Integration test for different jacoco and cobertura files. */ -class CoveragePluginITest extends IntegrationTestWithJenkinsPerSuite { +class CoveragePluginITest extends AbstractCoverageITest { /** * Covered lines in {@value JACOCO_ANALYSIS_MODEL_FILE}. @@ -200,35 +197,6 @@ private void verifyTwoJacocoResults(final ParameterizedJob project) { .build()); } - private FreeStyleProject createFreestyleJob(final CoverageParser parser, final String... fileNames) { - FreeStyleProject project = createFreeStyleProjectWithWorkspaceFiles(fileNames); - - CoverageRecorder recorder = new CoverageRecorder(); - var tool = new CoverageTool(); - tool.setParser(parser); - tool.setPattern("**/*xml"); - recorder.setTools(List.of(tool)); - project.getPublishersList().add(recorder); - - return project; - } - - private WorkflowJob createPipeline(final CoverageParser parser, final String... fileNames) { - WorkflowJob job = createPipelineWithWorkspaceFiles(fileNames); - - setPipelineScript(job, - "recordCoverage tools: [[parser: '" + parser.name() + "', pattern: '**/*xml']]"); - - return job; - } - - private void setPipelineScript(final WorkflowJob job, final String recorderSnippet) { - job.setDefinition(new CpsFlowDefinition( - "node {\n" - + recorderSnippet + "\n" - + " }\n", true)); - } - @Test void shouldRecordOneCoberturaResultInFreestyleJob() { FreeStyleProject project = createFreestyleJob(CoverageParser.COBERTURA, COBERTURA_HIGHER_COVERAGE_FILE); diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeltaComputationVsReferenceBuildITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeltaComputationVsReferenceBuildITest.java index a94926577..31de44de1 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeltaComputationVsReferenceBuildITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeltaComputationVsReferenceBuildITest.java @@ -1,30 +1,26 @@ package io.jenkins.plugins.coverage.model; -import java.util.AbstractMap.SimpleEntry; -import java.util.Collections; - -import org.apache.commons.lang3.math.Fraction; import org.junit.jupiter.api.Test; +import edu.hm.hafner.metric.Coverage; import edu.hm.hafner.metric.FileNode; import edu.hm.hafner.metric.Node; -import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; import org.jenkinsci.plugins.workflow.job.WorkflowJob; import hudson.model.FreeStyleProject; import hudson.model.Run; -import io.jenkins.plugins.coverage.CoveragePublisher; -import io.jenkins.plugins.coverage.adapter.JacocoReportAdapter; -import io.jenkins.plugins.util.IntegrationTestWithJenkinsPerSuite; +import io.jenkins.plugins.coverage.metrics.CoverageRecorder; +import io.jenkins.plugins.coverage.metrics.CoverageTool.CoverageParser; import static edu.hm.hafner.metric.Metric.*; import static io.jenkins.plugins.coverage.model.Assertions.*; +import static org.assertj.core.data.Percentage.*; /** * Integration test for delta computation of reference builds. */ -class DeltaComputationVsReferenceBuildITest extends IntegrationTestWithJenkinsPerSuite { +class DeltaComputationVsReferenceBuildITest extends AbstractCoverageITest { private static final String JACOCO_ANALYSIS_MODEL_FILE = "jacoco-analysis-model.xml"; private static final String JACOCO_CODINGSTYLE_FILE = "jacoco-codingstyle.xml"; @@ -33,21 +29,17 @@ class DeltaComputationVsReferenceBuildITest extends IntegrationTestWithJenkinsPe */ @Test void freestyleProjectTryCreatingReferenceBuildWithDeltaComputation() { - FreeStyleProject project = createFreeStyleProjectWithWorkspaceFiles(JACOCO_ANALYSIS_MODEL_FILE, - JACOCO_CODINGSTYLE_FILE); - - CoveragePublisher coveragePublisher = new CoveragePublisher(); - coveragePublisher.setAdapters(Collections.singletonList(new JacocoReportAdapter(JACOCO_ANALYSIS_MODEL_FILE))); - project.getPublishersList().add(coveragePublisher); + FreeStyleProject project = createFreestyleJob(CoverageParser.JACOCO, + JACOCO_ANALYSIS_MODEL_FILE, JACOCO_CODINGSTYLE_FILE); - //run first build Run firstBuild = buildSuccessfully(project); + verifyFirstBuild(firstBuild); - // update adapter - coveragePublisher.setAdapters(Collections.singletonList(new JacocoReportAdapter(JACOCO_CODINGSTYLE_FILE))); + // update parser pattern to pick only the coding style results + project.getPublishersList().get(CoverageRecorder.class).getTools().get(0).setPattern(JACOCO_CODINGSTYLE_FILE); - //run second build Run secondBuild = buildSuccessfully(project); + verifySecondBuild(secondBuild); verifyDeltaComputation(firstBuild, secondBuild); } @@ -57,24 +49,33 @@ void freestyleProjectTryCreatingReferenceBuildWithDeltaComputation() { */ @Test void pipelineCreatingReferenceBuildWithDeltaComputation() { - WorkflowJob job = createPipelineWithWorkspaceFiles(JACOCO_ANALYSIS_MODEL_FILE, JACOCO_CODINGSTYLE_FILE); - job.setDefinition(new CpsFlowDefinition("node {" - + " publishCoverage adapters: [jacocoAdapter('" + JACOCO_ANALYSIS_MODEL_FILE - + "')]" - + "}", true)); + WorkflowJob job = createPipeline(CoverageParser.JACOCO, JACOCO_ANALYSIS_MODEL_FILE, JACOCO_CODINGSTYLE_FILE); Run firstBuild = buildSuccessfully(job); + verifyFirstBuild(firstBuild); - job.setDefinition(new CpsFlowDefinition("node {" - + "publishCoverage adapters: [jacocoAdapter('" + JACOCO_CODINGSTYLE_FILE + "')]\n" - + "discoverReferenceBuild(referenceJob: '" + job.getName() + "')" - + "}", true)); + // update parser pattern to pick only the codingstyle results + setPipelineScript(job, + "recordCoverage tools: [[parser: 'JACOCO', pattern: '" + JACOCO_CODINGSTYLE_FILE + "']]"); Run secondBuild = buildSuccessfully(job); + verifySecondBuild(secondBuild); verifyDeltaComputation(firstBuild, secondBuild); } + private static void verifySecondBuild(final Run secondBuild) { + var secondBuildCoverage = secondBuild.getAction(CoverageBuildAction.class).getLineCoverage(); + assertThat(secondBuildCoverage).extracting(Coverage::getCovered).isEqualTo(294); + assertThat(secondBuildCoverage.getCoveredPercentage().doubleValue()).isCloseTo(0.91, withPercentage(1.0)); // 294 + 5531 ? + } + + private static void verifyFirstBuild(final Run firstBuild) { + var firstBuildLineCoverage = firstBuild.getAction(CoverageBuildAction.class).getLineCoverage(); + assertThat(firstBuildLineCoverage.getCovered()).isEqualTo(5882); // 294 + 5531 ? + assertThat(firstBuildLineCoverage.getCoveredPercentage().doubleValue()).isCloseTo(0.95, withPercentage(1.0)); // 294 + 5531 ? + } + /** * Verifies the coverageComputation of the first and second build of the job. * @@ -93,12 +94,8 @@ private void verifyDeltaComputation(final Run firstBuild, final Run .isPresent() .satisfies(reference -> assertThat(reference.get()).isEqualTo(firstBuild)); - assertThat(coverageBuildAction.getDelta()).contains( - new SimpleEntry<>(LINE, Fraction.getFraction(-2_315_425, 514_216)), - new SimpleEntry<>(BRANCH, Fraction.getFraction(11_699, 2175)), - new SimpleEntry<>(INSTRUCTION, Fraction.getFraction(-235_580, 81_957)), - new SimpleEntry<>(METHOD, Fraction.getFraction(-217_450, 94_299)) - ); + assertThat(coverageBuildAction.getDelta().get(LINE).doubleValue()).isCloseTo(-0.0415, withPercentage(1.0)); + // TODO: compute delta for other metrics verifyChangeCoverage(coverageBuildAction); } From 61a05fb947f3361af9ef4c43e16b8a65cab48892 Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Fri, 21 Oct 2022 09:31:59 +0200 Subject: [PATCH 11/29] Verify branch coverage in delta test. --- .../coverage/model/AbstractCoverageITest.java | 3 + ...DeltaComputationVsReferenceBuildITest.java | 53 +- .../coverage/model/jacoco-analysis-model.xml | 21646 +++++++++++++++- 3 files changed, 21677 insertions(+), 25 deletions(-) diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageITest.java index 06fc8ebae..b38b19db4 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageITest.java @@ -17,6 +17,9 @@ * @author Ullrich Hafner */ public abstract class AbstractCoverageITest extends IntegrationTestWithJenkinsPerSuite { + static final String JACOCO_ANALYSIS_MODEL_FILE = "jacoco-analysis-model.xml"; + static final String JACOCO_CODING_STYLE_FILE = "jacoco-codingstyle.xml"; + protected FreeStyleProject createFreestyleJob(final CoverageParser parser, final String... fileNames) { FreeStyleProject project = createFreeStyleProjectWithWorkspaceFiles(fileNames); diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeltaComputationVsReferenceBuildITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeltaComputationVsReferenceBuildITest.java index 31de44de1..91bf07da4 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeltaComputationVsReferenceBuildITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeltaComputationVsReferenceBuildITest.java @@ -21,22 +21,16 @@ * Integration test for delta computation of reference builds. */ class DeltaComputationVsReferenceBuildITest extends AbstractCoverageITest { - private static final String JACOCO_ANALYSIS_MODEL_FILE = "jacoco-analysis-model.xml"; - private static final String JACOCO_CODINGSTYLE_FILE = "jacoco-codingstyle.xml"; - - /** - * Checks if the delta coverage can be computed regarding a reference build within a freestyle project. - */ @Test - void freestyleProjectTryCreatingReferenceBuildWithDeltaComputation() { + void shouldComputeDeltaInFreestyleJob() { FreeStyleProject project = createFreestyleJob(CoverageParser.JACOCO, - JACOCO_ANALYSIS_MODEL_FILE, JACOCO_CODINGSTYLE_FILE); + JACOCO_ANALYSIS_MODEL_FILE, JACOCO_CODING_STYLE_FILE); Run firstBuild = buildSuccessfully(project); verifyFirstBuild(firstBuild); // update parser pattern to pick only the coding style results - project.getPublishersList().get(CoverageRecorder.class).getTools().get(0).setPattern(JACOCO_CODINGSTYLE_FILE); + project.getPublishersList().get(CoverageRecorder.class).getTools().get(0).setPattern(JACOCO_CODING_STYLE_FILE); Run secondBuild = buildSuccessfully(project); verifySecondBuild(secondBuild); @@ -44,19 +38,16 @@ void freestyleProjectTryCreatingReferenceBuildWithDeltaComputation() { verifyDeltaComputation(firstBuild, secondBuild); } - /** - * Checks if the delta coverage can be computed regarding a reference build within a pipeline project. - */ @Test - void pipelineCreatingReferenceBuildWithDeltaComputation() { - WorkflowJob job = createPipeline(CoverageParser.JACOCO, JACOCO_ANALYSIS_MODEL_FILE, JACOCO_CODINGSTYLE_FILE); + void shouldComputeDeltaInPipeline() { + WorkflowJob job = createPipeline(CoverageParser.JACOCO, JACOCO_ANALYSIS_MODEL_FILE, JACOCO_CODING_STYLE_FILE); Run firstBuild = buildSuccessfully(job); verifyFirstBuild(firstBuild); // update parser pattern to pick only the codingstyle results setPipelineScript(job, - "recordCoverage tools: [[parser: 'JACOCO', pattern: '" + JACOCO_CODINGSTYLE_FILE + "']]"); + "recordCoverage tools: [[parser: 'JACOCO', pattern: '" + JACOCO_CODING_STYLE_FILE + "']]"); Run secondBuild = buildSuccessfully(job); verifySecondBuild(secondBuild); @@ -64,16 +55,28 @@ void pipelineCreatingReferenceBuildWithDeltaComputation() { verifyDeltaComputation(firstBuild, secondBuild); } - private static void verifySecondBuild(final Run secondBuild) { - var secondBuildCoverage = secondBuild.getAction(CoverageBuildAction.class).getLineCoverage(); - assertThat(secondBuildCoverage).extracting(Coverage::getCovered).isEqualTo(294); - assertThat(secondBuildCoverage.getCoveredPercentage().doubleValue()).isCloseTo(0.91, withPercentage(1.0)); // 294 + 5531 ? + private static void verifyFirstBuild(final Run firstBuild) { + var action = firstBuild.getAction(CoverageBuildAction.class); + + var lineCoverage = action.getLineCoverage(); + assertThat(lineCoverage.getCovered()).isEqualTo(5882); // 294 + 5531 ? + assertThat(lineCoverage.getCoveredPercentage().doubleValue()).isCloseTo(0.95, withPercentage(1.0)); + + var branchCoverage = action.getBranchCoverage(); + assertThat(branchCoverage.getCovered()).isEqualTo(1544 + 109); + assertThat(branchCoverage.getCoveredPercentage().doubleValue()).isCloseTo(0.88, withPercentage(1.0)); } - private static void verifyFirstBuild(final Run firstBuild) { - var firstBuildLineCoverage = firstBuild.getAction(CoverageBuildAction.class).getLineCoverage(); - assertThat(firstBuildLineCoverage.getCovered()).isEqualTo(5882); // 294 + 5531 ? - assertThat(firstBuildLineCoverage.getCoveredPercentage().doubleValue()).isCloseTo(0.95, withPercentage(1.0)); // 294 + 5531 ? + private static void verifySecondBuild(final Run secondBuild) { + var action = secondBuild.getAction(CoverageBuildAction.class); + + var lineCoverage = action.getLineCoverage(); + assertThat(lineCoverage).extracting(Coverage::getCovered).isEqualTo(294); + assertThat(lineCoverage.getCoveredPercentage().doubleValue()).isCloseTo(0.91, withPercentage(1.0)); + + var branchCoverage = action.getBranchCoverage(); + assertThat(branchCoverage.getCovered()).isEqualTo(109); + assertThat(branchCoverage.getCoveredPercentage().doubleValue()).isCloseTo(0.94, withPercentage(1.0)); } /** @@ -94,7 +97,9 @@ private void verifyDeltaComputation(final Run firstBuild, final Run .isPresent() .satisfies(reference -> assertThat(reference.get()).isEqualTo(firstBuild)); - assertThat(coverageBuildAction.getDelta().get(LINE).doubleValue()).isCloseTo(-0.0415, withPercentage(1.0)); + var delta = coverageBuildAction.getDelta(); + assertThat(delta.get(LINE).doubleValue()).isCloseTo(-0.0415, withPercentage(1.0)); + assertThat(delta.get(BRANCH).doubleValue()).isCloseTo(0.0533, withPercentage(1.0)); // TODO: compute delta for other metrics verifyChangeCoverage(coverageBuildAction); diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/model/jacoco-analysis-model.xml b/plugin/src/test/resources/io/jenkins/plugins/coverage/model/jacoco-analysis-model.xml index 8eafbda5b..52c1b39a6 100644 --- a/plugin/src/test/resources/io/jenkins/plugins/coverage/model/jacoco-analysis-model.xml +++ b/plugin/src/test/resources/io/jenkins/plugins/coverage/model/jacoco-analysis-model.xml @@ -1 +1,21645 @@ - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From cc4e3e7b02db4c00e6c215b583f96b2f6aee3946 Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Fri, 21 Oct 2022 14:53:35 +0200 Subject: [PATCH 12/29] Verify LOC and COMPLEXITY in delta test. --- .../DeltaComputationVsReferenceBuildITest.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeltaComputationVsReferenceBuildITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeltaComputationVsReferenceBuildITest.java index 91bf07da4..e1bdcfb56 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeltaComputationVsReferenceBuildITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeltaComputationVsReferenceBuildITest.java @@ -3,7 +3,9 @@ import org.junit.jupiter.api.Test; import edu.hm.hafner.metric.Coverage; +import edu.hm.hafner.metric.CyclomaticComplexity; import edu.hm.hafner.metric.FileNode; +import edu.hm.hafner.metric.LinesOfCode; import edu.hm.hafner.metric.Node; import org.jenkinsci.plugins.workflow.job.WorkflowJob; @@ -65,6 +67,9 @@ private static void verifyFirstBuild(final Run firstBuild) { var branchCoverage = action.getBranchCoverage(); assertThat(branchCoverage.getCovered()).isEqualTo(1544 + 109); assertThat(branchCoverage.getCoveredPercentage().doubleValue()).isCloseTo(0.88, withPercentage(1.0)); + + assertThat(action.getCoverage(LOC)).isEqualTo(new LinesOfCode(lineCoverage.getTotal())); + assertThat(action.getCoverage(COMPLEXITY)).isEqualTo(new CyclomaticComplexity(2718)); } private static void verifySecondBuild(final Run secondBuild) { @@ -77,6 +82,9 @@ private static void verifySecondBuild(final Run secondBuild) { var branchCoverage = action.getBranchCoverage(); assertThat(branchCoverage.getCovered()).isEqualTo(109); assertThat(branchCoverage.getCoveredPercentage().doubleValue()).isCloseTo(0.94, withPercentage(1.0)); + + assertThat(action.getCoverage(LOC)).isEqualTo(new LinesOfCode(lineCoverage.getTotal())); + assertThat(action.getCoverage(COMPLEXITY)).isEqualTo(new CyclomaticComplexity(160)); } /** @@ -98,9 +106,12 @@ private void verifyDeltaComputation(final Run firstBuild, final Run .satisfies(reference -> assertThat(reference.get()).isEqualTo(firstBuild)); var delta = coverageBuildAction.getDelta(); + assertThat(delta.get(MODULE).doubleValue()).isCloseTo(0, withPercentage(1.0)); + assertThat(delta.get(PACKAGE).doubleValue()).isCloseTo(0, withPercentage(1.0)); assertThat(delta.get(LINE).doubleValue()).isCloseTo(-0.0415, withPercentage(1.0)); assertThat(delta.get(BRANCH).doubleValue()).isCloseTo(0.0533, withPercentage(1.0)); - // TODO: compute delta for other metrics + assertThat(delta.get(LOC).intValue()).isEqualTo(-5857); + assertThat(delta.get(COMPLEXITY).intValue()).isEqualTo(160 - 2718); verifyChangeCoverage(coverageBuildAction); } From 3ef4f232409503bb6cd9e8b3559487b111f78f9b Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Fri, 21 Oct 2022 15:05:37 +0200 Subject: [PATCH 13/29] Move and fix test for declarative pipelines. --- .../coverage/model/AbstractCoverageITest.java | 17 +++++++ .../coverage/model/CoveragePluginITest.java | 32 ++++++++----- .../DeclarativePipelineSupportITest.java | 48 ------------------- 3 files changed, 37 insertions(+), 60 deletions(-) delete mode 100644 plugin/src/test/java/io/jenkins/plugins/coverage/model/DeclarativePipelineSupportITest.java diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageITest.java index b38b19db4..b39015b8e 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageITest.java @@ -48,4 +48,21 @@ protected void setPipelineScript(final WorkflowJob job, final String recorderSni + recorderSnippet + "\n" + " }\n", true)); } + + protected WorkflowJob createDeclarativePipeline(final CoverageParser parser, final String... fileNames) { + WorkflowJob job = createPipelineWithWorkspaceFiles(fileNames); + + job.setDefinition(new CpsFlowDefinition("pipeline {\n" + + " agent any\n" + + " stages {\n" + + " stage('Test') {\n" + + " steps {\n" + + " recordCoverage(\n" + + " tools: [[parser: '" + parser.name() + "', pattern: '**/*xml']]" + + " )}\n" + + " }\n" + + " }\n" + + "}", true)); + return job; + } } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java index f3b1ad905..4e23035bf 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java @@ -66,14 +66,6 @@ class CoveragePluginITest extends AbstractCoverageITest { * All lines in {@value JACOCO_ANALYSIS_MODEL_FILE} and {@value COBERTURA_HIGHER_COVERAGE_FILE}. */ private static final int JACOCO_COBERTURA_ALL_LINES = 6370; - /** - * Jacoco file for testing. - */ - private static final String JACOCO_ANALYSIS_MODEL_FILE = "jacoco-analysis-model.xml"; - /** - * Another jacoco file for testing. - */ - private static final String JACOCO_CODINGSTYLE_FILE = "jacoco-codingstyle.xml"; /** * Cobertura file for testing. */ @@ -137,7 +129,8 @@ private void verifyNoFilesFound(final ParameterizedJob project) { @Test void shouldRecordOneJacocoResultInFreestyleJob() { - FreeStyleProject project = createFreestyleJob(CoverageParser.JACOCO, JACOCO_ANALYSIS_MODEL_FILE); + FreeStyleProject project = createFreestyleJob(CoverageParser.JACOCO, + JACOCO_ANALYSIS_MODEL_FILE); // FIXME: which parser is correct? /* @@ -154,6 +147,13 @@ void shouldRecordOneJacocoResultInPipeline() { verifyOneJacocoResult(job); } + @Test + void shouldRecordOneJacocoResultInDeclarativePipeline() { + WorkflowJob job = createDeclarativePipeline(CoverageParser.JACOCO, JACOCO_ANALYSIS_MODEL_FILE); + + verifyOneJacocoResult(job); + } + private void verifyOneJacocoResult(final ParameterizedJob project) { Run build = buildSuccessfully(project); @@ -168,7 +168,7 @@ private void verifyOneJacocoResult(final ParameterizedJob project) { @Test void shouldRecordTwoJacocoResultsInFreestyleJob() { FreeStyleProject project = createFreestyleJob(CoverageParser.JACOCO, - JACOCO_ANALYSIS_MODEL_FILE, JACOCO_CODINGSTYLE_FILE); + JACOCO_ANALYSIS_MODEL_FILE, JACOCO_CODING_STYLE_FILE); // FIXME: which parser is correct? /* @@ -181,7 +181,7 @@ void shouldRecordTwoJacocoResultsInFreestyleJob() { @Test void shouldRecordTwoJacocoResultsInPipeline() { WorkflowJob job = createPipeline(CoverageParser.JACOCO, - JACOCO_ANALYSIS_MODEL_FILE, JACOCO_CODINGSTYLE_FILE); + JACOCO_ANALYSIS_MODEL_FILE, JACOCO_CODING_STYLE_FILE); verifyTwoJacocoResults(job); } @@ -212,6 +212,13 @@ void shouldRecordOneCoberturaResultInPipeline() { verifyOneCoberturaResult(job); } + @Test + void shouldRecordOneCoberturaResultInDeclarativePipeline() { + WorkflowJob job = createDeclarativePipeline(CoverageParser.COBERTURA, COBERTURA_HIGHER_COVERAGE_FILE); + + verifyOneCoberturaResult(job); + } + private void verifyOneCoberturaResult(final ParameterizedJob project) { Run build = buildSuccessfully(project); @@ -308,7 +315,8 @@ void freestyleForOneCoberturaAndOneJacoco() { List coverageAdapters = new ArrayList<>(); - JacocoReportAdapter jacocoReportAdapter = new JacocoReportAdapter(JACOCO_ANALYSIS_MODEL_FILE); + JacocoReportAdapter jacocoReportAdapter = new JacocoReportAdapter( + JACOCO_ANALYSIS_MODEL_FILE); coverageAdapters.add(jacocoReportAdapter); CoberturaReportAdapter coberturaReportAdapter = new CoberturaReportAdapter(COBERTURA_HIGHER_COVERAGE_FILE); diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeclarativePipelineSupportITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeclarativePipelineSupportITest.java deleted file mode 100644 index a5bc25899..000000000 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeclarativePipelineSupportITest.java +++ /dev/null @@ -1,48 +0,0 @@ -package io.jenkins.plugins.coverage.model; - -import org.junit.jupiter.api.Test; - -import edu.hm.hafner.metric.Coverage.CoverageBuilder; -import edu.hm.hafner.metric.Metric; - -import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; -import org.jenkinsci.plugins.workflow.job.WorkflowJob; -import hudson.model.Run; - -import io.jenkins.plugins.util.IntegrationTestWithJenkinsPerSuite; - -import static org.assertj.core.api.Assertions.*; - -/** - * Integration test to check if declarative pipelines are supported. - */ -class DeclarativePipelineSupportITest extends IntegrationTestWithJenkinsPerSuite { - private static final String JACOCO_ANALYSIS_MODEL_XML = "jacoco-analysis-model.xml"; - - /** - * Check if code coverage is supported in declarative pipelines. - */ - @Test - void declarativePipelineSupportJacoco() { - WorkflowJob job = createPipelineWithWorkspaceFiles(JACOCO_ANALYSIS_MODEL_XML); - - job.setDefinition(new CpsFlowDefinition("pipeline {\n" - + " agent any\n" - + " stages {\n" - + " stage('Test') {\n" - + " steps {\n" - + " publishCoverage(\n" - + " adapters: [\n" - + " jacocoAdapter('" + JACOCO_ANALYSIS_MODEL_XML + "')\n" - + " ],\n" - + " )}\n" - + " }\n" - + " }\n" - + "}", true)); - Run build = buildSuccessfully(job); - assertThat(build.getAction(CoverageBuildAction.class).getLineCoverage()) - .isEqualTo(new CoverageBuilder().setMetric(Metric.LINE).setCovered(6083).setMissed(6368 - 6083).build()); - } -} - - From 64acf68899f9f9dc04f557fffbfa1db8effcafd3 Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Fri, 21 Oct 2022 22:20:48 +0200 Subject: [PATCH 14/29] Fix summary: now also LOC and COMPLEXITY will be shown. --- .../model/ChangeCoverageTableModel.java | 2 +- .../coverage/model/CoverageBuildAction.java | 32 +++++++++++++++---- .../coverage/model/CoverageFormatter.java | 6 +++- .../coverage/model/CoverageTableModel.java | 8 ++--- .../model/IndirectCoverageChangesTable.java | 5 ++- .../model/CoverageBuildAction/summary.jelly | 8 ++--- .../model/CoverageBuildActionTest.java | 6 ++-- .../coverage/model/CoveragePluginITest.java | 3 ++ ...DeltaComputationVsReferenceBuildITest.java | 17 ++++++---- 9 files changed, 57 insertions(+), 30 deletions(-) diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/ChangeCoverageTableModel.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/ChangeCoverageTableModel.java index 8d10ce0b4..a6f504621 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/ChangeCoverageTableModel.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/ChangeCoverageTableModel.java @@ -102,7 +102,7 @@ public int getLoc() { private DetailedCell createColoredChangeCoverageDeltaColumn(final Metric metric) { Coverage changeCoverage = getFile().getTypedValue(metric, Coverage.nullObject(metric)); if (changeCoverage.isSet()) { - return createColoredCoverageDeltaColumn( + return createColoredCoverageDeltaColumn(metric, changeCoverage.delta(originalFile.getTypedValue(metric, Coverage.nullObject(metric)))); } return NO_COVERAGE; diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageBuildAction.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageBuildAction.java index 7a116b6dd..ade70050d 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageBuildAction.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageBuildAction.java @@ -3,9 +3,11 @@ import java.util.Collection; import java.util.Map; import java.util.NavigableMap; +import java.util.NavigableSet; import java.util.Optional; import java.util.Set; import java.util.TreeMap; +import java.util.TreeSet; import java.util.stream.Collectors; import org.apache.commons.lang3.math.Fraction; @@ -25,6 +27,7 @@ import hudson.model.HealthReportingAction; import hudson.model.Run; +import io.jenkins.plugins.coverage.model.CoverageViewModel.ValueLabelProvider; import io.jenkins.plugins.coverage.model.CoverageXmlStream.FractionConverter; import io.jenkins.plugins.forensics.reference.ReferenceBuild; import io.jenkins.plugins.util.AbstractXmlStream; @@ -44,7 +47,7 @@ public class CoverageBuildAction extends BuildAction implements HealthReportingAction, StaplerProxy { /** Relative URL to the details of the code coverage results. */ public static final String DETAILS_URL = "coverage"; - /** The coverage report icon. */ + /** The coverage report symbol from the Ionicons plugin. */ public static final String ICON = "symbol-footsteps-outline plugin-ionicons-api"; private static final long serialVersionUID = -6023811049340671399L; @@ -52,6 +55,7 @@ public class CoverageBuildAction extends BuildAction implements HealthRepo private static final String NO_REFERENCE_BUILD = "-"; private static final CoverageFormatter FORMATTER = new CoverageFormatter(); + private static final ValueLabelProvider VALUE_LABEL_PROVIDER = new ValueLabelProvider(); private final HealthReport healthReport; @@ -142,12 +146,27 @@ public CoverageBuildAction(final Run owner, final Node result, this.healthReport = healthReport; } + public NavigableSet getMetricsForSummary() { + var metrics = new TreeSet(); + if (hasCoverage(Metric.LINE)) { + metrics.add(Metric.LINE); + metrics.add(Metric.LOC); + } + if (hasCoverage(Metric.BRANCH)) { + metrics.add(Metric.BRANCH); + } + if (hasCoverage(Metric.COMPLEXITY)) { + metrics.add(Metric.COMPLEXITY); + } + return metrics; + } + public Coverage getLineCoverage() { - return ((Coverage)getCoverage(Metric.LINE)); + return (Coverage) getCoverage(Metric.LINE); } public Coverage getBranchCoverage() { - return ((Coverage)getCoverage(Metric.BRANCH)); + return (Coverage) getCoverage(Metric.BRANCH); } /** @@ -298,7 +317,7 @@ public boolean hasDelta(final Metric metric) { @SuppressWarnings("unused") // Called by jelly view public String formatDelta(final Metric metric) { if (hasDelta(metric)) { - return FORMATTER.formatDelta(difference.get(metric), Functions.getCurrentLocale()); + return FORMATTER.formatDelta(metric, difference.get(metric), Functions.getCurrentLocale()); } return Messages.Coverage_Not_Available(); } @@ -396,7 +415,7 @@ public boolean hasCodeChanges() { @SuppressWarnings("unused") // Called by jelly view public String formatChangeCoverageDifference(final Metric metric) { if (hasChangeCoverage(metric)) { - return FORMATTER.formatDelta(changeCoverageDifference.get(metric), Functions.getCurrentLocale()); + return FORMATTER.formatDelta(metric, changeCoverageDifference.get(metric), Functions.getCurrentLocale()); } return Messages.Coverage_Not_Available(); } @@ -504,7 +523,8 @@ private String getFormattedChangesOverview(final long lineAmount, final int file */ @SuppressWarnings("unused") // Called by jelly view public String formatCoverage(final Metric metric) { - return metric + ": " + FORMATTER.format(getCoverage(metric), Functions.getCurrentLocale()); + return VALUE_LABEL_PROVIDER.getDisplayName(metric) + ": " + + FORMATTER.format(getCoverage(metric), Functions.getCurrentLocale()); } @Override diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageFormatter.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageFormatter.java index 63626629b..e76ed3b22 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageFormatter.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageFormatter.java @@ -6,6 +6,7 @@ import edu.hm.hafner.metric.Coverage; import edu.hm.hafner.metric.IntegerValue; +import edu.hm.hafner.metric.Metric; import edu.hm.hafner.metric.MutationValue; import edu.hm.hafner.metric.Value; @@ -54,7 +55,10 @@ private String formatPercentage(final Fraction fraction, final Locale locale) { * * @return the formatted delta percentage as plain text with a leading sign */ - public String formatDelta(final Fraction fraction, final Locale locale) { + public String formatDelta(final Metric metric, final Fraction fraction, final Locale locale) { + if (metric.equals(Metric.COMPLEXITY) || metric.equals(Metric.LOC)) { // TODO: move to metric? + return String.format(locale, "%+d", fraction.intValue()); + } return String.format(locale, "%+.2f%%", fraction.multiplyBy(HUNDRED).doubleValue()); } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTableModel.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTableModel.java index 72a7aa41b..c33b8ffd9 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTableModel.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTableModel.java @@ -178,7 +178,7 @@ static class CoverageRow { this.file = file; this.browserLocale = browserLocale; this.renderer = renderer; - this.colorProvider = colors; + colorProvider = colors; } public String getFileHash() { @@ -249,14 +249,14 @@ protected DetailedCell createColoredCoverageColumn(final Coverage coverage) { * * @return the created {@link DetailedCell} */ - protected DetailedCell createColoredCoverageDeltaColumn(final Fraction delta) { + protected DetailedCell createColoredCoverageDeltaColumn(final Metric metric, final Fraction delta) { double percentage = delta.doubleValue() * 100.0; DisplayColors colors = CoverageChangeTendency.getDisplayColorsForTendency(percentage, colorProvider); String cell = div().withClasses(COVERAGE_COLUMN_OUTER).with( div().withClasses(COVERAGE_COLUMN_INNER) .withStyle(String.format("background-color:%s;", colors.getFillColorAsRGBAHex( TABLE_COVERAGE_COLOR_ALPHA))) - .withText(FORMATTER.formatDelta(delta, browserLocale))) + .withText(FORMATTER.formatDelta(metric, delta, browserLocale))) .render(); return new DetailedCell<>(cell, percentage); } @@ -277,7 +277,7 @@ protected FileNode getFile() { */ private DetailedCell createColoredFileCoverageDeltaColumn(final Metric metric) { if (file.hasChangeCoverage(metric)) { - return createColoredCoverageDeltaColumn(file.getChangeCoverage(metric)); + return createColoredCoverageDeltaColumn(metric, file.getChangeCoverage(metric)); } return NO_COVERAGE; } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/IndirectCoverageChangesTable.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/IndirectCoverageChangesTable.java index 6a49ad471..bdfbf1f46 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/IndirectCoverageChangesTable.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/IndirectCoverageChangesTable.java @@ -91,11 +91,10 @@ public int getLoc() { return originalFile.getIndirectCoverageChanges().size(); } - private DetailedCell createColoredChangeCoverageDeltaColumn( - final Metric metric) { + private DetailedCell createColoredChangeCoverageDeltaColumn(final Metric metric) { Coverage changeCoverage = getFile().getTypedValue(metric, Coverage.nullObject(metric)); if (changeCoverage.isSet()) { - return createColoredCoverageDeltaColumn( + return createColoredCoverageDeltaColumn(metric, changeCoverage.delta(originalFile.getTypedValue(metric, Coverage.nullObject(metric)))); } return NO_COVERAGE; diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageBuildAction/summary.jelly b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageBuildAction/summary.jelly index dd1d527e0..c8d3bf7bb 100644 --- a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageBuildAction/summary.jelly +++ b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageBuildAction/summary.jelly @@ -3,10 +3,8 @@ - - ${%Coverage Report} - - + + ${%Coverage Report}
  • @@ -100,7 +98,7 @@
  • - Reference build: + Reference build:
  • diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageBuildActionTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageBuildActionTest.java index 80d39ef71..5b93b3e96 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageBuildActionTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageBuildActionTest.java @@ -90,7 +90,6 @@ void shouldNotLoadResultIfDeltasArePersistedInAction() { CoverageBuilder coverageBuilder = new CoverageBuilder(); - Coverage percent50 = coverageBuilder.setMetric(Metric.BRANCH).setCovered(1).setMissed(1).build(); coverages.put(Metric.BRANCH, percent50); deltas.put(Metric.BRANCH, percent50.getCoveredPercentage()); @@ -151,9 +150,8 @@ void shouldGetCoverageForSpecifiedMetric() { CoverageBuildAction action = createCoverageBuildAction(); assertThat(action.hasCoverage(COVERAGE_METRIC)).isTrue(); - assertThat(action.getCoverage(COVERAGE_METRIC)) - .isEqualTo(VALUE); - assertThat(action.formatCoverage(COVERAGE_METRIC)).isEqualTo("LINE: 50.00%"); + assertThat(action.getCoverage(COVERAGE_METRIC)).isEqualTo(VALUE); + assertThat(action.formatCoverage(COVERAGE_METRIC)).isEqualTo("Line: 50.00%"); } @Test diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java index 4e23035bf..f100bbc08 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java @@ -158,6 +158,9 @@ private void verifyOneJacocoResult(final ParameterizedJob project) { Run build = buildSuccessfully(project); CoverageBuildAction coverageResult = build.getAction(CoverageBuildAction.class); + assertThat(coverageResult.getMetricsForSummary()) + .containsExactly(Metric.LINE, Metric.BRANCH, Metric.COMPLEXITY, Metric.LOC); + assertThat(coverageResult.getLineCoverage()) .isEqualTo(createLineCoverageBuilder() .setCovered(JACOCO_COVERED_LINES) diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeltaComputationVsReferenceBuildITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeltaComputationVsReferenceBuildITest.java index e1bdcfb56..2f7c4a07d 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeltaComputationVsReferenceBuildITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeltaComputationVsReferenceBuildITest.java @@ -98,22 +98,27 @@ private static void verifySecondBuild(final Run secondBuild) { private void verifyDeltaComputation(final Run firstBuild, final Run secondBuild) { assertThat(secondBuild.getAction(CoverageBuildAction.class)).isNotNull(); - CoverageBuildAction coverageBuildAction = secondBuild.getAction(CoverageBuildAction.class); + CoverageBuildAction action = secondBuild.getAction(CoverageBuildAction.class); - assertThat(coverageBuildAction).isNotNull(); - assertThat(coverageBuildAction.getReferenceBuild()) + assertThat(action).isNotNull(); + assertThat(action.getReferenceBuild()) .isPresent() .satisfies(reference -> assertThat(reference.get()).isEqualTo(firstBuild)); - var delta = coverageBuildAction.getDelta(); + var delta = action.getDelta(); assertThat(delta.get(MODULE).doubleValue()).isCloseTo(0, withPercentage(1.0)); assertThat(delta.get(PACKAGE).doubleValue()).isCloseTo(0, withPercentage(1.0)); - assertThat(delta.get(LINE).doubleValue()).isCloseTo(-0.0415, withPercentage(1.0)); + assertThat(delta.get(LINE).doubleValue()).isCloseTo(-0.0416, withPercentage(1.0)); assertThat(delta.get(BRANCH).doubleValue()).isCloseTo(0.0533, withPercentage(1.0)); assertThat(delta.get(LOC).intValue()).isEqualTo(-5857); assertThat(delta.get(COMPLEXITY).intValue()).isEqualTo(160 - 2718); - verifyChangeCoverage(coverageBuildAction); + assertThat(action.formatDelta(LINE)).isEqualTo("-4.16%"); + assertThat(action.formatDelta(BRANCH)).isEqualTo("+5.33%"); + assertThat(action.formatDelta(LOC)).isEqualTo("-5857"); + assertThat(action.formatDelta(COMPLEXITY)).isEqualTo("-2558"); + + verifyChangeCoverage(action); } /** From 54f0e0f16e77ca6c547aa0698a20cf13b0ec3eba Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Fri, 21 Oct 2022 23:07:54 +0200 Subject: [PATCH 15/29] Fix creation of coverage tree. --- .../coverage/model/CoverageTreeCreator.java | 16 +++++++++++----- .../coverage/model/CoverageTreeCreatorTest.java | 11 ++++++----- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTreeCreator.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTreeCreator.java index 5cd8a537d..13799fc62 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTreeCreator.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTreeCreator.java @@ -75,16 +75,19 @@ private boolean calculateChangeCoverageTree(final Node root) { // this is required since there might be changes which do not affect the code coverage -> ignore these files return fileNode.hasCoveredLinesInChangeSet(); } - Iterator nodeIterator = root.getChildren().iterator(); + var children = root.getChildren(); + Iterator nodeIterator = children.iterator(); boolean hasChanged = false; while (nodeIterator.hasNext()) { Node child = nodeIterator.next(); - boolean childHasChanged = calculateChangeCoverageTree(child); - if (!childHasChanged) { + boolean hasChildChanges = calculateChangeCoverageTree(child); + if (!hasChildChanges) { nodeIterator.remove(); } - hasChanged |= childHasChanged; + hasChanged |= hasChildChanges; } + root.clearChildren(); + root.addAllChildren(children); return hasChanged; } @@ -101,7 +104,8 @@ private boolean calculateIndirectCoverageChangesTree(final Node root) { clearChildrenAndLeaves(root); return ((FileNode) root).hasIndirectCoverageChanges(); } - Iterator nodeIterator = root.getChildren().iterator(); + var children = root.getChildren(); + Iterator nodeIterator = children.iterator(); boolean hasChangedCoverage = false; while (nodeIterator.hasNext()) { Node child = nodeIterator.next(); @@ -111,6 +115,8 @@ private boolean calculateIndirectCoverageChangesTree(final Node root) { } hasChangedCoverage |= childHasChangedCoverage; } + root.clearChildren(); + root.addAllChildren(children); return hasChangedCoverage; } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTreeCreatorTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTreeCreatorTest.java index 7597fa943..bf7ecd2a6 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTreeCreatorTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTreeCreatorTest.java @@ -8,7 +8,6 @@ import org.junit.jupiter.api.Test; -import edu.hm.hafner.metric.Coverage; import edu.hm.hafner.metric.Coverage.CoverageBuilder; import edu.hm.hafner.metric.FileNode; import edu.hm.hafner.metric.Metric; @@ -70,10 +69,11 @@ void shouldCreateChangeCoverageTree() { assertThat(root.getPath()).isEqualTo(tree.getPath()); assertThat(root.getMetric()).isEqualTo(tree.getMetric()); assertThat(root.getAll(FILE)).hasSize(1); + var builder = new CoverageBuilder(); assertThat(root.getValue(LINE)).isNotEmpty().contains( - new Coverage.CoverageBuilder().setCovered(2).setMissed(2).build()); + builder.setMetric(Metric.LINE).setCovered(2).setMissed(2).build()); assertThat(root.getValue(BRANCH)).isNotEmpty().contains( - new Coverage.CoverageBuilder().setCovered(4).setMissed(4).build()); + builder.setMetric((Metric.BRANCH)).setCovered(4).setMissed(4).build()); }); } @@ -90,10 +90,11 @@ void shouldCreateIndirectCoverageChangesTree() { assertThat(root.getPath()).isEqualTo(tree.getPath()); assertThat(root.getMetric()).isEqualTo(tree.getMetric()); assertThat(root.getAll(FILE)).hasSize(1); + var builder = new CoverageBuilder(); assertThat(root.getValue(LINE)).isNotEmpty().contains( - new Coverage.CoverageBuilder().setCovered(2).setMissed(2).build()); + builder.setMetric(Metric.LINE).setCovered(2).setMissed(2).build()); assertThat(root.getValue(BRANCH)).isNotEmpty().contains( - new Coverage.CoverageBuilder().setCovered(4).setMissed(4).build()); + builder.setMetric(Metric.BRANCH).setCovered(4).setMissed(4).build()); }); } From a3b3304e730d3a44afd337a5e908636dd274396e Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Sat, 22 Oct 2022 22:55:50 +0200 Subject: [PATCH 16/29] Fix creation of coverage tree. --- .../coverage/model/CoverageTreeCreator.java | 62 +++++++------------ .../model/CoverageBuildAction/summary.jelly | 20 +++--- .../model/CoverageTreeCreatorTest.java | 9 ++- ...dITest.java => DeltaComputationITest.java} | 2 +- 4 files changed, 40 insertions(+), 53 deletions(-) rename plugin/src/test/java/io/jenkins/plugins/coverage/model/{DeltaComputationVsReferenceBuildITest.java => DeltaComputationITest.java} (98%) diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTreeCreator.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTreeCreator.java index 13799fc62..72d4b0afb 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTreeCreator.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTreeCreator.java @@ -1,8 +1,8 @@ package io.jenkins.plugins.coverage.model; import java.util.Iterator; -import java.util.List; import java.util.Map; +import java.util.SortedSet; import edu.hm.hafner.metric.Coverage; import edu.hm.hafner.metric.Coverage.CoverageBuilder; @@ -128,12 +128,11 @@ private boolean calculateIndirectCoverageChangesTree(final Node root) { */ private void attachChangeCoverageLeaves(final Node node) { node.getAllFileNodes() - .forEach(fileNode -> createChangeCoverageLeaves(fileNode, fileNode.getCoverageOfChangeSet())); + .forEach(fileNode -> createChangeCoverageLeaves(fileNode, fileNode.getChangedLines())); } /** - * Attaches leaves to the passed {@link Node node} which represent its underlying indirect coverage - * changes. + * Attaches leaves to the passed {@link Node node} which represent its underlying indirect coverage changes. * * @param node * The node which contains indirect coverage changes @@ -149,39 +148,23 @@ private void attachIndirectCoverageChangesLeaves(final Node node) { * * @param fileNode * The node the leaves are attached to - * @param changes + * @param changedLines * The {@link Coverage} to be represented by the leaves */ - private void createChangeCoverageLeaves(final FileNode fileNode, final List changes) { - Coverage lineCoverage = Coverage.nullObject(Metric.LINE); - Coverage branchCoverage = Coverage.nullObject(Metric.BRANCH); - - CoverageBuilder builder = new CoverageBuilder(); - for (Coverage change : changes) { - int covered = change.getCovered() > 0 ? 1 : 0; - if (change.getTotal() > 1) { - builder.setMetric(Metric.BRANCH).setCovered(change.getCovered()).setMissed(change.getMissed()); - branchCoverage = branchCoverage.add(builder.build()); - builder.setMetric(Metric.LINE).setCovered(covered).setMissed(1 - covered); - lineCoverage = lineCoverage.add(builder.build()); - } - else { - int missed = change.getMissed() > 0 ? 1 : 0; - builder.setMetric(Metric.LINE).setCovered(covered).setMissed(missed); - lineCoverage = lineCoverage.add(builder.build()); - } - } - if (lineCoverage.isSet()) { - fileNode.addValue(lineCoverage); - } - if (branchCoverage.isSet()) { - fileNode.addValue(branchCoverage); - } + private void createChangeCoverageLeaves(final FileNode fileNode, final SortedSet changedLines) { + fileNode.addValue(changedLines.stream() + .map(fileNode::getLineCoverage) + .reduce(Coverage::add) + .orElse(Coverage.nullObject(Metric.LINE))); + fileNode.addValue(changedLines.stream() + .map(fileNode::getBranchCoverage) + .reduce(Coverage::add) + .orElse(Coverage.nullObject(Metric.BRANCH))); } /** - * Creates both a line and a branch indirect coverage changes leaf for the passed {@link FileNode node}. The - * leaves represent the delta for a file regarding the amount of lines / branches that got hit by tests. + * Creates both a line and a branch indirect coverage changes leaf for the passed {@link FileNode node}. The leaves + * represent the delta for a file regarding the amount of lines / branches that got hit by tests. * * @param fileNode * The node the leaves are attached to @@ -193,30 +176,33 @@ private void createIndirectCoverageChangesLeaves(final FileNode fileNode) { Coverage branchCoverage = Coverage.nullObject(Metric.BRANCH); for (Map.Entry change : fileNode.getIndirectCoverageChanges().entrySet()) { int delta = change.getValue(); - Coverage currentLineCoverage = fileNode.getLineCoverage(change.getKey()); - Coverage currentBranchCoverage = fileNode.getBranchCoverage(change.getKey()); + Coverage currentCoverage = fileNode.getBranchCoverage(change.getKey()); + if (!currentCoverage.isSet()) { + currentCoverage = fileNode.getLineCoverage(change.getKey()); + } CoverageBuilder builder = new CoverageBuilder(); if (delta > 0) { // the line is fully covered - even in case of branch coverage - if (delta == currentLineCoverage.getCovered()) { + if (delta == currentCoverage.getCovered()) { builder.setMetric(Metric.LINE).setCovered(1).setMissed(0); lineCoverage = lineCoverage.add(builder.build()); } // the branch coverage increased for 'delta' hits - if (currentBranchCoverage.getTotal() > 1) { + if (currentCoverage.getTotal() > 1) { builder.setMetric(Metric.BRANCH).setCovered(delta).setMissed(0); branchCoverage = branchCoverage.add(builder.build()); } } else if (delta < 0) { // the line is not covered anymore - if (currentLineCoverage.getCovered() == 0) { + if (currentCoverage.getCovered() == 0) { builder.setMetric(Metric.LINE).setCovered(0).setMissed(1); lineCoverage = lineCoverage.add(builder.build()); } // the branch coverage is decreased by 'delta' hits - if (currentBranchCoverage.getTotal() > 1) { + if (currentCoverage.getTotal() > 1) { builder.setMetric(Metric.BRANCH).setCovered(0).setMissed(Math.abs(delta)); + branchCoverage = branchCoverage.add(builder.build()); } } } diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageBuildAction/summary.jelly b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageBuildAction/summary.jelly index c8d3bf7bb..d5f588711 100644 --- a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageBuildAction/summary.jelly +++ b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageBuildAction/summary.jelly @@ -7,16 +7,14 @@ ${%Coverage Report}
    • - - Project Coverage: - + ${%Project Coverage:}
        - +
      • ${it.formatCoverage(metric)} + tooltip="The delta of the code coverage with respect to the reference build"> @@ -29,7 +27,7 @@
      • - +
    • @@ -41,11 +39,11 @@
        - +
      • ${it.formatChangeCoverage(metric)} + tooltip="The delta of the change coverage against the overall coverage"> @@ -60,7 +58,7 @@
      • - +
      • ${it.formatChangeCoverageOverview()}
      @@ -84,12 +82,12 @@
        + tooltip="The indirect coverage changes of only modified lines of code (the value represents newly covered vs. uncovered lines)">
      • ${it.formatIndirectCoverageChanges(metric)}
      • + tooltip="The amount of unmodified lines which contain indirect coverage changes">
      • ${it.formatIndirectCoverageChangesOverview()}
      diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTreeCreatorTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTreeCreatorTest.java index bf7ecd2a6..1f6f2718b 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTreeCreatorTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTreeCreatorTest.java @@ -73,7 +73,7 @@ void shouldCreateChangeCoverageTree() { assertThat(root.getValue(LINE)).isNotEmpty().contains( builder.setMetric(Metric.LINE).setCovered(2).setMissed(2).build()); assertThat(root.getValue(BRANCH)).isNotEmpty().contains( - builder.setMetric((Metric.BRANCH)).setCovered(4).setMissed(4).build()); + builder.setMetric(Metric.BRANCH).setCovered(4).setMissed(4).build()); }); } @@ -167,9 +167,12 @@ private FileNode attachFileCoverageNodeToTree(final Node root) { private void attachCoveragePerLine(final FileNode file) { var builder = new CoverageBuilder().setMetric(Metric.LINE); file.addLineCoverage(10, builder.setCovered(1).setMissed(0).build()); - file.addLineCoverage(11, builder.setCovered(0).setMissed(4).build()); - file.addLineCoverage(12, builder.setCovered(4).setMissed(0).build()); + file.addLineCoverage(11, builder.setCovered(0).setMissed(1).build()); + file.addLineCoverage(12, builder.setCovered(1).setMissed(0).build()); file.addLineCoverage(13, builder.setCovered(0).setMissed(1).build()); + builder.setMetric(Metric.BRANCH); + file.addBranchCoverage(11, builder.setCovered(0).setMissed(4).build()); + file.addBranchCoverage(12, builder.setCovered(4).setMissed(0).build()); } /** diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeltaComputationVsReferenceBuildITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeltaComputationITest.java similarity index 98% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/DeltaComputationVsReferenceBuildITest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/model/DeltaComputationITest.java index 2f7c4a07d..eeca11d42 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeltaComputationVsReferenceBuildITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeltaComputationITest.java @@ -22,7 +22,7 @@ /** * Integration test for delta computation of reference builds. */ -class DeltaComputationVsReferenceBuildITest extends AbstractCoverageITest { +class DeltaComputationITest extends AbstractCoverageITest { @Test void shouldComputeDeltaInFreestyleJob() { FreeStyleProject project = createFreestyleJob(CoverageParser.JACOCO, From a58ebb57a8c16b17f3d9b69e033efcf3fe184bcc Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Sun, 23 Oct 2022 23:21:05 +0200 Subject: [PATCH 17/29] Restore source code coloring. --- .../coverage/metrics/CoverageRecorder.java | 2 - .../coverage/model/CoverageFormatter.java | 12 ++++ .../coverage/model/CoverageReporter.java | 2 +- .../model/visualization/code/PaintedNode.java | 52 ++++++++++++++ .../visualization/code/SourceCodeFacade.java | 68 +++++++------------ .../visualization/code/SourceCodePainter.java | 14 ++-- .../coverage/model/Messages.properties | 2 +- .../model/CoveragePluginSourceITest.java | 52 +++----------- .../DockerCoveragePluginSourceITest.java | 34 ++++++---- 9 files changed, 128 insertions(+), 110 deletions(-) create mode 100644 plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/code/PaintedNode.java diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageRecorder.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageRecorder.java index 365677917..39307a05d 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageRecorder.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageRecorder.java @@ -241,8 +241,6 @@ public String getScm() { return scm; } - - @Override public BuildStepMonitor getRequiredMonitorService() { return BuildStepMonitor.NONE; diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageFormatter.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageFormatter.java index e76ed3b22..c981facca 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageFormatter.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageFormatter.java @@ -46,6 +46,18 @@ private String formatPercentage(final Fraction fraction, final Locale locale) { return String.format(locale, "%.2f%%", fraction.multiplyBy(HUNDRED).doubleValue()); } + /** + * Formats a percentage to plain text and rounds the value to two decimals. + * + * @param locale + * The used locale + * + * @return the formatted percentage as plain text + */ + public String formatPercentage(final int covered, final int total, final Locale locale) { + return formatPercentage(Fraction.getFraction(covered, total), locale); + } + /** * Formats a delta percentage to its plain text representation with a leading sign and rounds the value to two * decimals. diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageReporter.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageReporter.java index f2fa6506d..a1e5c5e70 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageReporter.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageReporter.java @@ -167,7 +167,7 @@ private void runWithModel(final Run build, final FilePath workspace, final log.logInfo("Executing source code painting..."); SourceCodePainter sourceCodePainter = new SourceCodePainter(build, workspace); - sourceCodePainter.processSourceCodePainting(paintedFiles, sourceDirectories, + sourceCodePainter.processSourceCodePainting(rootNode, sourceDirectories, sourceCodeEncoding, sourceCodeRetention, log); log.logInfo("Finished coverage processing - adding the action to the build..."); diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/code/PaintedNode.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/code/PaintedNode.java new file mode 100644 index 000000000..5437ecf59 --- /dev/null +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/code/PaintedNode.java @@ -0,0 +1,52 @@ +package io.jenkins.plugins.coverage.model.visualization.code; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.SortedSet; + +import edu.hm.hafner.metric.FileNode; + +/** + * FIXME: comment class. + * + * @author Ullrich Hafner + */ +class PaintedNode implements Serializable { + private static final long serialVersionUID = -6044649044983631852L; + private final String path; + private final SortedSet coveredLines; + private final Map coveredPerLine; + private final Map missedPerLine; + + public PaintedNode(final FileNode file) { + path = file.getPath(); + coveredLines = file.getCoveredLines(); + coveredPerLine = new HashMap<>(); + missedPerLine = new HashMap<>(); + for (Integer line : coveredLines) { + var coverage = file.getBranchCoverage(line); + if (!coverage.isSet()) { + coverage = file.getLineCoverage(line); + } + coveredPerLine.put(line, coverage.getCovered()); + missedPerLine.put(line, coverage.getMissed()); + } + } + + public String getPath() { + return path; + } + + public boolean isPainted(final int line) { + return coveredLines.contains(line); + } + + public int getCovered(final int line) { + return coveredPerLine.getOrDefault(line, 0); + } + + public int getMissed(final int line) { + return missedPerLine.getOrDefault(line, 0); + } +} diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodeFacade.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodeFacade.java index 5c17bbb4a..1cc815e41 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodeFacade.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodeFacade.java @@ -3,15 +3,14 @@ import java.io.BufferedWriter; import java.io.File; import java.io.IOException; -import java.io.Serializable; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; +import java.util.Locale; import java.util.Map; -import java.util.Map.Entry; import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -30,7 +29,6 @@ import org.jsoup.select.Elements; import edu.hm.hafner.metric.FileNode; -import edu.hm.hafner.metric.Node; import edu.hm.hafner.util.FilteredLog; import hudson.FilePath; @@ -39,7 +37,7 @@ import hudson.util.TextFile; import jenkins.MasterToSlaveFileCallable; -import io.jenkins.plugins.coverage.targets.CoveragePaint; +import io.jenkins.plugins.coverage.model.CoverageFormatter; import io.jenkins.plugins.prism.CharsetValidation; import io.jenkins.plugins.prism.FilePermissionEnforcer; import io.jenkins.plugins.prism.SourceDirectoryFilter; @@ -351,14 +349,12 @@ private static String sanitizeFilename(final String inputName) { * @param directory * the subdirectory where the source files will be stored in */ - AgentCoveragePainter(final Set> paintedFiles, + AgentCoveragePainter(final List paintedFiles, final Set permittedSourceDirectories, final Set requestedSourceDirectories, final String sourceCodeEncoding, final String directory) { super(); - this.paintedFiles = paintedFiles.stream() - .map(e -> new PaintedNode(e.getKey(), e.getValue())) - .collect(Collectors.toList()); + this.paintedFiles = paintedFiles; this.permittedSourceDirectories = permittedSourceDirectories; this.requestedSourceDirectories = requestedSourceDirectories; this.sourceCodeEncoding = sourceCodeEncoding; @@ -430,15 +426,15 @@ private Set filterSourceDirectories(final File workspace, final Filtered private int paintSource(final PaintedNode fileNode, final FilePath workspace, final Path temporaryFolder, final Set sourceSearchDirectories, final Charset sourceEncoding, final FilteredLog log) { - String relativePathIdentifier = fileNode.getNode().getPath(); + String relativePathIdentifier = fileNode.getPath(); FilePath paintedFilesDirectory = workspace.child(directory); return findSourceFile(workspace, relativePathIdentifier, sourceSearchDirectories, log) - .map(resolvedPath -> paint(fileNode.getPaint(), relativePathIdentifier, resolvedPath, + .map(resolvedPath -> paint(fileNode, relativePathIdentifier, resolvedPath, paintedFilesDirectory, temporaryFolder, sourceEncoding, log)) .orElse(0); } - private int paint(final CoveragePaint paint, final String relativePathIdentifier, final FilePath resolvedPath, + private int paint(final PaintedNode paint, final String relativePathIdentifier, final FilePath resolvedPath, final FilePath paintedFilesDirectory, final Path temporaryFolder, final Charset charset, final FilteredLog log) { String sanitizedFileName = sanitizeFilename(relativePathIdentifier); @@ -452,7 +448,6 @@ private int paint(final CoveragePaint paint, final String relativePathIdentifier String content = lines.get(line); paintLine(line + 1, content, paint, output); } - paint.setTotalLines(lines.size()); } new FilePath(fullSourcePath.toFile()).zip(zipOutputPath); FileUtils.deleteDirectory(paintedFilesFolder.toFile()); @@ -465,28 +460,29 @@ private int paint(final CoveragePaint paint, final String relativePathIdentifier } } - private void paintLine(final int line, final String content, final CoveragePaint paint, + private void paintLine(final int line, final String content, final PaintedNode paint, final BufferedWriter output) throws IOException { if (paint.isPainted(line)) { - final int hits = paint.getHits(line); - final int branchCoverage = paint.getBranchCoverage(line); - final int branchTotal = paint.getBranchTotal(line); - final int coveragePercent = (hits == 0) ? 0 : (int) (branchCoverage * 100.0 / branchTotal); - if (hits > 0) { - if (branchTotal == branchCoverage) { + int covered = paint.getCovered(line); + int missed = paint.getMissed(line); + int total = missed + covered; + + if (covered == 0) { + output.write("\n"); + } + else { + if (missed == 0) { output.write("\n"); } else { - output.write("\n"); + var formatter = new CoverageFormatter(); + output.write("\n"); } - } - else { - output.write("\n"); + } output.write("" + line + "\n"); - output.write("" + hits + "\n"); + output.write("" + covered + "\n"); } else { output.write("\n"); @@ -562,24 +558,6 @@ private void deleteFolder(final File folder, final FilteredLog log) { } } - private static class PaintedNode implements Serializable { - private static final long serialVersionUID = -6044649044983631852L; - - private final Node node; - private final CoveragePaint paint; - - PaintedNode(final Node node, final CoveragePaint paint) { - this.node = node; - this.paint = paint; - } - - public Node getNode() { - return node; - } - - public CoveragePaint getPaint() { - return paint; - } - } } + } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodePainter.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodePainter.java index 0f8a8c41a..18aac307c 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodePainter.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodePainter.java @@ -1,7 +1,7 @@ package io.jenkins.plugins.coverage.model.visualization.code; import java.io.IOException; -import java.util.Map.Entry; +import java.util.List; import java.util.Set; import java.util.stream.Collectors; @@ -13,7 +13,6 @@ import hudson.model.Run; import io.jenkins.plugins.coverage.model.visualization.code.SourceCodeFacade.AgentCoveragePainter; -import io.jenkins.plugins.coverage.targets.CoveragePaint; import io.jenkins.plugins.prism.PermittedSourceCodeDirectory; import io.jenkins.plugins.prism.PrismConfiguration; import io.jenkins.plugins.prism.SourceCodeRetention; @@ -42,7 +41,7 @@ public SourceCodePainter(@NonNull final Run build, @NonNull final FilePath /** * Processes the source code painting. * - * @param paintedFiles + * @param node * The files to be painted together with the information which lines has to be highlighted * @param sourceDirectories * the source directories that have been configured in the associated job @@ -56,15 +55,16 @@ public SourceCodePainter(@NonNull final Run build, @NonNull final FilePath * @throws InterruptedException * if the painting process has been interrupted */ - public void processSourceCodePainting(final Set> paintedFiles, + public void processSourceCodePainting(final Node node, final Set sourceDirectories, final String sourceCodeEncoding, final SourceCodeRetention sourceCodeRetention, final FilteredLog log) throws InterruptedException { SourceCodeFacade sourceCodeFacade = new SourceCodeFacade(); if (sourceCodeRetention != SourceCodeRetention.NEVER) { - log.logInfo("Painting %d source files on agent", paintedFiles.size()); + var files = node.getAllFileNodes().stream().map(PaintedNode::new).collect(Collectors.toList()); + log.logInfo("Painting %d source files on agent", files.size()); - paintFilesOnAgent(paintedFiles, sourceDirectories, sourceCodeEncoding, log); + paintFilesOnAgent(files, sourceDirectories, sourceCodeEncoding, log); log.logInfo("Copying painted sources from agent to build folder"); sourceCodeFacade.copySourcesToBuildFolder(build, workspace, log); @@ -72,7 +72,7 @@ public void processSourceCodePainting(final Set> pain sourceCodeRetention.cleanup(build, sourceCodeFacade.getCoverageSourcesDirectory(), log); } - private void paintFilesOnAgent(final Set> paintedFiles, + private void paintFilesOnAgent(final List paintedFiles, final Set requestedSourceDirectories, final String sourceCodeEncoding, final FilteredLog log) throws InterruptedException { try { diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/Messages.properties b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/Messages.properties index 95bf8cb03..e6a294cd7 100644 --- a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/Messages.properties +++ b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/Messages.properties @@ -27,4 +27,4 @@ Metric.LINE=Line Metric.MUTATION=Mutation Metric.BRANCH=Branch Metric.COMPLEXITY=Cyclomatic Complexity -Metric.LOC=Line of code +Metric.LOC=Lines of Code diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginSourceITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginSourceITest.java index 7f7a23266..b77787d65 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginSourceITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginSourceITest.java @@ -10,7 +10,6 @@ import org.junit.jupiter.api.Test; -import edu.hm.hafner.metric.Coverage; import edu.hm.hafner.metric.Coverage.CoverageBuilder; import edu.hm.hafner.metric.Metric; import edu.hm.hafner.metric.Node; @@ -18,17 +17,14 @@ import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; import org.jenkinsci.plugins.workflow.job.WorkflowJob; -import hudson.model.FreeStyleProject; import hudson.model.Run; import hudson.model.TopLevelItem; -import jenkins.model.ParameterizedJobMixIn.ParameterizedJob; -import io.jenkins.plugins.coverage.CoveragePublisher; -import io.jenkins.plugins.coverage.adapter.JacocoReportAdapter; import io.jenkins.plugins.prism.PermittedSourceCodeDirectory; import io.jenkins.plugins.prism.PrismConfiguration; -import io.jenkins.plugins.util.IntegrationTestWithJenkinsPerSuite; +import io.jenkins.plugins.prism.SourceCodeRetention; +import static io.jenkins.plugins.prism.SourceCodeRetention.*; import static org.assertj.core.api.Assertions.*; /** @@ -37,7 +33,7 @@ * @author Ullrich Hafner */ @SuppressWarnings({"checkstyle:ClassDataAbstractionCoupling", "checkstyle:ClassFanOutComplexity"}) -class CoveragePluginSourceITest extends IntegrationTestWithJenkinsPerSuite { +class CoveragePluginSourceITest extends AbstractCoverageITest { private static final String ACU_COBOL_PARSER = "public class AcuCobolParser extends LookaheadParser {"; private static final String NO_SOURCE_CODE = "n/a"; private static final String SOURCE_FILE_NAME = "AcuCobolParser.java"; @@ -46,7 +42,6 @@ class CoveragePluginSourceITest extends IntegrationTestWithJenkinsPerSuite { private static final String SOURCE_FILE_PATH = PACKAGE_PATH + SOURCE_FILE_NAME; private static final String ACU_COBOL_PARSER_COVERAGE_REPORT = "jacoco-acu-cobol-parser.xml"; private static final PathUtil PATH_UTIL = new PathUtil(); - static final String FILE_NAME = "jacoco-analysis-model.xml"; /** Verifies that the plugin reads source code from the workspace root. */ @Test @@ -94,8 +89,7 @@ void coveragePluginPipelineNotRegisteredSourceCodeDirectory() throws IOException WorkflowJob job = createPipelineWithWorkspaceFiles(ACU_COBOL_PARSER_COVERAGE_REPORT); copyFileToWorkspace(job, SOURCE_FILE, "ignore/" + PACKAGE_PATH + "AcuCobolParser.java"); - String sourceCodeRetention = "STORE_ALL_BUILD"; - job.setDefinition(createPipelineWithSourceCode(sourceCodeRetention, sourceDirectory)); + job.setDefinition(createPipelineWithSourceCode(EVERY_BUILD, sourceDirectory)); Run firstBuild = buildSuccessfully(job); @@ -127,8 +121,7 @@ private String createExternalSourceFolder() throws IOException { assertThat(temporaryDirectory.isDirectory()).isTrue(); File[] temporaryFiles = temporaryDirectory.listFiles(); - String sourceCodeRetention = "STORE_ALL_BUILD"; - job.setDefinition(createPipelineWithSourceCode(sourceCodeRetention, sourceDirectory)); + job.setDefinition(createPipelineWithSourceCode(EVERY_BUILD, sourceDirectory)); Run firstBuild = buildSuccessfully(job); assertThat(getConsoleLog(firstBuild)) .contains("-> finished painting successfully"); @@ -138,13 +131,13 @@ private String createExternalSourceFolder() throws IOException { verifySourceCodeInBuild(secondBuild, ACU_COBOL_PARSER); verifySourceCodeInBuild(firstBuild, ACU_COBOL_PARSER); // should be still available - job.setDefinition(createPipelineWithSourceCode("STORE_LAST_BUILD", sourceDirectory)); + job.setDefinition(createPipelineWithSourceCode(LAST_BUILD, sourceDirectory)); Run thirdBuild = buildSuccessfully(job); verifySourceCodeInBuild(thirdBuild, ACU_COBOL_PARSER); verifySourceCodeInBuild(firstBuild, NO_SOURCE_CODE); // should be still available verifySourceCodeInBuild(secondBuild, NO_SOURCE_CODE); // should be still available - job.setDefinition(createPipelineWithSourceCode("NEVER_STORE", sourceDirectory)); + job.setDefinition(createPipelineWithSourceCode(NEVER, sourceDirectory)); Run lastBuild = buildSuccessfully(job); verifySourceCodeInBuild(lastBuild, NO_SOURCE_CODE); verifySourceCodeInBuild(firstBuild, NO_SOURCE_CODE); // should be still available @@ -156,11 +149,11 @@ private String createExternalSourceFolder() throws IOException { return firstBuild; } - private CpsFlowDefinition createPipelineWithSourceCode(final String sourceCodeRetention, + private CpsFlowDefinition createPipelineWithSourceCode(final SourceCodeRetention sourceCodeRetention, final String sourceDirectory) { return new CpsFlowDefinition("node {" - + " publishCoverage adapters: [jacocoAdapter('" + ACU_COBOL_PARSER_COVERAGE_REPORT + "')], \n" - + " sourceFileResolver: sourceFiles('" + sourceCodeRetention + "'), \n" + + " recordCoverage tools: [[parser: 'JACOCO', pattern: '**/*xml']], \n" + + " sourceCodeRetention: '" + sourceCodeRetention.name() + "', \n" + " sourceCodeEncoding: 'UTF-8', \n" + " sourceDirectories: [[path: '" + sourceDirectory + "']]" + "}", true); @@ -184,29 +177,4 @@ private CoverageViewModel verifyViewModel(final Run build) { return action.getTarget(); } - - /** Freestyle job integration test for a simple build with code coverage. */ - @Test - void coveragePluginFreestyleHelloWorld() { - FreeStyleProject project = createFreeStyleProject(); - copyFilesToWorkspace(project, FILE_NAME); - - CoveragePublisher coveragePublisher = new CoveragePublisher(); - JacocoReportAdapter jacocoReportAdapter = new JacocoReportAdapter(FILE_NAME); - coveragePublisher.setAdapters(Collections.singletonList(jacocoReportAdapter)); - project.getPublishersList().add(coveragePublisher); - - verifySimpleCoverageNode(project); - } - - @SuppressWarnings("PMD.SystemPrintln") - void verifySimpleCoverageNode(final ParameterizedJob project) { - Run build = buildSuccessfully(project); - assertThat(build.getNumber()).isEqualTo(1); - - CoverageBuildAction coverageResult = build.getAction(CoverageBuildAction.class); - assertThat(coverageResult.getLineCoverage()) - .isEqualTo(new Coverage.CoverageBuilder().setCovered(6083).setMissed(6368 - 6083).build()); - System.out.println(getConsoleLog(build)); - } } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerCoveragePluginSourceITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerCoveragePluginSourceITest.java index 2dbf341ba..9561ddfba 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerCoveragePluginSourceITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerCoveragePluginSourceITest.java @@ -1,20 +1,24 @@ package io.jenkins.plugins.coverage.model; import java.io.IOException; -import java.util.Collections; import org.junit.jupiter.api.Test; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; +import edu.hm.hafner.metric.Coverage; +import edu.hm.hafner.metric.Metric; + import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; import org.jenkinsci.plugins.workflow.job.WorkflowJob; import hudson.model.FreeStyleProject; import hudson.model.Node; +import hudson.model.Run; +import jenkins.model.ParameterizedJobMixIn.ParameterizedJob; -import io.jenkins.plugins.coverage.CoveragePublisher; -import io.jenkins.plugins.coverage.adapter.JacocoReportAdapter; +import io.jenkins.plugins.coverage.metrics.CoverageTool.CoverageParser; +import static org.assertj.core.api.Assertions.*; import static org.assertj.core.api.Assumptions.*; /** @@ -33,14 +37,9 @@ void coverageFreeStyleOnAgent() throws IOException { assumeThat(isWindows()).as("Running on Windows").isFalse(); Node agent = createDockerAgent(AGENT_CONTAINER); - FreeStyleProject project = createFreeStyleProject(); + FreeStyleProject project = createFreestyleJob(CoverageParser.JACOCO, JACOCO_ANALYSIS_MODEL_FILE); project.setAssignedNode(agent); - - copySingleFileToAgentWorkspace(agent, project, FILE_NAME, FILE_NAME); - CoveragePublisher coveragePublisher = new CoveragePublisher(); - JacocoReportAdapter jacocoReportAdapter = new JacocoReportAdapter(FILE_NAME); - coveragePublisher.setAdapters(Collections.singletonList(jacocoReportAdapter)); - project.getPublishersList().add(coveragePublisher); + copySingleFileToAgentWorkspace(agent, project, JACOCO_ANALYSIS_MODEL_FILE, JACOCO_ANALYSIS_MODEL_FILE); verifySimpleCoverageNode(project); } @@ -53,11 +52,22 @@ void coveragePipelineOnAgentNode() { Node agent = createDockerAgent(AGENT_CONTAINER); WorkflowJob project = createPipelineOnAgent(); - copySingleFileToAgentWorkspace(agent, project, FILE_NAME, FILE_NAME); + copySingleFileToAgentWorkspace(agent, project, JACOCO_ANALYSIS_MODEL_FILE, JACOCO_ANALYSIS_MODEL_FILE); verifySimpleCoverageNode(project); } + @SuppressWarnings("PMD.SystemPrintln") + private void verifySimpleCoverageNode(final ParameterizedJob project) { + Run build = buildSuccessfully(project); + assertThat(build.getNumber()).isEqualTo(1); + + CoverageBuildAction coverageResult = build.getAction(CoverageBuildAction.class); + assertThat(coverageResult.getLineCoverage()) + .isEqualTo(new Coverage.CoverageBuilder().setMetric(Metric.LINE).setCovered(6083).setMissed(6368 - 6083).build()); + System.out.println(getConsoleLog(build)); + } + private WorkflowJob createPipelineOnAgent() { WorkflowJob job = createPipeline(); job.setDefinition(new CpsFlowDefinition("node('" + DOCKER_AGENT_NAME + "') {" @@ -67,7 +77,7 @@ private WorkflowJob createPipelineOnAgent() { + " userRemoteConfigs: [[url: '" + "https://github.com/jenkinsci/analysis-model.git" + "']],\n" + " extensions: [[$class: 'RelativeTargetDirectory', \n" + " relativeTargetDir: 'checkout']]])\n" - + " publishCoverage adapters: [jacocoAdapter('" + FILE_NAME + + " publishCoverage adapters: [jacocoAdapter('" + JACOCO_ANALYSIS_MODEL_FILE + "')], sourceFileResolver: sourceFiles('STORE_ALL_BUILD')\n" + "}" + "}", true)); From 296bd67a1ebc4604ecf94e3512bf1e74d3ea328f Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Mon, 24 Oct 2022 18:47:32 +0200 Subject: [PATCH 18/29] Simplify code. --- .../plugins/coverage/model/FileChangesProcessor.java | 8 ++++---- .../model/visualization/dashboard/CoverageColumn.java | 1 - .../model/visualization/dashboard/CoverageColumnType.java | 4 ---- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/FileChangesProcessor.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/FileChangesProcessor.java index 6ba686ef3..bacaa86a0 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/FileChangesProcessor.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/FileChangesProcessor.java @@ -134,12 +134,12 @@ public void attachIndirectCoveragesChanges(final Node root, final Node reference } /** - * Attaches the indirect coverage changes for a specific file, represented by the passed {@link FileCoverageNode}. + * Attaches the indirect coverage changes for a specific file, represented by the specified {@link FileNode}. * * @param fileNode - * The file coverage node which represents the processed file + * the file coverage node which represents the processed file * @param referenceCoverageMapping - * A mapping which contains the coverage per line of the reference file + * a mapping which contains the coverage per line of the reference file */ private void attachIndirectCoverageChangeForFile(final FileNode fileNode, final SortedMap referenceCoverageMapping) { @@ -170,7 +170,7 @@ private Optional> getReferenceCoveragePerLine( final Map references, final String fullyQualifiedName) { if (references.containsKey(fullyQualifiedName)) { NavigableMap coveragePerLine = references.get(fullyQualifiedName).getLineNumberToLineCoverage(); - if (coveragePerLine != null && !coveragePerLine.isEmpty()) { + if (!coveragePerLine.isEmpty()) { return Optional.of(coveragePerLine); } } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumn.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumn.java index 5e1d04181..7a2468f60 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumn.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumn.java @@ -211,7 +211,6 @@ private boolean hasCoverageAction(final Job job) { */ @Extension(optional = true) public static class CoverageDescriptor extends ListViewColumnDescriptor { - @NonNull @Override public String getDisplayName() { diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumnType.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumnType.java index 4f8adccc6..9af1e476b 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumnType.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumnType.java @@ -22,10 +22,6 @@ * @author Florian Orendi */ public abstract class CoverageColumnType { - - /** - * The localized display name. - */ private final Localizable displayName; /** From 799e7d8ad613297df7ec93cbf06e945d01fe3920 Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Tue, 25 Oct 2022 10:45:07 +0200 Subject: [PATCH 19/29] Create delta tree on the fly without removing nodes. --- plugin/pom.xml | 2 +- .../coverage/model/CoverageTreeCreator.java | 15 ++++----------- .../coverage/model/CoverageTreeCreatorTest.java | 8 ++++++++ 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/plugin/pom.xml b/plugin/pom.xml index 743d47969..2c16a7333 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -19,7 +19,7 @@ https://github.com/jenkinsci/code-coverage-api-plugin - 3.3.0 + 4.0.0 -SNAPSHOT jenkinsci/code-coverage-api-plugin diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTreeCreator.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTreeCreator.java index 72d4b0afb..23e6d3e76 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTreeCreator.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTreeCreator.java @@ -2,6 +2,7 @@ import java.util.Iterator; import java.util.Map; +import java.util.Optional; import java.util.SortedSet; import edu.hm.hafner.metric.Coverage; @@ -25,17 +26,9 @@ public class CoverageTreeCreator { * @return the filtered tree */ public Node createChangeCoverageTree(final Node coverageNode) { - Node copy = coverageNode.copyTree(); - - boolean treeExists = calculateChangeCoverageTree(copy); - if (treeExists) { - attachChangeCoverageLeaves(copy); - } - else { - clearChildrenAndLeaves(copy); - } - - return copy; + Optional copy = coverageNode.prune( + n -> n instanceof FileNode && ((FileNode)n).hasCoveredLinesInChangeSet()); + return copy.orElse(coverageNode.copy()); } /** diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTreeCreatorTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTreeCreatorTest.java index 1f6f2718b..b98ad16a4 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTreeCreatorTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTreeCreatorTest.java @@ -10,6 +10,7 @@ import edu.hm.hafner.metric.Coverage.CoverageBuilder; import edu.hm.hafner.metric.FileNode; +import edu.hm.hafner.metric.MethodNode; import edu.hm.hafner.metric.Metric; import edu.hm.hafner.metric.Node; @@ -165,14 +166,20 @@ private FileNode attachFileCoverageNodeToTree(final Node root) { * The node to which coverage information should be added */ private void attachCoveragePerLine(final FileNode file) { + var method = new MethodNode("aMethod", "{}"); var builder = new CoverageBuilder().setMetric(Metric.LINE); file.addLineCoverage(10, builder.setCovered(1).setMissed(0).build()); file.addLineCoverage(11, builder.setCovered(0).setMissed(1).build()); file.addLineCoverage(12, builder.setCovered(1).setMissed(0).build()); file.addLineCoverage(13, builder.setCovered(0).setMissed(1).build()); + method.addValue(builder.setCovered(2).setMissed(2).build()); + builder.setMetric(Metric.BRANCH); file.addBranchCoverage(11, builder.setCovered(0).setMissed(4).build()); file.addBranchCoverage(12, builder.setCovered(4).setMissed(0).build()); + method.addValue(builder.setCovered(4).setMissed(4).build()); + + file.addChild(method); } /** @@ -203,5 +210,6 @@ private void attachCodeChanges(final FileNode file) { changes.add(12); changes.add(13); file.getChangedLines().addAll(changes); + } } From 8c20115f99340b1b487ba127275bbd879da901ee Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Tue, 1 Nov 2022 23:04:56 +0100 Subject: [PATCH 20/29] Improve storage of coverages per line. --- .../coverage/model/CoverageTreeCreator.java | 37 +++++++++++++-- .../coverage/model/FileChangesProcessor.java | 45 +++++++++--------- .../model/visualization/code/PaintedNode.java | 46 +++++++++++-------- .../coverage/model/CoveragePluginITest.java | 5 +- .../model/CoverageTreeCreatorTest.java | 13 +++--- .../model/testutil/CoverageStubs.java | 4 +- .../code/SourceCodeFacadeTest.java | 2 +- 7 files changed, 94 insertions(+), 58 deletions(-) diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTreeCreator.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTreeCreator.java index 23e6d3e76..50b132221 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTreeCreator.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTreeCreator.java @@ -4,6 +4,7 @@ import java.util.Map; import java.util.Optional; import java.util.SortedSet; +import java.util.stream.Collectors; import edu.hm.hafner.metric.Coverage; import edu.hm.hafner.metric.Coverage.CoverageBuilder; @@ -26,9 +27,30 @@ public class CoverageTreeCreator { * @return the filtered tree */ public Node createChangeCoverageTree(final Node coverageNode) { - Optional copy = coverageNode.prune( - n -> n instanceof FileNode && ((FileNode)n).hasCoveredLinesInChangeSet()); - return copy.orElse(coverageNode.copy()); + return prune(coverageNode).orElse(coverageNode.copyNode()); + } + + private Optional prune(final Node original) { + if (original instanceof FileNode) { + var file = (FileNode) original; + if (file.hasCoveredLinesInChangeSet()) { + return Optional.of(file.filter()); + } + return Optional.empty(); + } + else { + var copy = original.copy(); + var children = original.getChildren() + .stream() + .map(this::prune) + .flatMap(Optional::stream) + .collect(Collectors.toList()); + if (children.isEmpty()) { + return Optional.empty(); + } + copy.addAllChildren(children); + return Optional.of(copy); + } } /** @@ -145,6 +167,15 @@ private void attachIndirectCoverageChangesLeaves(final Node node) { * The {@link Coverage} to be represented by the leaves */ private void createChangeCoverageLeaves(final FileNode fileNode, final SortedSet changedLines) { + var lineCoverage = Coverage.nullObject(Metric.LINE); + var branchCoverage = Coverage.nullObject(Metric.BRANCH); + for (int changedLine : changedLines) { + if (fileNode.hasCoverageForLine(changedLine)) { + var covered = fileNode.getCoveredOfLine(changedLine); + var missed = fileNode.getMissedOfLine(changedLine); + + } + } fileNode.addValue(changedLines.stream() .map(fileNode::getLineCoverage) .reduce(Coverage::add) diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/FileChangesProcessor.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/FileChangesProcessor.java index bacaa86a0..b9e722827 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/FileChangesProcessor.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/FileChangesProcessor.java @@ -15,7 +15,6 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; -import edu.hm.hafner.metric.Coverage; import edu.hm.hafner.metric.FileNode; import edu.hm.hafner.metric.Node; @@ -119,14 +118,13 @@ public void attachIndirectCoveragesChanges(final Node root, final Node reference for (Map.Entry entry : fileNodes.entrySet()) { String referencePath = entry.getKey(); FileNode fileNode = entry.getValue(); - Optional> referenceCoveragePerLine = + Optional> referenceCoveragePerLine = getReferenceCoveragePerLine(referenceFileNodes, referencePath); if (referenceCoveragePerLine.isPresent()) { - SortedMap referenceCoverageMapping = new TreeMap<>(referenceCoveragePerLine.get()); + SortedMap referenceCoverageMapping = new TreeMap<>(referenceCoveragePerLine.get()); String currentPath = fileNode.getPath(); if (codeChanges.containsKey(currentPath)) { - adjustedCoveragePerLine(referenceCoverageMapping, - codeChanges.get(currentPath)); + adjustedCoveragePerLine(referenceCoverageMapping, codeChanges.get(currentPath)); } attachIndirectCoverageChangeForFile(fileNode, referenceCoverageMapping); } @@ -142,12 +140,11 @@ public void attachIndirectCoveragesChanges(final Node root, final Node reference * a mapping which contains the coverage per line of the reference file */ private void attachIndirectCoverageChangeForFile(final FileNode fileNode, - final SortedMap referenceCoverageMapping) { - fileNode.getLineNumberToLineCoverage().forEach((line, coverage) -> { - if (!fileNode.getChangedLines().contains(line) && referenceCoverageMapping.containsKey(line)) { - Coverage referenceCoverage = referenceCoverageMapping.get(line); - int covered = coverage.getCovered(); - int referenceCovered = referenceCoverage.getCovered(); + final SortedMap referenceCoverageMapping) { + fileNode.getCoveredLines().forEach(line -> { + if (!fileNode.hasChangedLine(line) && referenceCoverageMapping.containsKey(line)) { + int referenceCovered = referenceCoverageMapping.get(line); + int covered = fileNode.getCoveredOfLine(line); if (covered != referenceCovered) { fileNode.addIndirectCoverageChange(line, covered - referenceCovered); } @@ -166,10 +163,10 @@ private void attachIndirectCoverageChangeForFile(final FileNode fileNode, * * @return an Optional of the coverage mapping if existent, else an empty Optional */ - private Optional> getReferenceCoveragePerLine( + private Optional> getReferenceCoveragePerLine( final Map references, final String fullyQualifiedName) { if (references.containsKey(fullyQualifiedName)) { - NavigableMap coveragePerLine = references.get(fullyQualifiedName).getLineNumberToLineCoverage(); + NavigableMap coveragePerLine = references.get(fullyQualifiedName).getCounters(); if (!coveragePerLine.isEmpty()) { return Optional.of(coveragePerLine); } @@ -186,9 +183,9 @@ private Optional> getReferenceCoveragePerLine( * @param fileChanges * The applied code changes of the file */ - private void adjustedCoveragePerLine(final SortedMap coveragePerLine, + private void adjustedCoveragePerLine(final SortedMap coveragePerLine, final FileChanges fileChanges) { - List> coverages = transformCoveragePerLine(coveragePerLine, fileChanges); + List> coverages = transformCoveragePerLine(coveragePerLine, fileChanges); fileChanges.getChangesByType(ChangeEditType.DELETE).forEach(change -> { for (int i = change.getChangedFromLine(); i <= change.getChangedToLine(); i++) { @@ -197,13 +194,13 @@ private void adjustedCoveragePerLine(final SortedMap coverage }); fileChanges.getChangesByType(ChangeEditType.INSERT).forEach(change -> { - List inserted = coverages.get(change.getChangedFromLine()); + List inserted = coverages.get(change.getChangedFromLine()); int changedLinesNumber = change.getToLine() - change.getFromLine() + 1; fillCoverageListWithNull(inserted, changedLinesNumber); }); fileChanges.getChangesByType(ChangeEditType.REPLACE).forEach(change -> { - List replaced = coverages.get(change.getChangedFromLine()); + List replaced = coverages.get(change.getChangedFromLine()); replaced.clear(); // coverage of replaced code is irrelevant int changedLinesNumber = change.getToLine() - change.getFromLine() + 1; fillCoverageListWithNull(replaced, changedLinesNumber); @@ -212,13 +209,13 @@ private void adjustedCoveragePerLine(final SortedMap coverage } }); - List adjustedCoveragesList = coverages.stream() + List adjustedCoveragesList = coverages.stream() .flatMap(Collection::stream) .collect(Collectors.toList()); coveragePerLine.clear(); for (int line = 1; line < adjustedCoveragesList.size(); line++) { - Coverage coverage = adjustedCoveragesList.get(line); + Integer coverage = adjustedCoveragesList.get(line); if (coverage != null) { coveragePerLine.put(line, coverage); } @@ -236,9 +233,9 @@ private void adjustedCoveragePerLine(final SortedMap coverage * * @return the list expandable list representation of the coverage-per-line mapping */ - private List> transformCoveragePerLine( - final SortedMap coveragePerLine, final FileChanges fileChanges) { - List> coverages = coveragePerLine.values().stream() + private List> transformCoveragePerLine( + final SortedMap coveragePerLine, final FileChanges fileChanges) { + List> coverages = coveragePerLine.values().stream() .map(coverage -> new ArrayList<>(Collections.singletonList(coverage))) .collect(Collectors.toList()); @@ -286,7 +283,7 @@ private Map getFileNodeMappingWithReferencePaths( } /** - * Gets all {@link FileCoverageNode file nodes} from a reference coverage tree which also exist in the current + * Gets all {@link FileNode file nodes} from a reference coverage tree which also exist in the current * coverage tree. The found nodes are mapped by their path. * * @param nodeMapping @@ -311,7 +308,7 @@ private Map getReferenceFileNodeMapping( * @param number * The number of values to be inserted */ - private void fillCoverageListWithNull(final List coverageList, final int number) { + private void fillCoverageListWithNull(final List coverageList, final int number) { for (int i = 0; i < number; i++) { coverageList.add(null); } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/code/PaintedNode.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/code/PaintedNode.java index 5437ecf59..d4a9a0ac7 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/code/PaintedNode.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/code/PaintedNode.java @@ -1,9 +1,7 @@ package io.jenkins.plugins.coverage.model.visualization.code; import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; -import java.util.SortedSet; +import java.util.Arrays; import edu.hm.hafner.metric.FileNode; @@ -15,23 +13,15 @@ class PaintedNode implements Serializable { private static final long serialVersionUID = -6044649044983631852L; private final String path; - private final SortedSet coveredLines; - private final Map coveredPerLine; - private final Map missedPerLine; + private final int[] linesToPaint; + private final int[] coveredPerLine; + private final int[] missedPerLine; public PaintedNode(final FileNode file) { path = file.getPath(); - coveredLines = file.getCoveredLines(); - coveredPerLine = new HashMap<>(); - missedPerLine = new HashMap<>(); - for (Integer line : coveredLines) { - var coverage = file.getBranchCoverage(line); - if (!coverage.isSet()) { - coverage = file.getLineCoverage(line); - } - coveredPerLine.put(line, coverage.getCovered()); - missedPerLine.put(line, coverage.getMissed()); - } + linesToPaint = file.getCoveredLines().stream().mapToInt(i -> i).toArray(); + coveredPerLine = file.getCoveredCounters(); + missedPerLine = file.getMissedCounters(); } public String getPath() { @@ -39,14 +29,30 @@ public String getPath() { } public boolean isPainted(final int line) { - return coveredLines.contains(line); + var index = findLine(line); + if (index >= 0) { + return coveredPerLine[index] > 0; + } + return false; + } + + private int findLine(final int line) { + return Arrays.binarySearch(linesToPaint, line); } public int getCovered(final int line) { - return coveredPerLine.getOrDefault(line, 0); + return getCounter(line, coveredPerLine); } public int getMissed(final int line) { - return missedPerLine.getOrDefault(line, 0); + return getCounter(line, missedPerLine); + } + + private int getCounter(final int line, final int[] counters) { + var index = findLine(line); + if (index >= 0) { + return counters[index]; + } + return 0; } } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java index f100bbc08..ba725eac5 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java @@ -13,6 +13,7 @@ import edu.hm.hafner.metric.Coverage.CoverageBuilder; import edu.hm.hafner.metric.Metric; import edu.hm.hafner.metric.MutationValue; +import edu.hm.hafner.metric.Node; import org.jenkinsci.plugins.workflow.job.WorkflowJob; import hudson.model.FreeStyleProject; @@ -160,7 +161,9 @@ private void verifyOneJacocoResult(final ParameterizedJob project) { CoverageBuildAction coverageResult = build.getAction(CoverageBuildAction.class); assertThat(coverageResult.getMetricsForSummary()) .containsExactly(Metric.LINE, Metric.BRANCH, Metric.COMPLEXITY, Metric.LOC); - + for (Node node : coverageResult.getResult().find(Metric.CLASS, "edu/hm/hafner/analysis/parser/pvsstudio/AnalyzerType").get().getAll(Metric.METHOD)) { + System.out.format("%s: %s%n", node, node.getValue(Metric.LINE)); + } assertThat(coverageResult.getLineCoverage()) .isEqualTo(createLineCoverageBuilder() .setCovered(JACOCO_COVERED_LINES) diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTreeCreatorTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTreeCreatorTest.java index b98ad16a4..32a6b47a2 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTreeCreatorTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTreeCreatorTest.java @@ -22,7 +22,6 @@ * @author Florian Orendi */ class CoverageTreeCreatorTest extends AbstractCoverageTest { - @Test void shouldCreateEmptyChangeCoverageTreeWithoutChanges() { CoverageTreeCreator coverageTreeCreator = createCoverageTreeCreator(); @@ -168,15 +167,15 @@ private FileNode attachFileCoverageNodeToTree(final Node root) { private void attachCoveragePerLine(final FileNode file) { var method = new MethodNode("aMethod", "{}"); var builder = new CoverageBuilder().setMetric(Metric.LINE); - file.addLineCoverage(10, builder.setCovered(1).setMissed(0).build()); - file.addLineCoverage(11, builder.setCovered(0).setMissed(1).build()); - file.addLineCoverage(12, builder.setCovered(1).setMissed(0).build()); - file.addLineCoverage(13, builder.setCovered(0).setMissed(1).build()); + file.addCounters(10, 1, 0); + file.addCounters(11, 0, 1); + file.addCounters(12, 1, 0); + file.addCounters(13, 0, 1); method.addValue(builder.setCovered(2).setMissed(2).build()); builder.setMetric(Metric.BRANCH); - file.addBranchCoverage(11, builder.setCovered(0).setMissed(4).build()); - file.addBranchCoverage(12, builder.setCovered(4).setMissed(0).build()); + file.addCounters(11, 0, 4); + file.addCounters(12, 4, 0); method.addValue(builder.setCovered(4).setMissed(4).build()); file.addChild(method); diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/testutil/CoverageStubs.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/testutil/CoverageStubs.java index 708058a27..31efc3db3 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/testutil/CoverageStubs.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/testutil/CoverageStubs.java @@ -159,7 +159,7 @@ public static Node createChangeCoverageNode(final Fraction changeCoverage, final var fileNode = new FileNode("File-" + file); for (int line = 0; line < 2; line++) { - fileNode.addLineCoverage(10 + line, builder.setCovered(1).setMissed(1).build()); + fileNode.addCounters(10 + line, 1, 1); fileNode.addChangedCodeLine(10 + line); } root.addChild(fileNode); @@ -191,7 +191,7 @@ public static Node createIndirectCoverageChangesNode(final Fraction coverageChan var fileNode = new FileNode("File-" + file); for (int line = 0; line < 2; line++) { - fileNode.addLineCoverage(10 + line, builder.setCovered(1).setMissed(1).build()); + fileNode.addCounters(10 + line, 1, 1); fileNode.addIndirectCoverageChange(10 + line, 2); } root.addChild(fileNode); diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodeFacadeTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodeFacadeTest.java index 9abb1b05e..0a2e62aa8 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodeFacadeTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodeFacadeTest.java @@ -121,7 +121,7 @@ private FileNode createFileCoverageNode() { file.addIndirectCoverageChange(14, 1); file.addIndirectCoverageChange(15, 1); for (int i = 1; i <= 25; i++) { - file.addLineCoverage(i, COVERAGE); + file.addCounters(i, 1, 0); } return file; } From 4aae7da279738eedcd7c713c4a1448eff1c0324d Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Wed, 2 Nov 2022 11:12:59 +0100 Subject: [PATCH 21/29] Rework coverage trees. --- .../coverage/model/CoverageBuildAction.java | 3 +- .../model/CoverageBuildActionTest.java | 32 +++++++++++++++++++ .../model/testutil/CoverageStubs.java | 31 ------------------ 3 files changed, 34 insertions(+), 32 deletions(-) diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageBuildAction.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageBuildAction.java index ade70050d..c0a3f67cc 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageBuildAction.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageBuildAction.java @@ -465,7 +465,8 @@ public long getLineAmountWithChangedCoverage() { private Set extractFileNodesWithChangeCoverage() { CoverageTreeCreator treeCreator = new CoverageTreeCreator(); - return treeCreator.createChangeCoverageTree(getResult()).getAllFileNodes().stream() + var allFileNodes = treeCreator.createChangeCoverageTree(getResult()).getAllFileNodes(); + return allFileNodes.stream() .filter(FileNode::hasCoveredLinesInChangeSet) .collect(Collectors.toSet()); } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageBuildActionTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageBuildActionTest.java index 5b93b3e96..da83009a9 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageBuildActionTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageBuildActionTest.java @@ -10,6 +10,7 @@ import edu.hm.hafner.metric.Coverage; import edu.hm.hafner.metric.Coverage.CoverageBuilder; +import edu.hm.hafner.metric.FileNode; import edu.hm.hafner.metric.Metric; import edu.hm.hafner.metric.ModuleNode; import edu.hm.hafner.metric.Node; @@ -236,6 +237,37 @@ private CoverageBuildAction createChangeCoverageBuildAction() { return createCoverageBuildAction(root); } + /** + * Creates a stub of {@link Node}, which represents the change coverage and provides information about it. + * + * @param changeCoverage + * The change coverage + * @param metric + * The coverage metric + * @param coverageFileChange + * The amount of files which contain indirect coverage changes + * @param coverageLineChanges + * The amount of lines which contain indirect coverage changes + * + * @return the created stub + */ + private Node createChangeCoverageNode(final Fraction changeCoverage, final Metric metric, + final int coverageFileChange, final long coverageLineChanges) { + var root = new ModuleNode("root"); + var builder = new CoverageBuilder().setMetric(Metric.LINE); + for (int file = 0; file < 5; file++) { + var fileNode = new FileNode("File-" + file); + + for (int line = 0; line < 2; line++) { + fileNode.addCounters(10 + line, 1, 1); + fileNode.addChangedCodeLine(10 + line); + } + root.addChild(fileNode); + } + + return root; + } + /** * Creates a {@link CoverageBuildAction} which represents the indirect coverage changes for the metric {@link * #COVERAGE_METRIC} with the value {@link #COVERAGE_PERCENTAGE}. diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/testutil/CoverageStubs.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/testutil/CoverageStubs.java index 31efc3db3..1d7dbf465 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/testutil/CoverageStubs.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/testutil/CoverageStubs.java @@ -136,37 +136,6 @@ public static FileNode createCoverageNode(final Fraction coverageFraction, final return coverageNode; } - /** - * Creates a stub of {@link Node}, which represents the change coverage and provides information about it. - * - * @param changeCoverage - * The change coverage - * @param metric - * The coverage metric - * @param coverageFileChange - * The amount of files which contain indirect coverage changes - * @param coverageLineChanges - * The amount of lines which contain indirect coverage changes - * - * @return the created stub - */ - @VisibleForTesting - public static Node createChangeCoverageNode(final Fraction changeCoverage, final Metric metric, - final int coverageFileChange, final long coverageLineChanges) { - var root = new ModuleNode("root"); - var builder = new CoverageBuilder().setMetric(Metric.LINE); - for (int file = 0; file < 5; file++) { - var fileNode = new FileNode("File-" + file); - - for (int line = 0; line < 2; line++) { - fileNode.addCounters(10 + line, 1, 1); - fileNode.addChangedCodeLine(10 + line); - } - root.addChild(fileNode); - } - - return root; - } /** * Creates a stub of {@link Node}, which represents indirect coverage changes and provides information about it. From 8a37843d8b1c314f2b2420a59fd390e292232587 Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Wed, 2 Nov 2022 12:13:14 +0100 Subject: [PATCH 22/29] Store values on all levels if available. --- .../coverage/model/AbstractCoverageITest.java | 9 ++++ .../coverage/model/CoveragePluginITest.java | 51 +++++-------------- 2 files changed, 22 insertions(+), 38 deletions(-) diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageITest.java index b39015b8e..58c4cc6ac 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageITest.java @@ -18,7 +18,16 @@ */ public abstract class AbstractCoverageITest extends IntegrationTestWithJenkinsPerSuite { static final String JACOCO_ANALYSIS_MODEL_FILE = "jacoco-analysis-model.xml"; + static final int JACOCO_ANALYSIS_MODEL_COVERED = 5531; + static final int JACOCO_ANALYSIS_MODEL_MISSED = 267; + static final int JACOCO_ANALYSIS_MODEL_TOTAL + = JACOCO_ANALYSIS_MODEL_COVERED + JACOCO_ANALYSIS_MODEL_MISSED; + static final String JACOCO_CODING_STYLE_FILE = "jacoco-codingstyle.xml"; + static final int JACOCO_CODING_STYLE_COVERED = 294; + static final int JACOCO_CODING_STYLE_MISSED = 29; + static final int JACOCO_CODING_STYLE_TOTAL + = JACOCO_CODING_STYLE_COVERED + JACOCO_CODING_STYLE_MISSED; protected FreeStyleProject createFreestyleJob(final CoverageParser parser, final String... fileNames) { FreeStyleProject project = createFreeStyleProjectWithWorkspaceFiles(fileNames); diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java index ba725eac5..14ea6e8f6 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java @@ -13,7 +13,6 @@ import edu.hm.hafner.metric.Coverage.CoverageBuilder; import edu.hm.hafner.metric.Metric; import edu.hm.hafner.metric.MutationValue; -import edu.hm.hafner.metric.Node; import org.jenkinsci.plugins.workflow.job.WorkflowJob; import hudson.model.FreeStyleProject; @@ -34,23 +33,6 @@ * Integration test for different jacoco and cobertura files. */ class CoveragePluginITest extends AbstractCoverageITest { - - /** - * Covered lines in {@value JACOCO_ANALYSIS_MODEL_FILE}. - */ - private static final int JACOCO_COVERED_LINES = 6083; - /** - * All lines in {@value JACOCO_ANALYSIS_MODEL_FILE}. - */ - private static final int JACOCO_ALL_LINES = 6368; - /** - * Covered lines in {@value JACOCO_ANALYSIS_MODEL_FILE} and {@value JACOCO_CODINGSTYLE_FILE}. - */ - private static final int BOTH_JACOCO_COVERED_LINES = 6377; - /** - * All lines in {@value JACOCO_ANALYSIS_MODEL_FILE} and {@value JACOCO_CODINGSTYLE_FILE}. - */ - private static final int BOTH_JACOCO_ALL_LINES = 6691; /** * Covered lines in {@value COBERTURA_HIGHER_COVERAGE_FILE}. */ @@ -130,14 +112,8 @@ private void verifyNoFilesFound(final ParameterizedJob project) { @Test void shouldRecordOneJacocoResultInFreestyleJob() { - FreeStyleProject project = createFreestyleJob(CoverageParser.JACOCO, - JACOCO_ANALYSIS_MODEL_FILE); + FreeStyleProject project = createFreestyleJob(CoverageParser.JACOCO, JACOCO_ANALYSIS_MODEL_FILE); - // FIXME: which parser is correct? - /* - Expected :LINE: 95.52% (6083/6368) - Actual :LINE: 95.41% (5588/5857) - */ verifyOneJacocoResult(project); } @@ -161,13 +137,10 @@ private void verifyOneJacocoResult(final ParameterizedJob project) { CoverageBuildAction coverageResult = build.getAction(CoverageBuildAction.class); assertThat(coverageResult.getMetricsForSummary()) .containsExactly(Metric.LINE, Metric.BRANCH, Metric.COMPLEXITY, Metric.LOC); - for (Node node : coverageResult.getResult().find(Metric.CLASS, "edu/hm/hafner/analysis/parser/pvsstudio/AnalyzerType").get().getAll(Metric.METHOD)) { - System.out.format("%s: %s%n", node, node.getValue(Metric.LINE)); - } assertThat(coverageResult.getLineCoverage()) .isEqualTo(createLineCoverageBuilder() - .setCovered(JACOCO_COVERED_LINES) - .setMissed(JACOCO_ALL_LINES - JACOCO_COVERED_LINES) + .setCovered(JACOCO_ANALYSIS_MODEL_COVERED) + .setMissed(JACOCO_ANALYSIS_MODEL_TOTAL - JACOCO_ANALYSIS_MODEL_COVERED) .build()); } @@ -175,12 +148,6 @@ private void verifyOneJacocoResult(final ParameterizedJob project) { void shouldRecordTwoJacocoResultsInFreestyleJob() { FreeStyleProject project = createFreestyleJob(CoverageParser.JACOCO, JACOCO_ANALYSIS_MODEL_FILE, JACOCO_CODING_STYLE_FILE); - - // FIXME: which parser is correct? - /* - Expected :LINE: 95.31% (6377/6691) - Actual :LINE: 95.18% (5882/6180) - */ verifyTwoJacocoResults(project); } @@ -192,14 +159,22 @@ void shouldRecordTwoJacocoResultsInPipeline() { verifyTwoJacocoResults(job); } + @Test + void shouldRecordTwoJacocoResultsInDeclarativePipeline() { + WorkflowJob job = createDeclarativePipeline(CoverageParser.JACOCO, + JACOCO_ANALYSIS_MODEL_FILE, JACOCO_CODING_STYLE_FILE); + + verifyTwoJacocoResults(job); + } + private void verifyTwoJacocoResults(final ParameterizedJob project) { Run build = buildSuccessfully(project); CoverageBuildAction coverageResult = build.getAction(CoverageBuildAction.class); assertThat(coverageResult.getLineCoverage()) .isEqualTo(createLineCoverageBuilder() - .setCovered(BOTH_JACOCO_COVERED_LINES) - .setMissed(BOTH_JACOCO_ALL_LINES - BOTH_JACOCO_COVERED_LINES) + .setCovered(JACOCO_ANALYSIS_MODEL_COVERED + JACOCO_CODING_STYLE_COVERED) + .setMissed(JACOCO_ANALYSIS_MODEL_MISSED + JACOCO_CODING_STYLE_MISSED) .build()); } From 7f3115fa0a5429a0c0e9cfc23a67848d5488e407 Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Fri, 4 Nov 2022 14:37:10 +0100 Subject: [PATCH 23/29] Fix splitting of packages with values in the package nodes. --- .../coverage/model/CoverageTreeCreator.java | 2 +- .../coverage/model/AbstractCoverageTest.java | 4 ++- .../coverage/model/CoverageViewModelTest.java | 32 +++++++++++-------- .../coverage/model/DeltaComputationITest.java | 16 +++++----- .../tree => }/TreeMapNodeConverterTest.java | 19 +++++------ .../model/jacoco-acu-cobol-parser.xml | 22 ++++++------- 6 files changed, 50 insertions(+), 45 deletions(-) rename plugin/src/test/java/io/jenkins/plugins/coverage/model/{visualization/tree => }/TreeMapNodeConverterTest.java (81%) diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTreeCreator.java b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTreeCreator.java index 50b132221..20d4471bb 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTreeCreator.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTreeCreator.java @@ -27,7 +27,7 @@ public class CoverageTreeCreator { * @return the filtered tree */ public Node createChangeCoverageTree(final Node coverageNode) { - return prune(coverageNode).orElse(coverageNode.copyNode()); + return prune(coverageNode).orElse(coverageNode.copy()); } private Optional prune(final Node original) { diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageTest.java index 2de0cb227..d5b7a4b56 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageTest.java @@ -37,7 +37,9 @@ public abstract class AbstractCoverageTest extends ResourceTest { */ public Node readJacocoResult(final String fileName) { try { - return new JacocoParser().parse(Files.newBufferedReader(getResourceAsFile(fileName))); + var node = new JacocoParser().parse(Files.newBufferedReader(getResourceAsFile(fileName))); + node.splitPackages(); + return node; } catch (ParsingException | IOException exception) { throw new AssertionError(exception); diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageViewModelTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageViewModelTest.java index 0da2fcb71..00788abf7 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageViewModelTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageViewModelTest.java @@ -1,5 +1,6 @@ package io.jenkins.plugins.coverage.model; +import java.util.List; import java.util.NoSuchElementException; import org.apache.commons.lang3.math.Fraction; @@ -9,8 +10,6 @@ import hudson.model.Run; -import io.vavr.collection.List; - import static io.jenkins.plugins.coverage.model.Assertions.*; import static io.jenkins.plugins.coverage.model.CoverageViewModel.*; import static io.jenkins.plugins.coverage.model.testutil.CoverageStubs.*; @@ -27,7 +26,7 @@ class CoverageViewModelTest extends AbstractCoverageTest { @Test void shouldReturnEmptySourceViewForExistingLinkButMissingSourceFile() { - CoverageViewModel model = createModel(); + CoverageViewModel model = createModelFromCodingStyleReport(); String hash = String.valueOf("PathUtil.java".hashCode()); assertThat(model.getSourceCode(hash, ABSOLUTE_COVERAGE_TABLE_ID)).isEqualTo("n/a"); @@ -37,28 +36,31 @@ void shouldReturnEmptySourceViewForExistingLinkButMissingSourceFile() { @Test void shouldReportOverview() { - CoverageViewModel model = createModel(); - - assertThat(model.getDisplayName()).contains("'Java coding style'"); + CoverageViewModel model = createModelFromCodingStyleReport(); CoverageOverview overview = model.getOverview(); - var expectedMetrics = new String[] {"File", "Class", "Method", "Line", "Instruction", "Branch"}; + var expectedMetrics = new String[] {"Package", "File", "Class", "Method", "Line", "Instruction", "Branch"}; assertThat(overview.getMetrics()).containsExactly(expectedMetrics); - var expectedCovered = List.ofAll(7, 15, 97, 294, 1260, 109); + var expectedCovered = List.of(4, 7, 15, 97, 294, 1260, 109); assertThat(overview.getCovered()).containsExactlyElementsOf(expectedCovered); - assertThat(overview.getCoveredPercentages()).allSatisfy(d -> assertThat(d).isStrictlyBetween(0.0, 1.0)); + ensureValidPercentages(overview.getCoveredPercentages()); - var expectedMissed = List.ofAll(3, 3, 5, 29, 90, 7); + var expectedMissed = List.of(0, 3, 3, 5, 29, 90, 7); assertThat(overview.getMissed()).containsExactlyElementsOf(expectedMissed); - assertThat(overview.getMissedPercentages()).allSatisfy(d -> assertThat(d).isStrictlyBetween(0.0, 1.0)); + ensureValidPercentages(overview.getMissedPercentages()); assertThatJson(overview).node("metrics").isArray().containsExactly(expectedMetrics); assertThatJson(overview).node("covered").isArray().containsExactlyElementsOf(expectedCovered); assertThatJson(overview).node("missed").isArray().containsExactlyElementsOf(expectedMissed); } + private static void ensureValidPercentages(final List percentages) { + assertThat(percentages).allSatisfy(d -> + assertThat(d).isLessThanOrEqualTo(1.0).isGreaterThanOrEqualTo(0.0)); + } + @Test void shouldProvideIndirectCoverageChanges() { Node node = createIndirectCoverageChangesNode(Fraction.ZERO, LINE, 1, 1); @@ -70,7 +72,7 @@ void shouldProvideIndirectCoverageChanges() { @Test void shouldProvideRightTableModelById() { - CoverageViewModel model = createModel(); + CoverageViewModel model = createModelFromCodingStyleReport(); assertThat(model.getTableModel(CHANGE_COVERAGE_TABLE_ID)).isInstanceOf(ChangeCoverageTableModel.class); assertThat(model.getTableModel(INDIRECT_COVERAGE_TABLE_ID)).isInstanceOf(IndirectCoverageChangesTable.class); assertThat(model.getTableModel(ABSOLUTE_COVERAGE_TABLE_ID)).isInstanceOf(CoverageTableModel.class); @@ -79,8 +81,10 @@ void shouldProvideRightTableModelById() { .isThrownBy(() -> model.getTableModel("wrong-id")); } - private CoverageViewModel createModel() { - return new CoverageViewModel(mock(Run.class), readJacocoResult("jacoco-codingstyle.xml")); + private CoverageViewModel createModelFromCodingStyleReport() { + var model = new CoverageViewModel(mock(Run.class), readJacocoResult("jacoco-codingstyle.xml")); + assertThat(model.getDisplayName()).contains("'Java coding style'"); + return model; } } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeltaComputationITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeltaComputationITest.java index eeca11d42..5e65ebcd8 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeltaComputationITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeltaComputationITest.java @@ -61,14 +61,14 @@ private static void verifyFirstBuild(final Run firstBuild) { var action = firstBuild.getAction(CoverageBuildAction.class); var lineCoverage = action.getLineCoverage(); - assertThat(lineCoverage.getCovered()).isEqualTo(5882); // 294 + 5531 ? + assertThat(lineCoverage.getCovered()).isEqualTo(JACOCO_ANALYSIS_MODEL_COVERED + JACOCO_CODING_STYLE_COVERED); assertThat(lineCoverage.getCoveredPercentage().doubleValue()).isCloseTo(0.95, withPercentage(1.0)); var branchCoverage = action.getBranchCoverage(); assertThat(branchCoverage.getCovered()).isEqualTo(1544 + 109); assertThat(branchCoverage.getCoveredPercentage().doubleValue()).isCloseTo(0.88, withPercentage(1.0)); - assertThat(action.getCoverage(LOC)).isEqualTo(new LinesOfCode(lineCoverage.getTotal())); + assertThat(action.getCoverage(LOC)).isEqualTo(new LinesOfCode(JACOCO_ANALYSIS_MODEL_TOTAL + JACOCO_CODING_STYLE_TOTAL)); assertThat(action.getCoverage(COMPLEXITY)).isEqualTo(new CyclomaticComplexity(2718)); } @@ -76,14 +76,14 @@ private static void verifySecondBuild(final Run secondBuild) { var action = secondBuild.getAction(CoverageBuildAction.class); var lineCoverage = action.getLineCoverage(); - assertThat(lineCoverage).extracting(Coverage::getCovered).isEqualTo(294); + assertThat(lineCoverage).extracting(Coverage::getCovered).isEqualTo(JACOCO_CODING_STYLE_COVERED); assertThat(lineCoverage.getCoveredPercentage().doubleValue()).isCloseTo(0.91, withPercentage(1.0)); var branchCoverage = action.getBranchCoverage(); assertThat(branchCoverage.getCovered()).isEqualTo(109); assertThat(branchCoverage.getCoveredPercentage().doubleValue()).isCloseTo(0.94, withPercentage(1.0)); - assertThat(action.getCoverage(LOC)).isEqualTo(new LinesOfCode(lineCoverage.getTotal())); + assertThat(action.getCoverage(LOC)).isEqualTo(new LinesOfCode(JACOCO_CODING_STYLE_TOTAL)); assertThat(action.getCoverage(COMPLEXITY)).isEqualTo(new CyclomaticComplexity(160)); } @@ -110,13 +110,13 @@ private void verifyDeltaComputation(final Run firstBuild, final Run assertThat(delta.get(PACKAGE).doubleValue()).isCloseTo(0, withPercentage(1.0)); assertThat(delta.get(LINE).doubleValue()).isCloseTo(-0.0416, withPercentage(1.0)); assertThat(delta.get(BRANCH).doubleValue()).isCloseTo(0.0533, withPercentage(1.0)); - assertThat(delta.get(LOC).intValue()).isEqualTo(-5857); assertThat(delta.get(COMPLEXITY).intValue()).isEqualTo(160 - 2718); + assertThat(delta.get(LOC).intValue()).isEqualTo(-JACOCO_ANALYSIS_MODEL_TOTAL); - assertThat(action.formatDelta(LINE)).isEqualTo("-4.16%"); + assertThat(action.formatDelta(LINE)).isEqualTo("-4.14%"); assertThat(action.formatDelta(BRANCH)).isEqualTo("+5.33%"); - assertThat(action.formatDelta(LOC)).isEqualTo("-5857"); - assertThat(action.formatDelta(COMPLEXITY)).isEqualTo("-2558"); + assertThat(action.formatDelta(LOC)).isEqualTo(String.valueOf(-JACOCO_ANALYSIS_MODEL_TOTAL)); + assertThat(action.formatDelta(COMPLEXITY)).isEqualTo(String.valueOf(160 - 2718)); verifyChangeCoverage(action); } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/tree/TreeMapNodeConverterTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/TreeMapNodeConverterTest.java similarity index 81% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/tree/TreeMapNodeConverterTest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/model/TreeMapNodeConverterTest.java index aad37c9b1..119593e1e 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/tree/TreeMapNodeConverterTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/TreeMapNodeConverterTest.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model.visualization.tree; +package io.jenkins.plugins.coverage.model; import java.nio.file.Paths; @@ -8,11 +8,12 @@ import edu.hm.hafner.metric.Metric; import edu.hm.hafner.metric.Node; -import io.jenkins.plugins.coverage.model.AbstractCoverageTest; import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider; import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProviderFactory; import io.jenkins.plugins.coverage.model.visualization.colorization.CoverageLevel; +import io.jenkins.plugins.coverage.model.visualization.tree.TreeMapNodeConverter; +import static io.jenkins.plugins.coverage.model.AbstractCoverageITest.*; import static org.assertj.core.api.Assertions.*; /** @@ -26,10 +27,10 @@ class TreeMapNodeConverterTest extends AbstractCoverageTest { @Test void shouldConvertCodingStyleToTree() { - Node tree = readJacocoResult(Paths.get("..", "..", "jacoco-codingstyle.xml").toString()); + Node tree = readJacocoResult(Paths.get(JACOCO_CODING_STYLE_FILE).toString()); - final double totalLines = 323.0; - final double coveredLines = 294.0; + final double totalLines = JACOCO_CODING_STYLE_TOTAL; + final double coveredLines = JACOCO_CODING_STYLE_COVERED; final double coveredPercentage = coveredLines / totalLines * 100.0; TreeMapNode root = new TreeMapNodeConverter().toTeeChartModel(tree, Metric.LINE, COLOR_PROVIDER); @@ -48,13 +49,13 @@ void shouldConvertCodingStyleToTree() { @Test void shouldConvertAnalysisModelToTree() { - Node tree = readJacocoResult(Paths.get("..", "..", "jacoco-analysis-model.xml").toString()); + Node tree = readJacocoResult(Paths.get(JACOCO_ANALYSIS_MODEL_FILE).toString()); TreeMapNode root = new TreeMapNodeConverter().toTeeChartModel(tree, Metric.LINE, COLOR_PROVIDER); - final double totalLines = 6368.0; - final double coveredLines = 6083.0; - final double coveredPercentage = coveredLines / totalLines * 100.0; + double totalLines = JACOCO_ANALYSIS_MODEL_TOTAL; + double coveredLines = JACOCO_ANALYSIS_MODEL_COVERED; + double coveredPercentage = coveredLines / totalLines * 100.0; assertThat(root.getName()).isEqualTo("Static Analysis Model and Parsers"); assertThat(root.getValue()).containsExactly(totalLines, coveredLines); diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/model/jacoco-acu-cobol-parser.xml b/plugin/src/test/resources/io/jenkins/plugins/coverage/model/jacoco-acu-cobol-parser.xml index ba9223777..4a0184921 100644 --- a/plugin/src/test/resources/io/jenkins/plugins/coverage/model/jacoco-acu-cobol-parser.xml +++ b/plugin/src/test/resources/io/jenkins/plugins/coverage/model/jacoco-acu-cobol-parser.xml @@ -46,17 +46,15 @@ - - - - - - + + + + + - - - - - - + + + + + From acba09bc90443a2ac42b7dc5d3163b849b5d0cbe Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Fri, 4 Nov 2022 18:44:44 +0100 Subject: [PATCH 24/29] Fix tests with old model. --- .../DockerAndSourceCodeRenderingITest.java | 21 +++++++------------ .../DockerCoveragePluginSourceITest.java | 4 +++- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerAndSourceCodeRenderingITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerAndSourceCodeRenderingITest.java index 6ef48aa3a..78013dfa9 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerAndSourceCodeRenderingITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerAndSourceCodeRenderingITest.java @@ -1,7 +1,6 @@ package io.jenkins.plugins.coverage.model; import java.io.IOException; -import java.util.Collections; import java.util.TreeMap; import org.junit.jupiter.api.Test; @@ -19,9 +18,7 @@ import hudson.model.Node; import hudson.model.Run; -import io.jenkins.plugins.coverage.CoveragePublisher; -import io.jenkins.plugins.coverage.adapter.JacocoReportAdapter; -import io.jenkins.plugins.util.IntegrationTestWithJenkinsPerSuite; +import io.jenkins.plugins.coverage.metrics.CoverageTool.CoverageParser; import static org.assertj.core.api.Assertions.*; import static org.assertj.core.api.Assumptions.*; @@ -31,7 +28,7 @@ */ @Testcontainers(disabledWithoutDocker = true) @SuppressWarnings("checkstyle:ClassDataAbstractionCoupling") -class DockerAndSourceCodeRenderingITest extends IntegrationTestWithJenkinsPerSuite { +class DockerAndSourceCodeRenderingITest extends AbstractCoverageITest { private static final String JACOCO_ANALYSIS_MODEL_FILE = "jacoco-analysis-model.xml"; private static final String COMMIT = "6bd346bbcc9779467ce657b2618ab11e38e28c2c"; private static final String REPOSITORY = "https://github.com/jenkinsci/analysis-model.git"; @@ -57,20 +54,19 @@ void shouldCopyAndRenderSourceCodeAndRenderingInFreestyleJobOnAgent() throws IOE assumeThat(isWindows()).as("Running on Windows").isFalse(); Node agent = createDockerAgent(AGENT_CONTAINER); - FreeStyleProject project = createFreeStyleProject(); + FreeStyleProject project = createFreestyleJob(CoverageParser.JACOCO, JACOCO_ANALYSIS_MODEL_FILE); project.setAssignedNode(agent); copySingleFileToAgentWorkspace(agent, project, JACOCO_ANALYSIS_MODEL_FILE, JACOCO_ANALYSIS_MODEL_FILE); - CoveragePublisher coveragePublisher = new CoveragePublisher(); - JacocoReportAdapter jacocoReportAdapter = new JacocoReportAdapter(JACOCO_ANALYSIS_MODEL_FILE); - coveragePublisher.setAdapters(Collections.singletonList(jacocoReportAdapter)); - project.getPublishersList().add(coveragePublisher); + Run build = buildSuccessfully(project); assertThat(build.getNumber()).isEqualTo(1); CoverageBuildAction coverageResult = build.getAction(CoverageBuildAction.class); assertThat(coverageResult.getLineCoverage()) - .isEqualTo(new CoverageBuilder().setMetric(Metric.LINE).setCovered(6083).setMissed(6368 - 6083).build()); + .isEqualTo(new CoverageBuilder().setMetric(Metric.LINE) + .setCovered(JACOCO_ANALYSIS_MODEL_COVERED) + .setMissed(JACOCO_ANALYSIS_MODEL_MISSED).build()); } private void verifySourceCode(final Run build) { @@ -103,8 +99,7 @@ private WorkflowJob createPipelineWithGitAndJacocoAdapter(final String node) { + "userRemoteConfigs: [[url: '" + REPOSITORY + "']],\n" + "extensions: [[$class: 'RelativeTargetDirectory', \n" + " relativeTargetDir: 'checkout']]])\n" - + " publishCoverage adapters: [jacocoAdapter('" + JACOCO_ANALYSIS_MODEL_FILE - + "')], sourceFileResolver: sourceFiles('STORE_ALL_BUILD')\n" + + " recordCoverage tools: [[parser: 'JACOCO', pattern: '**/*xml']], sourceCodeRetention: 'LAST_BUILD' \n" + "}", true)); return job; diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerCoveragePluginSourceITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerCoveragePluginSourceITest.java index 9561ddfba..aa2935d1a 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerCoveragePluginSourceITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerCoveragePluginSourceITest.java @@ -64,7 +64,9 @@ private void verifySimpleCoverageNode(final ParameterizedJob project) { CoverageBuildAction coverageResult = build.getAction(CoverageBuildAction.class); assertThat(coverageResult.getLineCoverage()) - .isEqualTo(new Coverage.CoverageBuilder().setMetric(Metric.LINE).setCovered(6083).setMissed(6368 - 6083).build()); + .isEqualTo(new Coverage.CoverageBuilder().setMetric(Metric.LINE) + .setCovered(JACOCO_ANALYSIS_MODEL_COVERED) + .setMissed(JACOCO_ANALYSIS_MODEL_MISSED).build()); System.out.println(getConsoleLog(build)); } From 6ace53abc7bcda73b8eb8837e4d190ea49db10b7 Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Sat, 5 Nov 2022 13:25:41 +0100 Subject: [PATCH 25/29] Integrate mixed Cobertura and JaCoCo tests. --- .../coverage/metrics/AggregatedResult.java | 9 +- .../coverage/metrics/CoverageRecorder.java | 11 +- .../coverage/metrics/CoverageTool.java | 7 + .../coverage/metrics/FilesScanner.java | 6 +- .../coverage/model/CoveragePluginITest.java | 199 ++++++------------ .../DockerAndSourceCodeRenderingITest.java | 4 +- .../DockerCoveragePluginSourceITest.java | 6 +- 7 files changed, 92 insertions(+), 150 deletions(-) diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/AggregatedResult.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/AggregatedResult.java index 4cde4d7a6..dabf2fb46 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/AggregatedResult.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/AggregatedResult.java @@ -6,15 +6,18 @@ import edu.hm.hafner.util.FilteredLog; /** - * FIXME: comment class. + * Combines the root node of a coverage tree with the log that contains useful information gathered during parsing on an + * agent. * * @author Ullrich Hafner */ -public class AggregatedResult implements Serializable { +class AggregatedResult implements Serializable { + private static final long serialVersionUID = 2122230867938547733L; + private final FilteredLog log; private final Node root; - public AggregatedResult(final FilteredLog log, final Node root) { + AggregatedResult(final FilteredLog log, final Node root) { this.log = log; this.root = root; } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageRecorder.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageRecorder.java index 39307a05d..b050ba067 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageRecorder.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageRecorder.java @@ -261,6 +261,7 @@ public void perform(@NonNull final Run run, @NonNull final FilePath worksp logHandler.log(log); } else { + List results = new ArrayList<>(); for (CoverageTool tool : tools) { LogHandler toolHandler = new LogHandler(listener, tool.getActualName()); CoverageParser parser = tool.getParser(); @@ -278,17 +279,19 @@ public void perform(@NonNull final Run run, @NonNull final FilePath worksp AggregatedResult result = workspace.act( new FilesScanner(expandedPattern, "UTF-8", false, parser)); log.merge(result.getLog()); + results.add(result.getRoot()); - CoverageReporter reporter = new CoverageReporter(); - reporter.run(result.getRoot(), run, workspace, listener, getScm(), getSourceDirectoriesPaths(), - getSourceCodeEncoding(), getSourceCodeRetention()); } catch (IOException exception) { - log.logException(exception, "Exception during parsing"); + log.logException(exception, "Exception while parsing with tool " + tool); } toolHandler.log(log); } + + CoverageReporter reporter = new CoverageReporter(); + reporter.run(Node.merge(results), run, workspace, listener, getScm(), getSourceDirectoriesPaths(), + getSourceCodeEncoding(), getSourceCodeRetention()); } } else { diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageTool.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageTool.java index 05043a274..80c0add26 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageTool.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageTool.java @@ -46,6 +46,9 @@ public class CoverageTool extends AbstractDescribableImpl implemen private String pattern = StringUtils.EMPTY; private CoverageParser parser = CoverageParser.JACOCO; + /** + * Creates a new {@link CoverageTool}. + */ @DataBoundConstructor public CoverageTool() { // empty for stapler @@ -163,6 +166,10 @@ public String getActualPattern() { return StringUtils.defaultIfBlank(pattern, parser.getDefaultPattern()); } + @Override + public String toString() { + return String.format("%s (pattern: %s)", getActualName(), getActualPattern()); + } @Override public CoverageToolDescriptor getDescriptor() { diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/FilesScanner.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/FilesScanner.java index 1ce1d9f93..398c3f280 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/FilesScanner.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/FilesScanner.java @@ -78,7 +78,7 @@ public AggregatedResult invoke(final File workspace, final VirtualChannel channe } private Node scanFiles(final File workspace, final String[] fileNames, final FilteredLog log) { - List nodes = new ArrayList<>(); + List results = new ArrayList<>(); for (String fileName : fileNames) { Path file = workspace.toPath().resolve(fileName); @@ -89,10 +89,10 @@ else if (isEmpty(file)) { log.logError("Skipping file '%s' because it's empty", fileName); } else { - nodes.add(aggregateIssuesOfFile(file, log)); + results.add(aggregateIssuesOfFile(file, log)); } } - return Node.merge(nodes); + return Node.merge(results); } private boolean isEmpty(final Path file) { diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java index 14ea6e8f6..d13303e2f 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java @@ -1,30 +1,25 @@ package io.jenkins.plugins.coverage.model; -import java.util.ArrayList; import java.util.List; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; -import edu.hm.hafner.metric.Coverage; import edu.hm.hafner.metric.Coverage.CoverageBuilder; import edu.hm.hafner.metric.Metric; import edu.hm.hafner.metric.MutationValue; +import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; import org.jenkinsci.plugins.workflow.job.WorkflowJob; import hudson.model.FreeStyleProject; import hudson.model.Result; import hudson.model.Run; import jenkins.model.ParameterizedJobMixIn.ParameterizedJob; -import io.jenkins.plugins.coverage.CoveragePublisher; -import io.jenkins.plugins.coverage.adapter.CoberturaReportAdapter; -import io.jenkins.plugins.coverage.adapter.CoverageAdapter; -import io.jenkins.plugins.coverage.adapter.JacocoReportAdapter; import io.jenkins.plugins.coverage.metrics.CoverageRecorder; +import io.jenkins.plugins.coverage.metrics.CoverageTool; import io.jenkins.plugins.coverage.metrics.CoverageTool.CoverageParser; import static org.assertj.core.api.Assertions.*; @@ -33,34 +28,9 @@ * Integration test for different jacoco and cobertura files. */ class CoveragePluginITest extends AbstractCoverageITest { - /** - * Covered lines in {@value COBERTURA_HIGHER_COVERAGE_FILE}. - */ - private static final int COBERTURA_COVERED_LINES = 2; - /** - * All lines in {@value COBERTURA_HIGHER_COVERAGE_FILE}. - */ - private static final int COBERTURA_ALL_LINES = 2; - /** - * Covered lines in {@value JACOCO_ANALYSIS_MODEL_FILE} and {@value COBERTURA_HIGHER_COVERAGE_FILE}. - */ - private static final int JACOCO_COBERTURA_COVERED_LINES = 6085; - /** - * All lines in {@value JACOCO_ANALYSIS_MODEL_FILE} and {@value COBERTURA_HIGHER_COVERAGE_FILE}. - */ - private static final int JACOCO_COBERTURA_ALL_LINES = 6370; - /** - * Cobertura file for testing. - */ private static final String COBERTURA_HIGHER_COVERAGE_FILE = "cobertura-higher-coverage.xml"; - /** - * Another cobertura file for testing. - */ - private static final String COBERTURA_WITH_LOTS_OF_DATA_FILE = "cobertura-lots-of-data.xml"; - - // --------------------------------------------------------------------------------------- - // vv Converted tests vv - // --------------------------------------------------------------------------------------- + private static final int COBERTURA_COVERED_LINES = 2; + private static final int COBERTURA_MISSED_LINES = 0; @Test void shouldFailWithoutParserInFreestyleJob() { @@ -182,7 +152,6 @@ private void verifyTwoJacocoResults(final ParameterizedJob project) { void shouldRecordOneCoberturaResultInFreestyleJob() { FreeStyleProject project = createFreestyleJob(CoverageParser.COBERTURA, COBERTURA_HIGHER_COVERAGE_FILE); - // FIXME: all parsers should only fail for mandatory properties (complexity is only optional) verifyOneCoberturaResult(project); } @@ -206,137 +175,95 @@ private void verifyOneCoberturaResult(final ParameterizedJob project) { CoverageBuildAction coverageResult = build.getAction(CoverageBuildAction.class); assertThat(coverageResult.getLineCoverage()) .isEqualTo(new CoverageBuilder().setMetric(Metric.LINE).setCovered(COBERTURA_COVERED_LINES) - .setMissed(COBERTURA_ALL_LINES - COBERTURA_COVERED_LINES) + .setMissed(COBERTURA_MISSED_LINES) .build()); } @Test - void shouldRecordOnePitResultInFreestyleJob() { - FreeStyleProject project = createFreestyleJob(CoverageParser.PIT, "mutations.xml"); - - verifyOnePitResult(project); - } - - private void verifyOnePitResult(final ParameterizedJob project) { - Run build = buildSuccessfully(project); - - CoverageBuildAction coverageResult = build.getAction(CoverageBuildAction.class); - assertThat(coverageResult.getCoverage(Metric.MUTATION)) - .isInstanceOfSatisfying(MutationValue.class, m -> { - assertThat(m.getKilled()).isEqualTo(222); - assertThat(m.getTotal()).isEqualTo(246); - }); + void shouldRecordCoberturaAndJacocoResultsInFreestyleJob() { + FreeStyleProject project = createFreeStyleProjectWithWorkspaceFiles(JACOCO_ANALYSIS_MODEL_FILE, COBERTURA_HIGHER_COVERAGE_FILE); - } + CoverageRecorder recorder = new CoverageRecorder(); - private static CoverageBuilder createLineCoverageBuilder() { - return new CoverageBuilder().setMetric(Metric.LINE); - } + var cobertura = new CoverageTool(); + cobertura.setParser(CoverageParser.COBERTURA); + cobertura.setPattern(COBERTURA_HIGHER_COVERAGE_FILE); - // --------------------------------------------------------------------------------------- - // ^^ Converted tests ^^ - // --------------------------------------------------------------------------------------- + var jacoco = new CoverageTool(); + jacoco.setParser(CoverageParser.JACOCO); + jacoco.setPattern(JACOCO_ANALYSIS_MODEL_FILE); - /** - * Pipeline integration test with two cobertura files. - */ - @Test - void pipelineForTwoCobertura() { - WorkflowJob job = createPipeline(CoverageParser.COBERTURA, - COBERTURA_HIGHER_COVERAGE_FILE, COBERTURA_WITH_LOTS_OF_DATA_FILE); + recorder.setTools(List.of(jacoco, cobertura)); + project.getPublishersList().add(recorder); - verifyForTwoCobertura(job); + verifyForOneCoberturaAndOneJacoco(project); } - /** - * Freestyle integration test with two cobertura files. - */ - @Disabled("Bug") @Test - void freestyleForTwoCobertura() { - FreeStyleProject project = createFreeStyleProject(); - copyFilesToWorkspace(project, COBERTURA_HIGHER_COVERAGE_FILE, COBERTURA_WITH_LOTS_OF_DATA_FILE); - CoveragePublisher coveragePublisher = new CoveragePublisher(); - - List coverageAdapters = new ArrayList<>(); - - CoberturaReportAdapter coberturaReportAdapter = new CoberturaReportAdapter(COBERTURA_HIGHER_COVERAGE_FILE); - CoberturaReportAdapter coberturaReportAdapter2 = new CoberturaReportAdapter(COBERTURA_WITH_LOTS_OF_DATA_FILE); + void shouldRecordCoberturaAndJacocoResultsInPipeline() { + WorkflowJob job = createPipelineWithWorkspaceFiles(JACOCO_ANALYSIS_MODEL_FILE, COBERTURA_HIGHER_COVERAGE_FILE); - coverageAdapters.add(coberturaReportAdapter); - coverageAdapters.add(coberturaReportAdapter2); - coveragePublisher.setAdapters(coverageAdapters); - project.getPublishersList().add(coveragePublisher); + setPipelineScript(job, + "recordCoverage tools: [" + + "[parser: 'COBERTURA', pattern: '" + COBERTURA_HIGHER_COVERAGE_FILE + "']," + + "[parser: 'JACOCO', pattern: '" + JACOCO_ANALYSIS_MODEL_FILE + "']" + + "]"); - verifyForTwoCobertura(project); + verifyForOneCoberturaAndOneJacoco(job); } - /** - * Pipeline integration test with one cobertura and one jacoco file. - */ @Test - void pipelineForOneCoberturaAndOneJacoco() { + void shouldRecordCoberturaAndJacocoResultsInDeclarativePipeline() { WorkflowJob job = createPipelineWithWorkspaceFiles(JACOCO_ANALYSIS_MODEL_FILE, COBERTURA_HIGHER_COVERAGE_FILE); - setPipelineScript(job, "node {" - + " publishCoverage adapters: [jacocoAdapter('**/*.xml'), istanbulCoberturaAdapter('**/*.xml')]" - + "}"); + + job.setDefinition(new CpsFlowDefinition("pipeline {\n" + + " agent any\n" + + " stages {\n" + + " stage('Test') {\n" + + " steps {\n" + + " recordCoverage(tools: [\n" + + " [parser: 'COBERTURA', pattern: '" + COBERTURA_HIGHER_COVERAGE_FILE + "'],\n" + + " [parser: 'JACOCO', pattern: '" + JACOCO_ANALYSIS_MODEL_FILE + "']\n" + + " ])\n" + + " }\n" + + " }\n" + + " }\n" + + "}", true)); verifyForOneCoberturaAndOneJacoco(job); } - /** - * Freestyle integration test with one cobertura and one jacoco file. - */ - @Test - void freestyleForOneCoberturaAndOneJacoco() { - FreeStyleProject project = createFreeStyleProject(); - copyFilesToWorkspace(project, JACOCO_ANALYSIS_MODEL_FILE, COBERTURA_HIGHER_COVERAGE_FILE); - - CoveragePublisher coveragePublisher = new CoveragePublisher(); - - List coverageAdapters = new ArrayList<>(); - - JacocoReportAdapter jacocoReportAdapter = new JacocoReportAdapter( - JACOCO_ANALYSIS_MODEL_FILE); - coverageAdapters.add(jacocoReportAdapter); - - CoberturaReportAdapter coberturaReportAdapter = new CoberturaReportAdapter(COBERTURA_HIGHER_COVERAGE_FILE); - coverageAdapters.add(coberturaReportAdapter); - - coveragePublisher.setAdapters(coverageAdapters); - project.getPublishersList().add(coveragePublisher); + private void verifyForOneCoberturaAndOneJacoco(final ParameterizedJob project) { + Run build = buildSuccessfully(project); - verifyForOneCoberturaAndOneJacoco(project); + CoverageBuildAction coverageResult = build.getAction(CoverageBuildAction.class); + assertThat(coverageResult.getLineCoverage()) + .isEqualTo(createLineCoverageBuilder() + .setCovered(JACOCO_ANALYSIS_MODEL_COVERED + COBERTURA_COVERED_LINES) + .setMissed(JACOCO_ANALYSIS_MODEL_MISSED) + .build()); } - /** - * Verifies project with two cobertura files. - * - * @param project - * the project with added files - */ - private void verifyForTwoCobertura(final ParameterizedJob project) { - Run build = buildSuccessfully(project); - CoverageBuildAction coverageResult = build.getAction(CoverageBuildAction.class); - //FIXME - assertThat(coverageResult.getLineCoverage()).isEqualTo( - new Coverage.CoverageBuilder().setCovered(472).setMissed(722 - 472).build()); + @Test + void shouldRecordOnePitResultInFreestyleJob() { + FreeStyleProject project = createFreestyleJob(CoverageParser.PIT, "mutations.xml"); + + verifyOnePitResult(project); } - /** - * Verifies project with one cobertura and one jacoco file. - * - * @param project - * the project with added files - */ - private void verifyForOneCoberturaAndOneJacoco(final ParameterizedJob project) { + private void verifyOnePitResult(final ParameterizedJob project) { Run build = buildSuccessfully(project); CoverageBuildAction coverageResult = build.getAction(CoverageBuildAction.class); - assertThat(coverageResult.getLineCoverage()) - .isEqualTo(new Coverage.CoverageBuilder().setCovered(JACOCO_COBERTURA_COVERED_LINES) - .setMissed(JACOCO_COBERTURA_ALL_LINES - JACOCO_COBERTURA_COVERED_LINES) - .build()); + assertThat(coverageResult.getCoverage(Metric.MUTATION)) + .isInstanceOfSatisfying(MutationValue.class, m -> { + assertThat(m.getKilled()).isEqualTo(222); + assertThat(m.getTotal()).isEqualTo(246); + }); + } + private static CoverageBuilder createLineCoverageBuilder() { + return new CoverageBuilder().setMetric(Metric.LINE); + } } diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerAndSourceCodeRenderingITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerAndSourceCodeRenderingITest.java index 78013dfa9..5b181c941 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerAndSourceCodeRenderingITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerAndSourceCodeRenderingITest.java @@ -99,7 +99,9 @@ private WorkflowJob createPipelineWithGitAndJacocoAdapter(final String node) { + "userRemoteConfigs: [[url: '" + REPOSITORY + "']],\n" + "extensions: [[$class: 'RelativeTargetDirectory', \n" + " relativeTargetDir: 'checkout']]])\n" - + " recordCoverage tools: [[parser: 'JACOCO', pattern: '**/*xml']], sourceCodeRetention: 'LAST_BUILD' \n" + + " recordCoverage tools: [[parser: 'JACOCO', pattern: '" + + JACOCO_ANALYSIS_MODEL_FILE + + "']], sourceCodeRetention: 'LAST_BUILD' \n" + "}", true)); return job; diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerCoveragePluginSourceITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerCoveragePluginSourceITest.java index aa2935d1a..e2fcae6b5 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerCoveragePluginSourceITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerCoveragePluginSourceITest.java @@ -27,7 +27,7 @@ * @author Ullrich Hafner */ @Testcontainers(disabledWithoutDocker = true) -class DockerCoveragePluginSourceITest extends CoveragePluginSourceITest { +class DockerCoveragePluginSourceITest extends AbstractCoverageITest { @Container private static final AgentContainer AGENT_CONTAINER = new AgentContainer(); @@ -79,8 +79,8 @@ private WorkflowJob createPipelineOnAgent() { + " userRemoteConfigs: [[url: '" + "https://github.com/jenkinsci/analysis-model.git" + "']],\n" + " extensions: [[$class: 'RelativeTargetDirectory', \n" + " relativeTargetDir: 'checkout']]])\n" - + " publishCoverage adapters: [jacocoAdapter('" + JACOCO_ANALYSIS_MODEL_FILE - + "')], sourceFileResolver: sourceFiles('STORE_ALL_BUILD')\n" + + " recordCoverage tools: [[parser: 'JACOCO', pattern: '" + JACOCO_ANALYSIS_MODEL_FILE + "']], \n" + + " sourceCodeRetention: 'LAST_BUILD' \n" + "}" + "}", true)); From a9a51cb2b4c8a3ff89d08737f8033faadd10af74 Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Sat, 5 Nov 2022 17:19:40 +0100 Subject: [PATCH 26/29] Improve source code integration tests. Add dumb agent and docker agent based tests. --- .../coverage/metrics/AggregatedResult.java | 4 + .../coverage/metrics/CoverageRecorder.java | 23 +++- .../coverage/metrics/FilesScanner.java | 3 +- .../metrics/PipelineResultHandler.java | 37 ++++++ .../coverage/metrics/RunResultHandler.java | 26 +++++ .../coverage/metrics/StageResultHandler.java | 18 +++ .../coverage/model/AbstractCoverageITest.java | 10 ++ .../coverage/model/CoveragePluginITest.java | 39 ++++++- .../model/DockerAgentSourceCodeITest.java | 49 ++++++++ .../DockerAndSourceCodeRenderingITest.java | 109 ------------------ .../DockerCoveragePluginSourceITest.java | 89 -------------- .../model/LocalAgentSourceCodeITest.java | 39 +++++++ ...nSourceITest.java => SourceCodeITest.java} | 98 ++++++++-------- 13 files changed, 285 insertions(+), 259 deletions(-) create mode 100644 plugin/src/main/java/io/jenkins/plugins/coverage/metrics/PipelineResultHandler.java create mode 100644 plugin/src/main/java/io/jenkins/plugins/coverage/metrics/RunResultHandler.java create mode 100644 plugin/src/main/java/io/jenkins/plugins/coverage/metrics/StageResultHandler.java create mode 100644 plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerAgentSourceCodeITest.java delete mode 100644 plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerAndSourceCodeRenderingITest.java delete mode 100644 plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerCoveragePluginSourceITest.java create mode 100644 plugin/src/test/java/io/jenkins/plugins/coverage/model/LocalAgentSourceCodeITest.java rename plugin/src/test/java/io/jenkins/plugins/coverage/model/{CoveragePluginSourceITest.java => SourceCodeITest.java} (66%) diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/AggregatedResult.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/AggregatedResult.java index dabf2fb46..297986baa 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/AggregatedResult.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/AggregatedResult.java @@ -29,4 +29,8 @@ public FilteredLog getLog() { public Node getRoot() { return root; } + + public boolean hasErrors() { + return !getLog().getErrorMessages().isEmpty(); + } } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageRecorder.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageRecorder.java index b050ba067..32c2f8f1a 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageRecorder.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageRecorder.java @@ -52,8 +52,8 @@ * A pipeline {@code Step} or Freestyle or Maven {@link Recorder} that reads and parses coverage results in a build and * adds the results to the persisted build results. *

      - * Stores the created issues in a {@link Node}. This result is then attached to the {@link Run build} by - * registering a {@link CoverageBuildAction}. + * Stores the created issues in a {@link Node}. This result is then attached to the {@link Run build} by registering a + * {@link CoverageBuildAction}. *

      * * @author Ullrich Hafner @@ -66,7 +66,7 @@ public class CoverageRecorder extends Recorder implements SimpleBuildStep { private List tools = new ArrayList<>(); private boolean failOnError; - private boolean enabledForFailure; + private boolean enabledForFailure = true; private int healthy; private int unhealthy; private String scm = StringUtils.EMPTY; @@ -113,7 +113,8 @@ public boolean isSkipPublishingChecks() { /** * Sets whether publishing checks should be skipped or not. * - * @param skipPublishingChecks {@code true} if publishing checks should be skipped, {@code false} otherwise + * @param skipPublishingChecks + * {@code true} if publishing checks should be skipped, {@code false} otherwise */ @DataBoundSetter public void setSkipPublishingChecks(final boolean skipPublishingChecks) { @@ -280,7 +281,17 @@ public void perform(@NonNull final Run run, @NonNull final FilePath worksp new FilesScanner(expandedPattern, "UTF-8", false, parser)); log.merge(result.getLog()); results.add(result.getRoot()); - + if (result.getRoot().isEmpty() && result.hasErrors()) { + if (failOnError) { + var errorMessage = "Failing build due to some errors during recording of the coverage"; + log.logInfo(errorMessage); + var resultHandler = new RunResultHandler(run); + resultHandler.setResult(Result.FAILURE, errorMessage); + } + else { + log.logInfo("Ignore errors and continue processing"); + } + } } catch (IOException exception) { log.logException(exception, "Exception while parsing with tool " + tool); @@ -341,6 +352,7 @@ public boolean isApplicable(final Class jobType) { * * @param project * the project that is configured + * * @return a model with all {@link SourceCodeRetention} strategies. */ @POST @@ -364,6 +376,7 @@ private void add(final ListBoxModel options, final SourceCodeRetention sourceCod * * @param project * the project that is configured + * * @return a model with all available charsets */ @POST diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/FilesScanner.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/FilesScanner.java index 398c3f280..deba84e12 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/FilesScanner.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/FilesScanner.java @@ -10,6 +10,7 @@ import org.apache.commons.lang3.StringUtils; +import edu.hm.hafner.metric.ContainerNode; import edu.hm.hafner.metric.ModuleNode; import edu.hm.hafner.metric.Node; import edu.hm.hafner.metric.parser.XmlParser; @@ -67,7 +68,7 @@ public AggregatedResult invoke(final File workspace, final VirtualChannel channe if (fileNames.length == 0) { log.logError("No files found for pattern '%s'. Configuration error?", filePattern); - return new AggregatedResult(log, new ModuleNode(filePattern)); + return new AggregatedResult(log, ContainerNode.EMPTY_TREE); } else { log.logInfo("-> found %s", plural(fileNames.length, "file")); diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/PipelineResultHandler.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/PipelineResultHandler.java new file mode 100644 index 000000000..e7ddbc150 --- /dev/null +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/PipelineResultHandler.java @@ -0,0 +1,37 @@ +package io.jenkins.plugins.coverage.metrics; + +import org.jenkinsci.plugins.workflow.actions.WarningAction; +import org.jenkinsci.plugins.workflow.graph.FlowNode; +import hudson.model.Result; +import hudson.model.Run; + +/** + * {@link StageResultHandler} that sets the overall build result of the {@link Run} and annotates the given Pipeline + * step with a {@link WarningAction}. + */ +public class PipelineResultHandler implements StageResultHandler { + private final Run run; + private final FlowNode flowNode; + + /** + * Creates a new instance of {@link PipelineResultHandler}. + * + * @param run + * the run to set the result for + * @param flowNode + * the flow node to add a warning to + */ + public PipelineResultHandler(final Run run, final FlowNode flowNode) { + this.run = run; + this.flowNode = flowNode; + } + + @Override + public void setResult(final Result result, final String message) { + run.setResult(result); + WarningAction existing = flowNode.getPersistentAction(WarningAction.class); + if (existing == null || existing.getResult().isBetterThan(result)) { + flowNode.addOrReplaceAction(new WarningAction(result).withMessage(message)); + } + } +} diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/RunResultHandler.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/RunResultHandler.java new file mode 100644 index 000000000..65215f4d5 --- /dev/null +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/RunResultHandler.java @@ -0,0 +1,26 @@ +package io.jenkins.plugins.coverage.metrics; + +import hudson.model.Result; +import hudson.model.Run; + +/** + * {@link StageResultHandler} that sets the overall build result of the {@link Run}. + */ +public class RunResultHandler implements StageResultHandler { + private final Run run; + + /** + * Creates a new instance of {@link RunResultHandler}. + * + * @param run + * the run to set the result for + */ + public RunResultHandler(final Run run) { + this.run = run; + } + + @Override + public void setResult(final Result result, final String message) { + run.setResult(result); + } +} diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/StageResultHandler.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/StageResultHandler.java new file mode 100644 index 000000000..d8c5719ed --- /dev/null +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/StageResultHandler.java @@ -0,0 +1,18 @@ +package io.jenkins.plugins.coverage.metrics; + +import hudson.model.Result; + +/** + * Handles the setting of the results of a stage. + */ +public interface StageResultHandler { + /** + * Called to set the {@link Result} of a stage. + * + * @param result + * the result to set + * @param message + * a message that describes the cause for the result + */ + void setResult(Result result, String message); +} diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageITest.java index 58c4cc6ac..5b00a97e7 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageITest.java @@ -1,6 +1,7 @@ package io.jenkins.plugins.coverage.model; import java.util.List; +import java.util.function.Consumer; import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; import org.jenkinsci.plugins.workflow.job.WorkflowJob; @@ -30,13 +31,22 @@ public abstract class AbstractCoverageITest extends IntegrationTestWithJenkinsPe = JACOCO_CODING_STYLE_COVERED + JACOCO_CODING_STYLE_MISSED; protected FreeStyleProject createFreestyleJob(final CoverageParser parser, final String... fileNames) { + return createFreestyleJob(parser, i -> { }, fileNames); + } + + protected FreeStyleProject createFreestyleJob(final CoverageParser parser, + final Consumer configuration, final String... fileNames) { FreeStyleProject project = createFreeStyleProjectWithWorkspaceFiles(fileNames); CoverageRecorder recorder = new CoverageRecorder(); + var tool = new CoverageTool(); tool.setParser(parser); tool.setPattern("**/*xml"); recorder.setTools(List.of(tool)); + + configuration.accept(recorder); + project.getPublishersList().add(recorder); return project; diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java index d13303e2f..2ce899b43 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java @@ -31,6 +31,7 @@ class CoveragePluginITest extends AbstractCoverageITest { private static final String COBERTURA_HIGHER_COVERAGE_FILE = "cobertura-higher-coverage.xml"; private static final int COBERTURA_COVERED_LINES = 2; private static final int COBERTURA_MISSED_LINES = 0; + private static final String NO_FILES_FOUND_ERROR_MESSAGE = "[-ERROR-] No files found for pattern '**/*xml'. Configuration error?"; @Test void shouldFailWithoutParserInFreestyleJob() { @@ -62,7 +63,7 @@ private void verifyNoParserError(final ParameterizedJob project) { void shouldReportErrorWhenNoFilesHaveBeenFoundInFreestyleJob(final CoverageParser parser) { FreeStyleProject project = createFreestyleJob(parser); - verifyNoFilesFound(project); + verifyLogMessageThatNoFilesFound(project); } @EnumSource @@ -71,13 +72,43 @@ void shouldReportErrorWhenNoFilesHaveBeenFoundInFreestyleJob(final CoverageParse void shouldReportErrorWhenNoFilesHaveBeenFoundInPipeline(final CoverageParser parser) { WorkflowJob job = createPipeline(parser); - verifyNoFilesFound(job); + verifyLogMessageThatNoFilesFound(job); } - private void verifyNoFilesFound(final ParameterizedJob project) { + private void verifyLogMessageThatNoFilesFound(final ParameterizedJob project) { Run run = buildWithResult(project, Result.SUCCESS); - assertThat(getConsoleLog(run)).contains("[-ERROR-] No files found for pattern '**/*xml'. Configuration error?"); + assertThat(getConsoleLog(run)).contains(NO_FILES_FOUND_ERROR_MESSAGE, + "Ignore errors and continue processing"); + } + + @EnumSource + @ParameterizedTest(name = "{index} => Freestyle job with parser {0}") + @DisplayName("Report error and fail build in freestyle job when no input files are found") + void shouldFailBuildWhenNoFilesHaveBeenFoundInFreestyleJob(final CoverageParser parser) { + FreeStyleProject project = createFreestyleJob(parser, r -> r.setFailOnError(true)); + + verifyFailureWhenNoFilesFound(project); + } + + @EnumSource + @ParameterizedTest(name = "{index} => Pipeline with parser {0}") + @DisplayName("Report error and fail build in pipeline when no input files are found") + void shouldFailBuildWhenNoFilesHaveBeenFoundInPipeline(final CoverageParser parser) { + WorkflowJob job = createPipeline(); + + setPipelineScript(job, + "recordCoverage tools: [[parser: '" + parser.name() + "', pattern: '**/*xml']], " + + "failOnError: 'true'"); + + verifyFailureWhenNoFilesFound(job); + } + + private void verifyFailureWhenNoFilesFound(final ParameterizedJob project) { + Run run = buildWithResult(project, Result.FAILURE); + + assertThat(getConsoleLog(run)).contains(NO_FILES_FOUND_ERROR_MESSAGE, + "Failing build due to some errors during recording of the coverage"); } @Test diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerAgentSourceCodeITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerAgentSourceCodeITest.java new file mode 100644 index 000000000..2cea5775b --- /dev/null +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerAgentSourceCodeITest.java @@ -0,0 +1,49 @@ +package io.jenkins.plugins.coverage.model; + +import java.io.IOException; + +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.MountableFile; + +import org.jenkinsci.plugins.workflow.job.WorkflowJob; +import hudson.model.Node; + +/** + * Verifies if source code copying and rendering works on Docker agents. + * + * @author Ullrich Hafner + */ +@Testcontainers(disabledWithoutDocker = true) +class DockerAgentSourceCodeITest extends SourceCodeITest { + private static final String SOURCES_IN_DOCKER_PATH = "/tmp/coverage"; + private static final String CONTAINER_PATH = SOURCES_IN_DOCKER_PATH + "/" + PACKAGE_PATH + SOURCE_FILE_NAME; + + @Container + private static final AgentContainer AGENT_CONTAINER = new AgentContainer() + .withCopyFileToContainer(MountableFile.forClasspathResource("io/jenkins/plugins/coverage/model/" + SOURCE_FILE), CONTAINER_PATH); + + @Override + protected Node crateCoverageAgent() { + try { + Node agent = createDockerAgent(AGENT_CONTAINER); + agent.setLabelString(AGENT_LABEL); + return agent; + } + catch (IOException exception) { + throw new AssertionError(exception); + } + } + + @Override + protected String createExternalFolder() throws IOException { + return SOURCES_IN_DOCKER_PATH; + } + + @Override + protected void copySourceFileToAgent(final String sourceDirectory, final hudson.model.Node localAgent, final WorkflowJob job) { + if (!sourceDirectory.startsWith(SOURCES_IN_DOCKER_PATH)) { + copySingleFileToAgentWorkspace(localAgent, job, SOURCE_FILE, createDestinationPath(sourceDirectory)); + } + } +} diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerAndSourceCodeRenderingITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerAndSourceCodeRenderingITest.java deleted file mode 100644 index 5b181c941..000000000 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerAndSourceCodeRenderingITest.java +++ /dev/null @@ -1,109 +0,0 @@ -package io.jenkins.plugins.coverage.model; - -import java.io.IOException; -import java.util.TreeMap; - -import org.junit.jupiter.api.Test; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; - -import edu.hm.hafner.metric.Coverage.CoverageBuilder; -import edu.hm.hafner.metric.Metric; -import edu.hm.hafner.metric.ModuleNode; - -import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; -import org.jenkinsci.plugins.workflow.job.WorkflowJob; -import hudson.model.FreeStyleProject; -import hudson.model.HealthReport; -import hudson.model.Node; -import hudson.model.Run; - -import io.jenkins.plugins.coverage.metrics.CoverageTool.CoverageParser; - -import static org.assertj.core.api.Assertions.*; -import static org.assertj.core.api.Assumptions.*; - -/** - * Tests if source code copying and rendering and copying works on Docker agents. - */ -@Testcontainers(disabledWithoutDocker = true) -@SuppressWarnings("checkstyle:ClassDataAbstractionCoupling") -class DockerAndSourceCodeRenderingITest extends AbstractCoverageITest { - private static final String JACOCO_ANALYSIS_MODEL_FILE = "jacoco-analysis-model.xml"; - private static final String COMMIT = "6bd346bbcc9779467ce657b2618ab11e38e28c2c"; - private static final String REPOSITORY = "https://github.com/jenkinsci/analysis-model.git"; - @Container - private static final AgentContainer AGENT_CONTAINER = new AgentContainer(); - - @Test - void shouldCopyAndRenderSourceCodeAndRenderingInPipelineOnDockerAgent() { - assumeThat(isWindows()).as("Running on Windows").isFalse(); - - Node agent = createDockerAgent(AGENT_CONTAINER); - WorkflowJob project = createPipelineWithGitAndJacocoAdapter("node('" + DOCKER_AGENT_NAME + "')"); - - copySingleFileToAgentWorkspace(agent, project, JACOCO_ANALYSIS_MODEL_FILE, JACOCO_ANALYSIS_MODEL_FILE); - - Run build = verifyGitRepository(project); - - verifySourceCode(build); - } - - @Test - void shouldCopyAndRenderSourceCodeAndRenderingInFreestyleJobOnAgent() throws IOException { - assumeThat(isWindows()).as("Running on Windows").isFalse(); - - Node agent = createDockerAgent(AGENT_CONTAINER); - FreeStyleProject project = createFreestyleJob(CoverageParser.JACOCO, JACOCO_ANALYSIS_MODEL_FILE); - project.setAssignedNode(agent); - copySingleFileToAgentWorkspace(agent, project, JACOCO_ANALYSIS_MODEL_FILE, JACOCO_ANALYSIS_MODEL_FILE); - - Run build = buildSuccessfully(project); - - assertThat(build.getNumber()).isEqualTo(1); - - CoverageBuildAction coverageResult = build.getAction(CoverageBuildAction.class); - assertThat(coverageResult.getLineCoverage()) - .isEqualTo(new CoverageBuilder().setMetric(Metric.LINE) - .setCovered(JACOCO_ANALYSIS_MODEL_COVERED) - .setMissed(JACOCO_ANALYSIS_MODEL_MISSED).build()); - } - - private void verifySourceCode(final Run build) { - ModuleNode root = new ModuleNode("top-level"); - - CoverageBuildAction action = new CoverageBuildAction(build, root, new HealthReport(), "-", - new TreeMap<>(), new TreeMap<>(), new TreeMap<>(), new TreeMap<>(), false); - - assertThat(action.getTarget()).extracting(CoverageViewModel::getNode).isEqualTo(root); - assertThat(action.getTarget()).extracting(CoverageViewModel::getOwner).isEqualTo(build); - } - - private Run verifyGitRepository(final WorkflowJob workflowJob) { - Run build = buildSuccessfully(workflowJob); - - String consoleLog = getConsoleLog(build); - - assertThat(consoleLog) - .contains("Cloning repository " + REPOSITORY) - .contains("Checking out Revision " + COMMIT) - .contains("checkout -f " + COMMIT); - return build; - } - - private WorkflowJob createPipelineWithGitAndJacocoAdapter(final String node) { - WorkflowJob job = createPipeline(); - job.setDefinition(new CpsFlowDefinition(node + " {" - + " checkout([$class: 'GitSCM', " - + "branches: [[name: '" + COMMIT + "' ]],\n" - + "userRemoteConfigs: [[url: '" + REPOSITORY + "']],\n" - + "extensions: [[$class: 'RelativeTargetDirectory', \n" - + " relativeTargetDir: 'checkout']]])\n" - + " recordCoverage tools: [[parser: 'JACOCO', pattern: '" - + JACOCO_ANALYSIS_MODEL_FILE - + "']], sourceCodeRetention: 'LAST_BUILD' \n" - + "}", true)); - - return job; - } -} diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerCoveragePluginSourceITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerCoveragePluginSourceITest.java deleted file mode 100644 index e2fcae6b5..000000000 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerCoveragePluginSourceITest.java +++ /dev/null @@ -1,89 +0,0 @@ -package io.jenkins.plugins.coverage.model; - -import java.io.IOException; - -import org.junit.jupiter.api.Test; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; - -import edu.hm.hafner.metric.Coverage; -import edu.hm.hafner.metric.Metric; - -import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; -import org.jenkinsci.plugins.workflow.job.WorkflowJob; -import hudson.model.FreeStyleProject; -import hudson.model.Node; -import hudson.model.Run; -import jenkins.model.ParameterizedJobMixIn.ParameterizedJob; - -import io.jenkins.plugins.coverage.metrics.CoverageTool.CoverageParser; - -import static org.assertj.core.api.Assertions.*; -import static org.assertj.core.api.Assumptions.*; - -/** - * Integration tests for the coverage API plugin. - * - * @author Ullrich Hafner - */ -@Testcontainers(disabledWithoutDocker = true) -class DockerCoveragePluginSourceITest extends AbstractCoverageITest { - @Container - private static final AgentContainer AGENT_CONTAINER = new AgentContainer(); - - /** Integration test for a freestyle build with code coverage that runs on an agent. */ - @Test - void coverageFreeStyleOnAgent() throws IOException { - assumeThat(isWindows()).as("Running on Windows").isFalse(); - - Node agent = createDockerAgent(AGENT_CONTAINER); - FreeStyleProject project = createFreestyleJob(CoverageParser.JACOCO, JACOCO_ANALYSIS_MODEL_FILE); - project.setAssignedNode(agent); - copySingleFileToAgentWorkspace(agent, project, JACOCO_ANALYSIS_MODEL_FILE, JACOCO_ANALYSIS_MODEL_FILE); - - verifySimpleCoverageNode(project); - } - - /** Integration test for a pipeline with code coverage that runs on an agent. */ - @Test - void coveragePipelineOnAgentNode() { - assumeThat(isWindows()).as("Running on Windows").isFalse(); - - Node agent = createDockerAgent(AGENT_CONTAINER); - WorkflowJob project = createPipelineOnAgent(); - - copySingleFileToAgentWorkspace(agent, project, JACOCO_ANALYSIS_MODEL_FILE, JACOCO_ANALYSIS_MODEL_FILE); - - verifySimpleCoverageNode(project); - } - - @SuppressWarnings("PMD.SystemPrintln") - private void verifySimpleCoverageNode(final ParameterizedJob project) { - Run build = buildSuccessfully(project); - assertThat(build.getNumber()).isEqualTo(1); - - CoverageBuildAction coverageResult = build.getAction(CoverageBuildAction.class); - assertThat(coverageResult.getLineCoverage()) - .isEqualTo(new Coverage.CoverageBuilder().setMetric(Metric.LINE) - .setCovered(JACOCO_ANALYSIS_MODEL_COVERED) - .setMissed(JACOCO_ANALYSIS_MODEL_MISSED).build()); - System.out.println(getConsoleLog(build)); - } - - private WorkflowJob createPipelineOnAgent() { - WorkflowJob job = createPipeline(); - job.setDefinition(new CpsFlowDefinition("node('" + DOCKER_AGENT_NAME + "') {" - + "timestamps {\n" - + " checkout([$class: 'GitSCM', " - + " branches: [[name: '6bd346bbcc9779467ce657b2618ab11e38e28c2c' ]],\n" - + " userRemoteConfigs: [[url: '" + "https://github.com/jenkinsci/analysis-model.git" + "']],\n" - + " extensions: [[$class: 'RelativeTargetDirectory', \n" - + " relativeTargetDir: 'checkout']]])\n" - + " recordCoverage tools: [[parser: 'JACOCO', pattern: '" + JACOCO_ANALYSIS_MODEL_FILE + "']], \n" - + " sourceCodeRetention: 'LAST_BUILD' \n" - + "}" - + "}", true)); - - return job; - } -} diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/LocalAgentSourceCodeITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/LocalAgentSourceCodeITest.java new file mode 100644 index 000000000..260c71eb0 --- /dev/null +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/LocalAgentSourceCodeITest.java @@ -0,0 +1,39 @@ +package io.jenkins.plugins.coverage.model; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; + +import edu.hm.hafner.util.PathUtil; + +import org.jenkinsci.plugins.workflow.job.WorkflowJob; +import hudson.model.Node; +import hudson.slaves.DumbSlave; + +/** + * Verifies if source code copying and rendering works on local dummy agents (see {@link DumbSlave}). + * + * @author Ullrich Hafner + */ +class LocalAgentSourceCodeITest extends SourceCodeITest { + private static final PathUtil PATH_UTIL = new PathUtil(); + + protected hudson.model.Node crateCoverageAgent() { + return createAgent(AGENT_LABEL); + } + + protected String createExternalFolder() throws IOException { + Path tempDirectory = Files.createTempDirectory("coverage"); + Path sourceCodeDirectory = tempDirectory.resolve(PACKAGE_PATH); + Files.createDirectories(sourceCodeDirectory); + Files.copy(getResourceAsFile(SOURCE_FILE), sourceCodeDirectory.resolve("AcuCobolParser.java"), + StandardCopyOption.REPLACE_EXISTING); + return PATH_UTIL.getAbsolutePath(tempDirectory); + } + + @Override + protected void copySourceFileToAgent(final String sourceDirectory, final Node localAgent, final WorkflowJob job) { + copySingleFileToAgentWorkspace(localAgent, job, SOURCE_FILE, createDestinationPath(sourceDirectory)); + } +} diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginSourceITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/SourceCodeITest.java similarity index 66% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginSourceITest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/model/SourceCodeITest.java index b77787d65..1f932a72b 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginSourceITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/SourceCodeITest.java @@ -2,10 +2,7 @@ import java.io.File; import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardCopyOption; -import java.util.Collections; +import java.util.List; import java.util.Optional; import org.junit.jupiter.api.Test; @@ -13,12 +10,10 @@ import edu.hm.hafner.metric.Coverage.CoverageBuilder; import edu.hm.hafner.metric.Metric; import edu.hm.hafner.metric.Node; -import edu.hm.hafner.util.PathUtil; import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; import org.jenkinsci.plugins.workflow.job.WorkflowJob; import hudson.model.Run; -import hudson.model.TopLevelItem; import io.jenkins.plugins.prism.PermittedSourceCodeDirectory; import io.jenkins.plugins.prism.PrismConfiguration; @@ -28,66 +23,52 @@ import static org.assertj.core.api.Assertions.*; /** - * Integration tests for the coverage API plugin. + * Verifies if source code copying and rendering works on agents. * * @author Ullrich Hafner */ -@SuppressWarnings({"checkstyle:ClassDataAbstractionCoupling", "checkstyle:ClassFanOutComplexity"}) -class CoveragePluginSourceITest extends AbstractCoverageITest { +abstract class SourceCodeITest extends AbstractCoverageITest { private static final String ACU_COBOL_PARSER = "public class AcuCobolParser extends LookaheadParser {"; private static final String NO_SOURCE_CODE = "n/a"; - private static final String SOURCE_FILE_NAME = "AcuCobolParser.java"; - private static final String SOURCE_FILE = SOURCE_FILE_NAME + ".txt"; - private static final String PACKAGE_PATH = "edu/hm/hafner/analysis/parser/"; + static final String SOURCE_FILE_NAME = "AcuCobolParser.java"; + static final String SOURCE_FILE = SOURCE_FILE_NAME + ".txt"; + static final String PACKAGE_PATH = "edu/hm/hafner/analysis/parser/"; private static final String SOURCE_FILE_PATH = PACKAGE_PATH + SOURCE_FILE_NAME; private static final String ACU_COBOL_PARSER_COVERAGE_REPORT = "jacoco-acu-cobol-parser.xml"; - private static final PathUtil PATH_UTIL = new PathUtil(); + static final String AGENT_LABEL = "coverage-agent"; /** Verifies that the plugin reads source code from the workspace root. */ @Test - void coveragePluginPipelineWithSourceCode() { - Run workspace = runCoverageWithSourceCode("", ""); - - assertThat(getConsoleLog(workspace)).contains(createSingleMessage(workspace)); - } - - private String createSingleMessage(final Run workspace) { - return String.format("Searching for source code files in '%s'", createSingleDirectory(workspace)); + void coveragePluginPipelineWithSourceCode() throws IOException { + runCoverageWithSourceCode(""); } /** Verifies that the plugin reads source code in subdirectories of the workspace. */ @Test - void coveragePluginPipelineWithSourceCodeInSubdirectory() { - Run workspace = runCoverageWithSourceCode("", ""); - assertThat(getConsoleLog(workspace)).contains(createSingleMessage(workspace)); - } - - private String createSingleDirectory(final Run workspace) { - return PATH_UTIL.getAbsolutePath(String.format("%s/src/main/java", - getWorkspace((TopLevelItem) workspace.getParent()).getRemote())); + void coveragePluginPipelineWithSourceCodeInSubdirectory() throws IOException { + runCoverageWithSourceCode("sub-dir"); } /** Verifies that the plugin reads source code in external but approved directories. */ @Test void coveragePluginPipelineWithSourceCodeInPermittedDirectory() throws IOException { - String directory = createExternalSourceFolder(); - PrismConfiguration.getInstance().setSourceDirectories(Collections.singletonList( - new PermittedSourceCodeDirectory(directory))); + String directory = createExternalFolder(); + PrismConfiguration.getInstance().setSourceDirectories(List.of(new PermittedSourceCodeDirectory(directory))); - Run externalDirectory = runCoverageWithSourceCode("ignore", directory); + Run externalDirectory = runCoverageWithSourceCode(directory); assertThat(getConsoleLog(externalDirectory)) - .contains("Searching for source code files in:", - "-> " + createSingleDirectory(externalDirectory), - "-> " + directory); + .contains("Searching for source code files in:", "-> " + directory); } /** Verifies that the plugin refuses source code in directories that are not approved in Jenkins' configuration. */ @Test void coveragePluginPipelineNotRegisteredSourceCodeDirectory() throws IOException { - String sourceDirectory = createExternalSourceFolder(); + var localAgent = crateCoverageAgent(); + String sourceDirectory = createExternalFolder(); - WorkflowJob job = createPipelineWithWorkspaceFiles(ACU_COBOL_PARSER_COVERAGE_REPORT); - copyFileToWorkspace(job, SOURCE_FILE, "ignore/" + PACKAGE_PATH + "AcuCobolParser.java"); + WorkflowJob job = createPipeline(); + copySourceFileToAgent("ignore/", localAgent, job); + copySingleFileToAgentWorkspace(localAgent, job, ACU_COBOL_PARSER_COVERAGE_REPORT, ACU_COBOL_PARSER_COVERAGE_REPORT); job.setDefinition(createPipelineWithSourceCode(EVERY_BUILD, sourceDirectory)); @@ -100,20 +81,16 @@ void coveragePluginPipelineNotRegisteredSourceCodeDirectory() throws IOException sourceDirectory)); verifySourceCodeInBuild(firstBuild, NO_SOURCE_CODE); // should be still available + localAgent.setLabelString(""); } - private String createExternalSourceFolder() throws IOException { - Path tempDirectory = Files.createTempDirectory("coverage"); - Path sourceCodeDirectory = tempDirectory.resolve(PACKAGE_PATH); - Files.createDirectories(sourceCodeDirectory); - Files.copy(getResourceAsFile(SOURCE_FILE), sourceCodeDirectory.resolve("AcuCobolParser.java"), - StandardCopyOption.REPLACE_EXISTING); - return PATH_UTIL.getAbsolutePath(tempDirectory); - } + private Run runCoverageWithSourceCode(final String sourceDirectory) + throws IOException { + var localAgent = crateCoverageAgent(); - private Run runCoverageWithSourceCode(final String checkoutDirectory, final String sourceDirectory) { - WorkflowJob job = createPipelineWithWorkspaceFiles(ACU_COBOL_PARSER_COVERAGE_REPORT); - copyFileToWorkspace(job, SOURCE_FILE, checkoutDirectory + PACKAGE_PATH + "AcuCobolParser.java"); + WorkflowJob job = createPipeline(); + copySingleFileToAgentWorkspace(localAgent, job, ACU_COBOL_PARSER_COVERAGE_REPORT, ACU_COBOL_PARSER_COVERAGE_REPORT); + copySourceFileToAgent(sourceDirectory, localAgent, job); // get the temporary directory - used by unit tests - to verify its content File temporaryDirectory = new File(System.getProperty("java.io.tmpdir")); @@ -146,12 +123,13 @@ private String createExternalSourceFolder() throws IOException { assertThat(temporaryDirectory.listFiles()).isEqualTo(temporaryFiles); + localAgent.setLabelString(""); return firstBuild; } private CpsFlowDefinition createPipelineWithSourceCode(final SourceCodeRetention sourceCodeRetention, final String sourceDirectory) { - return new CpsFlowDefinition("node {" + return new CpsFlowDefinition("node ('coverage-agent') {" + " recordCoverage tools: [[parser: 'JACOCO', pattern: '**/*xml']], \n" + " sourceCodeRetention: '" + sourceCodeRetention.name() + "', \n" + " sourceCodeEncoding: 'UTF-8', \n" @@ -168,6 +146,9 @@ private void verifySourceCodeInBuild(final Run build, final String sourceC private CoverageViewModel verifyViewModel(final Run build) { CoverageBuildAction action = build.getAction(CoverageBuildAction.class); + + System.out.println(getConsoleLog(build)); + assertThat(action.getLineCoverage()) .isEqualTo(new CoverageBuilder().setMetric(Metric.LINE).setCovered(8).setMissed(0).build()); @@ -177,4 +158,19 @@ private CoverageViewModel verifyViewModel(final Run build) { return action.getTarget(); } + + String createDestinationPath(final String sourceDirectory) { + if (sourceDirectory.isEmpty()) { + return PACKAGE_PATH + "AcuCobolParser.java"; + } + else { + return sourceDirectory + "/" + PACKAGE_PATH + "AcuCobolParser.java"; + } + } + + abstract hudson.model.Node crateCoverageAgent(); + + abstract String createExternalFolder() throws IOException; + + abstract void copySourceFileToAgent(String sourceDirectory, hudson.model.Node localAgent, WorkflowJob job); } From de6f2d25e3a8bffc0566c6c7374eae102a63606b Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Sat, 5 Nov 2022 19:27:03 +0100 Subject: [PATCH 27/29] Fix delta test. --- .../coverage/model/AbstractCoverageITest.java | 22 ++++- .../coverage/model/GitForensicsITest.java | 93 ++++++++----------- 2 files changed, 58 insertions(+), 57 deletions(-) diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageITest.java index 5b00a97e7..0371efc24 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageITest.java @@ -1,5 +1,6 @@ package io.jenkins.plugins.coverage.model; +import java.io.IOException; import java.util.List; import java.util.function.Consumer; @@ -38,18 +39,33 @@ protected FreeStyleProject createFreestyleJob(final CoverageParser parser, final Consumer configuration, final String... fileNames) { FreeStyleProject project = createFreeStyleProjectWithWorkspaceFiles(fileNames); + addCoverageRecorder(project, parser, "**/*xml", configuration); + + return project; + } + + void addCoverageRecorder(final FreeStyleProject project, final CoverageParser parser, final String pattern) { + addCoverageRecorder(project, parser, pattern, i -> { }); + } + + void addCoverageRecorder(final FreeStyleProject project, + final CoverageParser parser, final String pattern, final Consumer configuration) { CoverageRecorder recorder = new CoverageRecorder(); var tool = new CoverageTool(); tool.setParser(parser); - tool.setPattern("**/*xml"); + tool.setPattern(pattern); recorder.setTools(List.of(tool)); configuration.accept(recorder); + try { + project.getPublishersList().remove(CoverageRecorder.class); + } + catch (IOException exception) { + // ignore and continue + } project.getPublishersList().add(recorder); - - return project; } protected WorkflowJob createPipeline(final CoverageParser parser, final String... fileNames) { diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/GitForensicsITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/GitForensicsITest.java index d3f1439da..05973119d 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/GitForensicsITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/model/GitForensicsITest.java @@ -1,17 +1,16 @@ package io.jenkins.plugins.coverage.model; import java.io.IOException; -import java.util.AbstractMap.SimpleEntry; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; -import org.apache.commons.lang3.math.Fraction; import org.junit.jupiter.api.Test; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import edu.hm.hafner.metric.Coverage; +import edu.hm.hafner.metric.Coverage.CoverageBuilder; import edu.hm.hafner.metric.FileNode; import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; @@ -26,7 +25,7 @@ import io.jenkins.plugins.coverage.CoveragePublisher; import io.jenkins.plugins.coverage.adapter.JacocoReportAdapter; -import io.jenkins.plugins.util.IntegrationTestWithJenkinsPerSuite; +import io.jenkins.plugins.coverage.metrics.CoverageTool.CoverageParser; import static edu.hm.hafner.metric.Metric.*; import static io.jenkins.plugins.coverage.model.Assertions.*; @@ -38,8 +37,7 @@ * @author Florian Orendi */ @Testcontainers(disabledWithoutDocker = true) -class GitForensicsITest extends IntegrationTestWithJenkinsPerSuite { - +class GitForensicsITest extends AbstractCoverageITest { /** * The JaCoCo coverage report, generated for the commit {@link #COMMIT}. */ @@ -67,11 +65,11 @@ void shouldIntegrateForensicsPluginInPipelineOnDockerAgent() { copySingleFileToAgentWorkspace(agent, project, JACOCO_REFERENCE_FILE, JACOCO_REFERENCE_FILE); copySingleFileToAgentWorkspace(agent, project, JACOCO_FILE, JACOCO_FILE); - project.setDefinition(createFlowDefinitionForCommit(node, COMMIT_REFERENCE, JACOCO_REFERENCE_FILE)); + project.setDefinition(createPipelineForCommit(node, COMMIT_REFERENCE, JACOCO_REFERENCE_FILE)); Run referenceBuild = buildSuccessfully(project); verifyGitRepositoryForCommit(referenceBuild, COMMIT_REFERENCE); - project.setDefinition(createFlowDefinitionForCommit(node, COMMIT, JACOCO_FILE)); + project.setDefinition(createPipelineForCommit(node, COMMIT, JACOCO_FILE)); Run build = buildSuccessfully(project); verifyGitRepositoryForCommit(build, COMMIT); @@ -83,17 +81,20 @@ void shouldIntegrateForensicsPluginInFreestyleJobOnAgent() throws IOException { assumeThat(isWindows()).as("Running on Windows").isFalse(); Node agent = createDockerAgent(AGENT_CONTAINER); - FreeStyleProject project = createFreeStyleProject(); + FreeStyleProject project = createFreestyleJob(CoverageParser.JACOCO); project.setAssignedNode(agent); + + configureGit(project, COMMIT_REFERENCE); + addCoverageRecorder(project, CoverageParser.JACOCO, JACOCO_REFERENCE_FILE); + copySingleFileToAgentWorkspace(agent, project, JACOCO_FILE, JACOCO_FILE); copySingleFileToAgentWorkspace(agent, project, JACOCO_REFERENCE_FILE, JACOCO_REFERENCE_FILE); - setGitScmWithCommitSpecForFreeStyleProject(project, COMMIT_REFERENCE); - setCoveragePublisherForFreeStyleProject(project, JACOCO_REFERENCE_FILE); Run referenceBuild = buildSuccessfully(project); - setGitScmWithCommitSpecForFreeStyleProject(project, COMMIT); - setCoveragePublisherForFreeStyleProject(project, JACOCO_FILE); + configureGit(project, COMMIT); + addCoverageRecorder(project, CoverageParser.JACOCO, JACOCO_FILE); + Run build = buildSuccessfully(project); verifyGitIntegration(build, referenceBuild); @@ -154,18 +155,13 @@ private void verifyCoverage(final CoverageBuildAction action) { * The created Jenkins action */ private void verifyOverallCoverage(final CoverageBuildAction action) { - assertThat(action.getLineCoverage()).satisfies(coverage -> { - assertThat(coverage.getCovered()).isEqualTo(546); - assertThat(coverage.getMissed()).isEqualTo(461); - }); - assertThat(action.getBranchCoverage()).satisfies(coverage -> { - assertThat(coverage.getCovered()).isEqualTo(136); - assertThat(coverage.getMissed()).isEqualTo(94); - }); - assertThat(action.getDelta()).contains( - new SimpleEntry<>(LINE, Fraction.getFraction(65_160, 103_721)), - new SimpleEntry<>(BRANCH, Fraction.getFraction(0, 1)) - ); + var builder = new CoverageBuilder(); + assertThat(action.getLineCoverage()) + .isEqualTo(builder.setMetric(LINE).setCovered(529).setMissed(408).build()); + assertThat(action.getBranchCoverage()) + .isEqualTo(builder.setMetric(BRANCH).setCovered(136).setMissed(94).build()); + assertThat(action.formatDelta(LINE)).isEqualTo("+0.72%"); + assertThat(action.formatDelta(BRANCH)).isEqualTo("+0.00%"); } /** @@ -175,19 +171,16 @@ private void verifyOverallCoverage(final CoverageBuildAction action) { * The created Jenkins action */ private void verifyChangeCoverage(final CoverageBuildAction action) { - assertThat(action.getChangeCoverage(LINE)).isInstanceOfSatisfying(Coverage.class, coverage -> { - assertThat(coverage.getCovered()).isEqualTo(1); - assertThat(coverage.getMissed()).isEqualTo(1); - }); - assertThat(action.getChangeCoverage(BRANCH)).isInstanceOfSatisfying(Coverage.class, coverage -> { - assertThat(coverage.getCovered()).isEqualTo(0); - assertThat(coverage.getMissed()).isEqualTo(0); - }); - assertThat(action.getChangeCoverageDifference(LINE)).satisfies(coverage -> { - assertThat(coverage.getNumerator()).isEqualTo(-4250); - assertThat(coverage.getDenominator()).isEqualTo(1007); - }); - assertThat(action.hasChangeCoverageDifference(BRANCH)).isFalse(); + var builder = new CoverageBuilder(); + assertThat(action.getChangeCoverage(LINE)) + .isEqualTo(builder.setMetric(LINE).setCovered(1).setMissed(1).build()); + assertThat(action.getChangeCoverage(BRANCH)) + .isEqualTo(builder.setMetric(BRANCH).setCovered(0).setMissed(0).build()); + + assertThat(action.formatChangeCoverageOverview()).isEqualTo("2 lines (2 files) are affected"); + + assertThat(action.formatChangeCoverageDifference(LINE)).isEqualTo("-6.46%"); + assertThat(action.formatChangeCoverageDifference(BRANCH)).isEqualTo("-59.13%"); } /** @@ -204,12 +197,6 @@ private void verifyIndirectCoverageChanges(final CoverageBuildAction action) { assertThat(action.hasIndirectCoverageChanges(BRANCH)).isFalse(); } - /** - * Verifies the calculated code delta. - * - * @param action - * The created Jenkins action - */ private void verifyCodeDelta(final CoverageBuildAction action) { edu.hm.hafner.metric.Node root = action.getResult(); assertThat(root).isNotNull(); @@ -219,7 +206,7 @@ private void verifyCodeDelta(final CoverageBuildAction action) { .collect(Collectors.toList()); assertThat(changedFiles).hasSize(4); assertThat(changedFiles).extracting(FileNode::getName) - .containsExactly("MinerFactory.java", "RepositoryMinerStep.java", + .containsExactlyInAnyOrder("MinerFactory.java", "RepositoryMinerStep.java", "SimpleReferenceRecorder.java", "CommitDecoratorFactory.java"); assertThat(changedFiles).flatExtracting(FileNode::getChangedLines) .containsExactlyInAnyOrder(15, 17, 63, 68, 80, 90, 130); @@ -232,21 +219,19 @@ private void verifyCodeDelta(final CoverageBuildAction action) { * The node * @param commit * The processed commit - * @param jacocoXML + * @param fileName * The content of the processed JaCoCo report * * @return the created definition */ - private FlowDefinition createFlowDefinitionForCommit( - final String node, final String commit, final String jacocoXML) { + private FlowDefinition createPipelineForCommit(final String node, final String commit, final String fileName) { return new CpsFlowDefinition(node + " {" + " checkout([$class: 'GitSCM', " - + "branches: [[name: '" + commit + "' ]],\n" - + "userRemoteConfigs: [[url: '" + REPOSITORY + "']],\n" - + "extensions: [[$class: 'RelativeTargetDirectory', \n" - + " relativeTargetDir: 'checkout']]])\n" - + " publishCoverage adapters: [jacocoAdapter('" + jacocoXML - + "')], sourceFileResolver: sourceFiles('NEVER_STORE')\n" + + " branches: [[name: '" + commit + "' ]],\n" + + " userRemoteConfigs: [[url: '" + REPOSITORY + "']],\n" + + " extensions: [[$class: 'RelativeTargetDirectory', \n" + + " relativeTargetDir: 'checkout']]])\n" + + " recordCoverage tools: [[parser: 'JACOCO', pattern: '" + fileName + "']]\n" + "}", true); } @@ -279,7 +264,7 @@ private void setCoveragePublisherForFreeStyleProject(final FreeStyleProject proj * @param commit * The ID of the commit to be represented */ - private void setGitScmWithCommitSpecForFreeStyleProject(final FreeStyleProject project, final String commit) + private void configureGit(final FreeStyleProject project, final String commit) throws IOException { GitSCM scm = new GitSCM(GitSCM.createRepoList(REPOSITORY, null), Collections.singletonList(new BranchSpec(commit)), null, null, From f705035a4a20ab280f6d3713dbe200cdf2610022 Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Sun, 6 Nov 2022 23:32:51 +0100 Subject: [PATCH 28/29] Move new code to metrics package. --- plugin/pom.xml | 1 + .../plugins/coverage/CoverageAction.java | 5 +- .../ChangeCoverageTableModel.java | 4 +- .../CodeDeltaCalculator.java | 3 +- .../CodeDeltaException.java | 2 +- .../CoverageBuildAction.java | 6 +- .../{model => metrics}/CoverageFormatter.java | 2 +- .../{model => metrics}/CoverageJobAction.java | 4 +- .../CoveragePercentage.java | 2 +- .../coverage/metrics/CoverageRecorder.java | 2 - .../{model => metrics}/CoverageReporter.java | 7 +- .../CoverageTableModel.java | 13 +- .../coverage/metrics/CoverageTool.java | 1 - .../CoverageTreeCreator.java | 2 +- .../{model => metrics}/CoverageViewModel.java | 18 +- .../{model => metrics}/CoverageXmlStream.java | 3 +- .../FileChangesProcessor.java | 2 +- .../{model => metrics}/FilePathValidator.java | 2 +- .../IndirectCoverageChangesTable.java | 4 +- .../{model => metrics}/ModelValidation.java | 2 +- .../{model => metrics}/SourceViewModel.java | 4 +- .../{model => metrics}/package-info.java | 2 +- .../charts/CoverageSeriesBuilder.java | 4 +- .../charts/CoverageTrendChart.java | 4 +- .../visualization/code/PaintedNode.java | 2 +- .../visualization/code/SourceCodeFacade.java | 4 +- .../visualization/code/SourceCodePainter.java | 4 +- .../visualization/colorization/ColorId.java | 2 +- .../colorization/ColorProvider.java | 2 +- .../colorization/ColorProviderFactory.java | 45 ++-- .../colorization/ColorScheme.java | 2 +- .../colorization/CoverageChangeLevel.java | 4 +- .../colorization/CoverageChangeTendency.java | 4 +- .../colorization/CoverageColorJenkinsId.java | 2 +- .../colorization/CoverageColorPalette.java | 2 +- .../colorization/CoverageLevel.java | 4 +- .../colorization/package-info.java | 2 +- .../dashboard/ChangeCoverage.java | 12 +- .../dashboard/ChangeCoverageDelta.java | 12 +- .../dashboard/CoverageColumn.java | 12 +- .../dashboard/CoverageColumnType.java | 14 +- .../dashboard/IndirectCoverageChanges.java | 12 +- .../dashboard/ProjectCoverage.java | 12 +- .../dashboard/ProjectCoverageDelta.java | 12 +- .../visualization/dashboard/package-info.java | 2 +- .../visualization/package-info.java | 2 +- .../tree/TreeMapNodeConverter.java | 8 +- .../CoverageBuildAction/summary.jelly | 0 .../CoverageJobAction/floatingBox.jelly | 0 .../CoverageViewModel/index.jelly | 0 .../CoverageViewModel/index.properties | 0 .../coverage/metrics/Messages.properties | 31 +++ .../SourceViewModel/index.jelly | 0 .../SourceViewModel/index.properties | 0 .../dashboard/CoverageColumn/column.jelly | 0 .../CoverageColumn/columnHeader.jelly | 0 .../dashboard/CoverageColumn/config.jelly | 0 .../CoverageColumn/config.properties | 0 .../coverage/model/Messages.properties | 30 --- .../AbstractCoverageITest.java | 4 +- .../AbstractCoverageTest.java | 2 +- .../CodeDeltaCalculatorTest.java | 5 +- .../CoverageBuildActionTest.java | 6 +- .../CoverageJobActionTest.java | 2 +- .../CoveragePercentageTest.java | 24 +- .../CoveragePluginITest.java | 4 +- .../CoverageTreeCreatorTest.java | 2 +- .../CoverageViewModelTest.java | 6 +- .../CoverageXmlStreamTest.java | 2 +- .../DeltaComputationITest.java | 3 +- .../DockerAgentSourceCodeITest.java | 4 +- .../FileChangesProcessorTest.java | 2 +- .../FilePathValidatorTest.java | 4 +- .../{model => metrics}/GitForensicsITest.java | 2 +- .../LocalAgentSourceCodeITest.java | 2 +- .../PluginArchitectureTest.java | 4 +- .../{model => metrics}/SourceCodeITest.java | 2 +- .../TreeMapNodeConverterTest.java | 12 +- .../testutil/CoverageStubs.java | 8 +- .../{model => metrics}/testutil/JobStubs.java | 4 +- .../charts/CoverageSeriesBuilderTest.java | 6 +- .../code/SourceCodeFacadeTest.java | 2 +- .../ColorProviderFactoryTest.java | 4 +- .../colorization/ColorProviderTest.java | 4 +- .../colorization/CoverageChangeLevelTest.java | 4 +- .../CoverageChangeTendencyTest.java | 2 +- .../CoverageColorJenkinsIdTest.java | 4 +- .../colorization/CoverageLevelTest.java | 4 +- .../dashboard/ChangeCoverageDeltaTest.java | 10 +- .../dashboard/ChangeCoverageTest.java | 10 +- .../dashboard/CoverageColumnTest.java | 20 +- .../dashboard/CoverageColumnTypeTest.java | 14 +- .../IndirectCoverageChangesTest.java | 10 +- .../dashboard/ProjectCoverageDeltaTest.java | 10 +- .../dashboard/ProjectCoverageTest.java | 10 +- .../model/CoveragePlugin0vsOkITest.java | 184 --------------- .../FailBuildIfCoverageDecreasesITest.java | 139 ------------ .../coverage/model/HealthReportITest.java | 139 ------------ .../plugins/coverage/model/JobDslITest.java | 60 ----- .../model/MultipleInvocationsOfStepITest.java | 131 ----------- .../coverage/model/QualityGatesITest.java | 168 -------------- .../model/ReportAggregationITest.java | 212 ------------------ .../model/SkipPublishingOfChecksITest.java | 136 ----------- .../AcuCobolParser.java.txt | 0 .../cobertura-higher-coverage.xml | 0 .../cobertura-lots-of-data.xml | 0 .../cobertura-lower-coverage.xml | 0 .../{model => metrics}/cobertura-npe.xml | 0 .../{model => metrics}/column-dsl.yaml | 0 .../file-changes-test-after.xml | 0 .../file-changes-test-before.xml | 0 .../forensics_integration.xml | 0 .../forensics_integration_reference.xml | 0 .../jacoco-acu-cobol-parser.xml | 0 .../jacoco-analysis-model.xml | 0 .../{model => metrics}/jacoco-codingstyle.xml | 0 .../coverage/{model => metrics}/mutations.xml | 0 .../visualization/code/SourcecodeTest.html | 0 .../visualization/code/SourcecodeTestCC.html | 0 .../visualization/code/SourcecodeTestICC.html | 0 120 files changed, 272 insertions(+), 1452 deletions(-) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/ChangeCoverageTableModel.java (96%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/CodeDeltaCalculator.java (99%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model/exception => metrics}/CodeDeltaException.java (89%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/CoverageBuildAction.java (98%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/CoverageFormatter.java (98%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/CoverageJobAction.java (93%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/CoveragePercentage.java (99%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/CoverageReporter.java (97%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/CoverageTableModel.java (96%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/CoverageTreeCreator.java (99%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/CoverageViewModel.java (96%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/CoverageXmlStream.java (97%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/FileChangesProcessor.java (99%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/FilePathValidator.java (97%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/IndirectCoverageChangesTable.java (96%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/ModelValidation.java (99%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/SourceViewModel.java (93%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/package-info.java (86%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/charts/CoverageSeriesBuilder.java (91%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/charts/CoverageTrendChart.java (95%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/code/PaintedNode.java (95%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/code/SourceCodeFacade.java (99%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/code/SourceCodePainter.java (95%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/colorization/ColorId.java (81%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/colorization/ColorProvider.java (99%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/colorization/ColorProviderFactory.java (68%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/colorization/ColorScheme.java (85%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/colorization/CoverageChangeLevel.java (93%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/colorization/CoverageChangeTendency.java (89%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/colorization/CoverageColorJenkinsId.java (92%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/colorization/CoverageColorPalette.java (95%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/colorization/CoverageLevel.java (94%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/colorization/package-info.java (76%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/dashboard/ChangeCoverage.java (77%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/dashboard/ChangeCoverageDelta.java (74%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/dashboard/CoverageColumn.java (94%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/dashboard/CoverageColumnType.java (84%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/dashboard/IndirectCoverageChanges.java (77%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/dashboard/ProjectCoverage.java (75%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/dashboard/ProjectCoverageDelta.java (72%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/dashboard/package-info.java (77%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/package-info.java (77%) rename plugin/src/main/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/tree/TreeMapNodeConverter.java (88%) rename plugin/src/main/resources/io/jenkins/plugins/coverage/{model => metrics}/CoverageBuildAction/summary.jelly (100%) rename plugin/src/main/resources/io/jenkins/plugins/coverage/{model => metrics}/CoverageJobAction/floatingBox.jelly (100%) rename plugin/src/main/resources/io/jenkins/plugins/coverage/{model => metrics}/CoverageViewModel/index.jelly (100%) rename plugin/src/main/resources/io/jenkins/plugins/coverage/{model => metrics}/CoverageViewModel/index.properties (100%) rename plugin/src/main/resources/io/jenkins/plugins/coverage/{model => metrics}/SourceViewModel/index.jelly (100%) rename plugin/src/main/resources/io/jenkins/plugins/coverage/{model => metrics}/SourceViewModel/index.properties (100%) rename plugin/src/main/resources/io/jenkins/plugins/coverage/{model => metrics}/visualization/dashboard/CoverageColumn/column.jelly (100%) rename plugin/src/main/resources/io/jenkins/plugins/coverage/{model => metrics}/visualization/dashboard/CoverageColumn/columnHeader.jelly (100%) rename plugin/src/main/resources/io/jenkins/plugins/coverage/{model => metrics}/visualization/dashboard/CoverageColumn/config.jelly (100%) rename plugin/src/main/resources/io/jenkins/plugins/coverage/{model => metrics}/visualization/dashboard/CoverageColumn/config.properties (100%) delete mode 100644 plugin/src/main/resources/io/jenkins/plugins/coverage/model/Messages.properties rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/AbstractCoverageITest.java (96%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/AbstractCoverageTest.java (97%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/CodeDeltaCalculatorTest.java (98%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/CoverageBuildActionTest.java (98%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/CoverageJobActionTest.java (98%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/CoveragePercentageTest.java (72%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/CoveragePluginITest.java (98%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/CoverageTreeCreatorTest.java (99%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/CoverageViewModelTest.java (94%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/CoverageXmlStreamTest.java (95%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/DeltaComputationITest.java (98%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/DockerAgentSourceCodeITest.java (92%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/FileChangesProcessorTest.java (99%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/FilePathValidatorTest.java (95%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/GitForensicsITest.java (99%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/LocalAgentSourceCodeITest.java (96%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/PluginArchitectureTest.java (95%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/SourceCodeITest.java (99%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/TreeMapNodeConverterTest.java (87%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/testutil/CoverageStubs.java (96%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/testutil/JobStubs.java (94%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/charts/CoverageSeriesBuilderTest.java (94%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/code/SourceCodeFacadeTest.java (98%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/colorization/ColorProviderFactoryTest.java (94%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/colorization/ColorProviderTest.java (95%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/colorization/CoverageChangeLevelTest.java (93%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/colorization/CoverageChangeTendencyTest.java (94%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/colorization/CoverageColorJenkinsIdTest.java (71%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/colorization/CoverageLevelTest.java (90%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/dashboard/ChangeCoverageDeltaTest.java (80%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/dashboard/ChangeCoverageTest.java (79%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/dashboard/CoverageColumnTest.java (91%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/dashboard/CoverageColumnTypeTest.java (85%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/dashboard/IndirectCoverageChangesTest.java (80%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/dashboard/ProjectCoverageDeltaTest.java (80%) rename plugin/src/test/java/io/jenkins/plugins/coverage/{model => metrics}/visualization/dashboard/ProjectCoverageTest.java (79%) delete mode 100644 plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePlugin0vsOkITest.java delete mode 100644 plugin/src/test/java/io/jenkins/plugins/coverage/model/FailBuildIfCoverageDecreasesITest.java delete mode 100644 plugin/src/test/java/io/jenkins/plugins/coverage/model/HealthReportITest.java delete mode 100644 plugin/src/test/java/io/jenkins/plugins/coverage/model/JobDslITest.java delete mode 100644 plugin/src/test/java/io/jenkins/plugins/coverage/model/MultipleInvocationsOfStepITest.java delete mode 100644 plugin/src/test/java/io/jenkins/plugins/coverage/model/QualityGatesITest.java delete mode 100644 plugin/src/test/java/io/jenkins/plugins/coverage/model/ReportAggregationITest.java delete mode 100644 plugin/src/test/java/io/jenkins/plugins/coverage/model/SkipPublishingOfChecksITest.java rename plugin/src/test/resources/io/jenkins/plugins/coverage/{model => metrics}/AcuCobolParser.java.txt (100%) rename plugin/src/test/resources/io/jenkins/plugins/coverage/{model => metrics}/cobertura-higher-coverage.xml (100%) rename plugin/src/test/resources/io/jenkins/plugins/coverage/{model => metrics}/cobertura-lots-of-data.xml (100%) rename plugin/src/test/resources/io/jenkins/plugins/coverage/{model => metrics}/cobertura-lower-coverage.xml (100%) rename plugin/src/test/resources/io/jenkins/plugins/coverage/{model => metrics}/cobertura-npe.xml (100%) rename plugin/src/test/resources/io/jenkins/plugins/coverage/{model => metrics}/column-dsl.yaml (100%) rename plugin/src/test/resources/io/jenkins/plugins/coverage/{model => metrics}/file-changes-test-after.xml (100%) rename plugin/src/test/resources/io/jenkins/plugins/coverage/{model => metrics}/file-changes-test-before.xml (100%) rename plugin/src/test/resources/io/jenkins/plugins/coverage/{model => metrics}/forensics_integration.xml (100%) rename plugin/src/test/resources/io/jenkins/plugins/coverage/{model => metrics}/forensics_integration_reference.xml (100%) rename plugin/src/test/resources/io/jenkins/plugins/coverage/{model => metrics}/jacoco-acu-cobol-parser.xml (100%) rename plugin/src/test/resources/io/jenkins/plugins/coverage/{model => metrics}/jacoco-analysis-model.xml (100%) rename plugin/src/test/resources/io/jenkins/plugins/coverage/{model => metrics}/jacoco-codingstyle.xml (100%) rename plugin/src/test/resources/io/jenkins/plugins/coverage/{model => metrics}/mutations.xml (100%) rename plugin/src/test/resources/io/jenkins/plugins/coverage/{model => metrics}/visualization/code/SourcecodeTest.html (100%) rename plugin/src/test/resources/io/jenkins/plugins/coverage/{model => metrics}/visualization/code/SourcecodeTestCC.html (100%) rename plugin/src/test/resources/io/jenkins/plugins/coverage/{model => metrics}/visualization/code/SourcecodeTestICC.html (100%) diff --git a/plugin/pom.xml b/plugin/pom.xml index 2c16a7333..c97ba29b6 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -314,6 +314,7 @@ assertj-assertions-generator-maven-plugin + io.jenkins.plugins.coverage.metrics io.jenkins.plugins.coverage.model diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/CoverageAction.java b/plugin/src/main/java/io/jenkins/plugins/coverage/CoverageAction.java index 76a88a911..10c8ac1f0 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/CoverageAction.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/CoverageAction.java @@ -5,9 +5,10 @@ import java.util.Collection; import java.util.Collections; +import org.apache.commons.lang.StringUtils; + import edu.umd.cs.findbugs.annotations.CheckForNull; -import org.apache.commons.lang.StringUtils; import org.kohsuke.stapler.StaplerProxy; import hudson.model.Action; import hudson.model.HealthReport; @@ -16,7 +17,7 @@ import jenkins.model.RunAction2; import jenkins.tasks.SimpleBuildStep; -import io.jenkins.plugins.coverage.model.CoverageBuildAction; +import io.jenkins.plugins.coverage.metrics.CoverageBuildAction; import io.jenkins.plugins.coverage.targets.CoverageResult; public class CoverageAction implements StaplerProxy, SimpleBuildStep.LastBuildAction, RunAction2, HealthReportingAction { diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/ChangeCoverageTableModel.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/ChangeCoverageTableModel.java similarity index 96% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/ChangeCoverageTableModel.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/ChangeCoverageTableModel.java index a6f504621..0c7198b0f 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/ChangeCoverageTableModel.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/ChangeCoverageTableModel.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import java.util.List; import java.util.Locale; @@ -12,7 +12,7 @@ import hudson.Functions; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProvider; import io.jenkins.plugins.datatables.DetailedCell; import io.jenkins.plugins.datatables.TableConfiguration; import io.jenkins.plugins.datatables.TableConfiguration.SelectStyle; diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CodeDeltaCalculator.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CodeDeltaCalculator.java similarity index 99% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/CodeDeltaCalculator.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CodeDeltaCalculator.java index a62918d97..25458c65e 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CodeDeltaCalculator.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CodeDeltaCalculator.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import java.util.Collection; import java.util.Comparator; @@ -21,7 +21,6 @@ import hudson.model.Run; import hudson.model.TaskListener; -import io.jenkins.plugins.coverage.model.exception.CodeDeltaException; import io.jenkins.plugins.forensics.delta.DeltaCalculatorFactory; import io.jenkins.plugins.forensics.delta.model.Delta; import io.jenkins.plugins.forensics.delta.model.FileChanges; diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/exception/CodeDeltaException.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CodeDeltaException.java similarity index 89% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/exception/CodeDeltaException.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CodeDeltaException.java index acfa924e5..bcd4bd586 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/exception/CodeDeltaException.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CodeDeltaException.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model.exception; +package io.jenkins.plugins.coverage.metrics; /** * Exception which is thrown when preprocessing the code delta failed. diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageBuildAction.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageBuildAction.java similarity index 98% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageBuildAction.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageBuildAction.java index c0a3f67cc..ce6ab1b8d 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageBuildAction.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageBuildAction.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import java.util.Collection; import java.util.Map; @@ -27,8 +27,8 @@ import hudson.model.HealthReportingAction; import hudson.model.Run; -import io.jenkins.plugins.coverage.model.CoverageViewModel.ValueLabelProvider; -import io.jenkins.plugins.coverage.model.CoverageXmlStream.FractionConverter; +import io.jenkins.plugins.coverage.metrics.CoverageViewModel.ValueLabelProvider; +import io.jenkins.plugins.coverage.metrics.CoverageXmlStream.FractionConverter; import io.jenkins.plugins.forensics.reference.ReferenceBuild; import io.jenkins.plugins.util.AbstractXmlStream; import io.jenkins.plugins.util.BuildAction; diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageFormatter.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageFormatter.java similarity index 98% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageFormatter.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageFormatter.java index c981facca..9f91cb79d 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageFormatter.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageFormatter.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import java.util.Locale; diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageJobAction.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageJobAction.java similarity index 93% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageJobAction.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageJobAction.java index 14eb0df1a..f9cd9fc44 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageJobAction.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageJobAction.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import edu.hm.hafner.echarts.BuildResult; import edu.hm.hafner.echarts.ChartModelConfiguration; @@ -9,7 +9,7 @@ import hudson.model.Job; import io.jenkins.plugins.coverage.Messages; -import io.jenkins.plugins.coverage.model.visualization.charts.CoverageTrendChart; +import io.jenkins.plugins.coverage.metrics.visualization.charts.CoverageTrendChart; import io.jenkins.plugins.echarts.AsyncConfigurableTrendJobAction; /** diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoveragePercentage.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoveragePercentage.java similarity index 99% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/CoveragePercentage.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoveragePercentage.java index b1387a873..c28615866 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoveragePercentage.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoveragePercentage.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import java.io.Serializable; import java.util.Locale; diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageRecorder.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageRecorder.java index 32c2f8f1a..3de1adb56 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageRecorder.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageRecorder.java @@ -39,8 +39,6 @@ import jenkins.tasks.SimpleBuildStep; import io.jenkins.plugins.coverage.metrics.CoverageTool.CoverageParser; -import io.jenkins.plugins.coverage.model.CoverageBuildAction; -import io.jenkins.plugins.coverage.model.CoverageReporter; import io.jenkins.plugins.prism.CharsetValidation; import io.jenkins.plugins.prism.SourceCodeDirectory; import io.jenkins.plugins.prism.SourceCodeRetention; diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageReporter.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageReporter.java similarity index 97% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageReporter.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageReporter.java index a1e5c5e70..2bdfe1a7a 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageReporter.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageReporter.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import java.util.HashSet; import java.util.Map; @@ -21,8 +21,7 @@ import hudson.model.Run; import hudson.model.TaskListener; -import io.jenkins.plugins.coverage.model.exception.CodeDeltaException; -import io.jenkins.plugins.coverage.model.visualization.code.SourceCodePainter; +import io.jenkins.plugins.coverage.metrics.visualization.code.SourceCodePainter; import io.jenkins.plugins.coverage.targets.CoveragePaint; import io.jenkins.plugins.forensics.delta.model.Delta; import io.jenkins.plugins.forensics.delta.model.FileChanges; @@ -30,7 +29,7 @@ import io.jenkins.plugins.prism.SourceCodeRetention; import io.jenkins.plugins.util.LogHandler; -import static io.jenkins.plugins.coverage.model.FilePathValidator.*; +import static io.jenkins.plugins.coverage.metrics.FilePathValidator.*; /** * Transforms the old model to the new model and invokes all steps that work on the new model. Currently, only the diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTableModel.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageTableModel.java similarity index 96% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTableModel.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageTableModel.java index c33b8ffd9..2ac86075e 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTableModel.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageTableModel.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import java.io.File; import java.util.ArrayList; @@ -16,10 +16,10 @@ import hudson.Functions; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors; -import io.jenkins.plugins.coverage.model.visualization.colorization.CoverageChangeTendency; -import io.jenkins.plugins.coverage.model.visualization.colorization.CoverageLevel; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProvider; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProvider.DisplayColors; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.CoverageChangeTendency; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.CoverageLevel; import io.jenkins.plugins.datatables.DetailedCell; import io.jenkins.plugins.datatables.TableColumn; import io.jenkins.plugins.datatables.TableColumn.ColumnBuilder; @@ -128,7 +128,8 @@ public List getColumns() { .withResponsivePriority(1) .build(); columns.add(branchCoverage); - TableColumn branchCoverageDelta = new ColumnBuilder().withHeaderLabel(Messages.Column_DeltaBranchCoverage("Δ")) + TableColumn branchCoverageDelta = new ColumnBuilder().withHeaderLabel( + Messages.Column_DeltaBranchCoverage("Δ")) .withDataPropertyKey("branchCoverageDelta") .withDetailedCell() .withType(ColumnType.NUMBER) diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageTool.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageTool.java index 80c0add26..9f49452cd 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageTool.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageTool.java @@ -27,7 +27,6 @@ import hudson.util.FormValidation; import hudson.util.ListBoxModel; -import io.jenkins.plugins.coverage.model.ModelValidation; import io.jenkins.plugins.prism.SourceCodeRetention; import io.jenkins.plugins.util.JenkinsFacade; diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTreeCreator.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageTreeCreator.java similarity index 99% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTreeCreator.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageTreeCreator.java index 20d4471bb..ef47dc246 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageTreeCreator.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageTreeCreator.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import java.util.Iterator; import java.util.Map; diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageViewModel.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageViewModel.java similarity index 96% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageViewModel.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageViewModel.java index c1a8ee6df..a212e4cbb 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageViewModel.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageViewModel.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import java.io.File; import java.io.IOException; @@ -37,14 +37,14 @@ import hudson.model.Run; import hudson.util.TextFile; -import io.jenkins.plugins.coverage.model.CoverageTableModel.InlineRowRenderer; -import io.jenkins.plugins.coverage.model.CoverageTableModel.LinkedRowRenderer; -import io.jenkins.plugins.coverage.model.CoverageTableModel.RowRenderer; -import io.jenkins.plugins.coverage.model.visualization.code.SourceCodeFacade; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProviderFactory; -import io.jenkins.plugins.coverage.model.visualization.colorization.CoverageColorJenkinsId; -import io.jenkins.plugins.coverage.model.visualization.tree.TreeMapNodeConverter; +import io.jenkins.plugins.coverage.metrics.CoverageTableModel.InlineRowRenderer; +import io.jenkins.plugins.coverage.metrics.CoverageTableModel.LinkedRowRenderer; +import io.jenkins.plugins.coverage.metrics.CoverageTableModel.RowRenderer; +import io.jenkins.plugins.coverage.metrics.visualization.code.SourceCodeFacade; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProvider; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProviderFactory; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.CoverageColorJenkinsId; +import io.jenkins.plugins.coverage.metrics.visualization.tree.TreeMapNodeConverter; import io.jenkins.plugins.datatables.DefaultAsyncTableContentProvider; import io.jenkins.plugins.datatables.TableModel; import io.jenkins.plugins.util.BuildResultNavigator; diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageXmlStream.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageXmlStream.java similarity index 97% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageXmlStream.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageXmlStream.java index 06713dd72..1a2e452f8 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/CoverageXmlStream.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/CoverageXmlStream.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import org.apache.commons.lang3.math.Fraction; @@ -48,7 +48,6 @@ protected void configureXStream(final XStream2 xStream) { xStream.alias("mutation", MutationValue.class); xStream.alias("complexity", CyclomaticComplexity.class); xStream.alias("loc", LinesOfCode.class); - xStream.alias("percentage", CoveragePercentage.class); xStream.addImmutableType(Metric.class, false); xStream.addImmutableType(Coverage.class, false); xStream.addImmutableType(LinesOfCode.class, false); diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/FileChangesProcessor.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/FileChangesProcessor.java similarity index 99% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/FileChangesProcessor.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/FileChangesProcessor.java index b9e722827..c0a6a99cc 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/FileChangesProcessor.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/FileChangesProcessor.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import java.util.ArrayList; import java.util.Collection; diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/FilePathValidator.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/FilePathValidator.java similarity index 97% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/FilePathValidator.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/FilePathValidator.java index 1d3da6796..fc551b43e 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/FilePathValidator.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/FilePathValidator.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import java.util.List; import java.util.stream.Collectors; diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/IndirectCoverageChangesTable.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/IndirectCoverageChangesTable.java similarity index 96% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/IndirectCoverageChangesTable.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/IndirectCoverageChangesTable.java index bdfbf1f46..fa81f1bb9 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/IndirectCoverageChangesTable.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/IndirectCoverageChangesTable.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import java.util.List; import java.util.Locale; @@ -11,7 +11,7 @@ import hudson.Functions; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProvider; import io.jenkins.plugins.datatables.DetailedCell; /** diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/ModelValidation.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/ModelValidation.java similarity index 99% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/ModelValidation.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/ModelValidation.java index d75dc5988..af577090a 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/ModelValidation.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/ModelValidation.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import java.io.IOException; import java.nio.charset.Charset; diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/SourceViewModel.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/SourceViewModel.java similarity index 93% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/SourceViewModel.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/SourceViewModel.java index 3d36402e0..4e58e6e4f 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/SourceViewModel.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/SourceViewModel.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import java.io.File; import java.io.IOException; @@ -10,7 +10,7 @@ import hudson.model.Run; import hudson.util.TextFile; -import io.jenkins.plugins.coverage.model.visualization.code.SourceCodeFacade; +import io.jenkins.plugins.coverage.metrics.visualization.code.SourceCodeFacade; /** * Server side model that provides the data for the source code view of the coverage results. The layout of the diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/package-info.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/package-info.java similarity index 86% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/package-info.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/package-info.java index 3fa6f60bf..dded77028 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/package-info.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/package-info.java @@ -4,7 +4,7 @@ * @author Ullrich Hafner */ @DefaultAnnotation(NonNull.class) -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import edu.umd.cs.findbugs.annotations.DefaultAnnotation; import edu.umd.cs.findbugs.annotations.NonNull; diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/charts/CoverageSeriesBuilder.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/charts/CoverageSeriesBuilder.java similarity index 91% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/charts/CoverageSeriesBuilder.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/charts/CoverageSeriesBuilder.java index 0ea757cd8..4e435268b 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/charts/CoverageSeriesBuilder.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/charts/CoverageSeriesBuilder.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model.visualization.charts; +package io.jenkins.plugins.coverage.metrics.visualization.charts; import java.util.HashMap; import java.util.Map; @@ -8,7 +8,7 @@ import edu.hm.hafner.echarts.SeriesBuilder; import edu.hm.hafner.metric.Coverage; -import io.jenkins.plugins.coverage.model.CoverageBuildAction; +import io.jenkins.plugins.coverage.metrics.CoverageBuildAction; /** * Builds one x-axis point for the series of a line chart showing the line and branch coverage of a project. diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/charts/CoverageTrendChart.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/charts/CoverageTrendChart.java similarity index 95% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/charts/CoverageTrendChart.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/charts/CoverageTrendChart.java index 27435c22a..a139d5ad7 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/charts/CoverageTrendChart.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/charts/CoverageTrendChart.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model.visualization.charts; +package io.jenkins.plugins.coverage.metrics.visualization.charts; import edu.hm.hafner.echarts.BuildResult; import edu.hm.hafner.echarts.ChartModelConfiguration; @@ -10,7 +10,7 @@ import edu.hm.hafner.echarts.LinesDataSet; import edu.hm.hafner.echarts.Palette; -import io.jenkins.plugins.coverage.model.CoverageBuildAction; +import io.jenkins.plugins.coverage.metrics.CoverageBuildAction; /** * Builds the Java side model for a trend chart showing the line and branch coverage of a project. diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/code/PaintedNode.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/code/PaintedNode.java similarity index 95% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/code/PaintedNode.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/code/PaintedNode.java index d4a9a0ac7..c18a4654f 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/code/PaintedNode.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/code/PaintedNode.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model.visualization.code; +package io.jenkins.plugins.coverage.metrics.visualization.code; import java.io.Serializable; import java.util.Arrays; diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodeFacade.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/code/SourceCodeFacade.java similarity index 99% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodeFacade.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/code/SourceCodeFacade.java index 1cc815e41..85ecbc1ff 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodeFacade.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/code/SourceCodeFacade.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model.visualization.code; +package io.jenkins.plugins.coverage.metrics.visualization.code; import java.io.BufferedWriter; import java.io.File; @@ -37,7 +37,7 @@ import hudson.util.TextFile; import jenkins.MasterToSlaveFileCallable; -import io.jenkins.plugins.coverage.model.CoverageFormatter; +import io.jenkins.plugins.coverage.metrics.CoverageFormatter; import io.jenkins.plugins.prism.CharsetValidation; import io.jenkins.plugins.prism.FilePermissionEnforcer; import io.jenkins.plugins.prism.SourceDirectoryFilter; diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodePainter.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/code/SourceCodePainter.java similarity index 95% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodePainter.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/code/SourceCodePainter.java index 18aac307c..80192ef7c 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodePainter.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/code/SourceCodePainter.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model.visualization.code; +package io.jenkins.plugins.coverage.metrics.visualization.code; import java.io.IOException; import java.util.List; @@ -12,7 +12,7 @@ import hudson.FilePath; import hudson.model.Run; -import io.jenkins.plugins.coverage.model.visualization.code.SourceCodeFacade.AgentCoveragePainter; +import io.jenkins.plugins.coverage.metrics.visualization.code.SourceCodeFacade.AgentCoveragePainter; import io.jenkins.plugins.prism.PermittedSourceCodeDirectory; import io.jenkins.plugins.prism.PrismConfiguration; import io.jenkins.plugins.prism.SourceCodeRetention; diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/ColorId.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/ColorId.java similarity index 81% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/ColorId.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/ColorId.java index a45978a81..95eb6015f 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/ColorId.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/ColorId.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model.visualization.colorization; +package io.jenkins.plugins.coverage.metrics.visualization.colorization; /** * Provides IDs for colors which are used within this plugin in order to separate the color palette from the logic. diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/ColorProvider.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/ColorProvider.java similarity index 99% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/ColorProvider.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/ColorProvider.java index a721534e6..7d9890615 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/ColorProvider.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/ColorProvider.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model.visualization.colorization; +package io.jenkins.plugins.coverage.metrics.visualization.colorization; import java.awt.*; import java.util.HashMap; diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/ColorProviderFactory.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/ColorProviderFactory.java similarity index 68% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/ColorProviderFactory.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/ColorProviderFactory.java index 4fec5e125..99e6be8f7 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/ColorProviderFactory.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/ColorProviderFactory.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model.visualization.colorization; +package io.jenkins.plugins.coverage.metrics.visualization.colorization; import java.awt.*; import java.util.Arrays; @@ -8,10 +8,7 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors; - -import static io.jenkins.plugins.coverage.model.visualization.colorization.ColorId.*; -import static io.jenkins.plugins.coverage.model.visualization.colorization.CoverageColorJenkinsId.*; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProvider.DisplayColors; /** * Provides factory methods for creating different {@link ColorProvider color providers}. @@ -46,29 +43,29 @@ public static ColorProvider createDefaultColorProvider() { * @return the created color provider */ public static ColorProvider createColorProvider(final Map colors) { - if (!colors.keySet().equals(getAll()) || !verifyHexCodes(colors.values())) { + if (!colors.keySet().equals(CoverageColorJenkinsId.getAll()) || !verifyHexCodes(colors.values())) { return createDefaultColorProvider(); } Map colorMap = new HashMap<>(); // TODO: use dynamic text color (not provided yet) - colorMap.put(INSUFFICIENT, - createDisplayColor(colors.get(RED.getJenkinsColorId()), "#ffffff")); - colorMap.put(VERY_BAD, - createDisplayColor(colors.get(LIGHT_RED.getJenkinsColorId()), "#ffffff")); - colorMap.put(BAD, - createDisplayColor(colors.get(ORANGE.getJenkinsColorId()), "#000000")); - colorMap.put(INADEQUATE, - createDisplayColor(colors.get(LIGHT_ORANGE.getJenkinsColorId()), "#000000")); - colorMap.put(AVERAGE, - createDisplayColor(colors.get(YELLOW.getJenkinsColorId()), "#000000")); - colorMap.put(GOOD, - createDisplayColor(colors.get(LIGHT_YELLOW.getJenkinsColorId()), "#000000")); - colorMap.put(VERY_GOOD, - createDisplayColor(colors.get(LIGHT_GREEN.getJenkinsColorId()), "#000000")); - colorMap.put(EXCELLENT, - createDisplayColor(colors.get(GREEN.getJenkinsColorId()), "#ffffff")); - colorMap.put(BLACK, createDisplayColor(CoverageColorPalette.BLACK)); - colorMap.put(WHITE, createDisplayColor(CoverageColorPalette.WHITE)); + colorMap.put(ColorId.INSUFFICIENT, + createDisplayColor(colors.get(CoverageColorJenkinsId.RED.getJenkinsColorId()), "#ffffff")); + colorMap.put(ColorId.VERY_BAD, + createDisplayColor(colors.get(CoverageColorJenkinsId.LIGHT_RED.getJenkinsColorId()), "#ffffff")); + colorMap.put(ColorId.BAD, + createDisplayColor(colors.get(CoverageColorJenkinsId.ORANGE.getJenkinsColorId()), "#000000")); + colorMap.put(ColorId.INADEQUATE, + createDisplayColor(colors.get(CoverageColorJenkinsId.LIGHT_ORANGE.getJenkinsColorId()), "#000000")); + colorMap.put(ColorId.AVERAGE, + createDisplayColor(colors.get(CoverageColorJenkinsId.YELLOW.getJenkinsColorId()), "#000000")); + colorMap.put(ColorId.GOOD, + createDisplayColor(colors.get(CoverageColorJenkinsId.LIGHT_YELLOW.getJenkinsColorId()), "#000000")); + colorMap.put(ColorId.VERY_GOOD, + createDisplayColor(colors.get(CoverageColorJenkinsId.LIGHT_GREEN.getJenkinsColorId()), "#000000")); + colorMap.put(ColorId.EXCELLENT, + createDisplayColor(colors.get(CoverageColorJenkinsId.GREEN.getJenkinsColorId()), "#ffffff")); + colorMap.put(ColorId.BLACK, createDisplayColor(CoverageColorPalette.BLACK)); + colorMap.put(ColorId.WHITE, createDisplayColor(CoverageColorPalette.WHITE)); return new ColorProvider(colorMap); } diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/ColorScheme.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/ColorScheme.java similarity index 85% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/ColorScheme.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/ColorScheme.java index 26f43f9f1..1e246cf6c 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/ColorScheme.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/ColorScheme.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model.visualization.colorization; +package io.jenkins.plugins.coverage.metrics.visualization.colorization; /** * Represents different types of color schemes that can be selected in order to load the matching color palette. diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageChangeLevel.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/CoverageChangeLevel.java similarity index 93% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageChangeLevel.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/CoverageChangeLevel.java index ecc01b28f..4ef41d6e9 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageChangeLevel.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/CoverageChangeLevel.java @@ -1,8 +1,8 @@ -package io.jenkins.plugins.coverage.model.visualization.colorization; +package io.jenkins.plugins.coverage.metrics.visualization.colorization; import edu.umd.cs.findbugs.annotations.NonNull; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProvider.DisplayColors; /** * Provides the colorization for different coverage change levels. diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageChangeTendency.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/CoverageChangeTendency.java similarity index 89% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageChangeTendency.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/CoverageChangeTendency.java index 6c047dcd3..2c5c8d7dc 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageChangeTendency.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/CoverageChangeTendency.java @@ -1,8 +1,8 @@ -package io.jenkins.plugins.coverage.model.visualization.colorization; +package io.jenkins.plugins.coverage.metrics.visualization.colorization; import edu.umd.cs.findbugs.annotations.NonNull; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProvider.DisplayColors; /** * Provides the colorization for different coverage change tendencies. diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageColorJenkinsId.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/CoverageColorJenkinsId.java similarity index 92% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageColorJenkinsId.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/CoverageColorJenkinsId.java index 64971c27a..7b1d1ca33 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageColorJenkinsId.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/CoverageColorJenkinsId.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model.visualization.colorization; +package io.jenkins.plugins.coverage.metrics.visualization.colorization; import java.util.Arrays; import java.util.Set; diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageColorPalette.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/CoverageColorPalette.java similarity index 95% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageColorPalette.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/CoverageColorPalette.java index 8ed5ef22a..7aaf599d3 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageColorPalette.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/CoverageColorPalette.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model.visualization.colorization; +package io.jenkins.plugins.coverage.metrics.visualization.colorization; import java.awt.*; diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageLevel.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/CoverageLevel.java similarity index 94% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageLevel.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/CoverageLevel.java index a09024a65..cb910c538 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageLevel.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/CoverageLevel.java @@ -1,8 +1,8 @@ -package io.jenkins.plugins.coverage.model.visualization.colorization; +package io.jenkins.plugins.coverage.metrics.visualization.colorization; import edu.umd.cs.findbugs.annotations.NonNull; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProvider.DisplayColors; /** * Provides the colorization for different coverage levels. diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/package-info.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/package-info.java similarity index 76% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/package-info.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/package-info.java index 6bbd102dd..8384163e5 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/colorization/package-info.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/package-info.java @@ -2,7 +2,7 @@ * Provides colors and operations on them to be used in order to visualize coverage. */ @DefaultAnnotation(NonNull.class) -package io.jenkins.plugins.coverage.model.visualization.colorization; +package io.jenkins.plugins.coverage.metrics.visualization.colorization; import edu.umd.cs.findbugs.annotations.DefaultAnnotation; import edu.umd.cs.findbugs.annotations.NonNull; diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ChangeCoverage.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/ChangeCoverage.java similarity index 77% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ChangeCoverage.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/ChangeCoverage.java index e75a60815..86eb6977d 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ChangeCoverage.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/ChangeCoverage.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model.visualization.dashboard; +package io.jenkins.plugins.coverage.metrics.visualization.dashboard; import java.util.Locale; import java.util.Optional; @@ -7,11 +7,11 @@ import edu.hm.hafner.metric.Metric; import edu.hm.hafner.metric.Value; -import io.jenkins.plugins.coverage.model.CoverageBuildAction; -import io.jenkins.plugins.coverage.model.CoveragePercentage; -import io.jenkins.plugins.coverage.model.Messages; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors; -import io.jenkins.plugins.coverage.model.visualization.colorization.CoverageLevel; +import io.jenkins.plugins.coverage.metrics.CoverageBuildAction; +import io.jenkins.plugins.coverage.metrics.CoveragePercentage; +import io.jenkins.plugins.coverage.metrics.Messages; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProvider.DisplayColors; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.CoverageLevel; /** * Concrete implementation of {@link CoverageColumnType} which represents the change coverage. diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ChangeCoverageDelta.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/ChangeCoverageDelta.java similarity index 74% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ChangeCoverageDelta.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/ChangeCoverageDelta.java index a02f02f99..6f24acac0 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ChangeCoverageDelta.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/ChangeCoverageDelta.java @@ -1,15 +1,15 @@ -package io.jenkins.plugins.coverage.model.visualization.dashboard; +package io.jenkins.plugins.coverage.metrics.visualization.dashboard; import java.util.Locale; import java.util.Optional; import edu.hm.hafner.metric.Metric; -import io.jenkins.plugins.coverage.model.CoverageBuildAction; -import io.jenkins.plugins.coverage.model.CoveragePercentage; -import io.jenkins.plugins.coverage.model.Messages; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors; -import io.jenkins.plugins.coverage.model.visualization.colorization.CoverageChangeTendency; +import io.jenkins.plugins.coverage.metrics.CoverageBuildAction; +import io.jenkins.plugins.coverage.metrics.CoveragePercentage; +import io.jenkins.plugins.coverage.metrics.Messages; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProvider.DisplayColors; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.CoverageChangeTendency; /** * Concrete implementation of {@link CoverageColumnType} which represents the change coverage delta. diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumn.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/CoverageColumn.java similarity index 94% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumn.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/CoverageColumn.java index 7a2468f60..177858c55 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumn.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/CoverageColumn.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model.visualization.dashboard; +package io.jenkins.plugins.coverage.metrics.visualization.dashboard; import java.util.Optional; import java.util.regex.Pattern; @@ -18,11 +18,11 @@ import hudson.views.ListViewColumnDescriptor; import jenkins.model.Jenkins; -import io.jenkins.plugins.coverage.model.CoverageBuildAction; -import io.jenkins.plugins.coverage.model.CoveragePercentage; -import io.jenkins.plugins.coverage.model.Messages; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors; +import io.jenkins.plugins.coverage.metrics.CoverageBuildAction; +import io.jenkins.plugins.coverage.metrics.CoveragePercentage; +import io.jenkins.plugins.coverage.metrics.Messages; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProvider; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProvider.DisplayColors; import io.jenkins.plugins.util.JenkinsFacade; /** diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumnType.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/CoverageColumnType.java similarity index 84% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumnType.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/CoverageColumnType.java index 9af1e476b..359887546 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumnType.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/CoverageColumnType.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model.visualization.dashboard; +package io.jenkins.plugins.coverage.metrics.visualization.dashboard; import java.util.Arrays; import java.util.List; @@ -9,12 +9,12 @@ import org.jvnet.localizer.Localizable; -import io.jenkins.plugins.coverage.model.CoverageBuildAction; -import io.jenkins.plugins.coverage.model.CoveragePercentage; -import io.jenkins.plugins.coverage.model.Messages; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProviderFactory; +import io.jenkins.plugins.coverage.metrics.CoverageBuildAction; +import io.jenkins.plugins.coverage.metrics.CoveragePercentage; +import io.jenkins.plugins.coverage.metrics.Messages; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProvider; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProvider.DisplayColors; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProviderFactory; /** * Provides functions for different types of coverage that can be represented within a {@link CoverageColumn}. diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/IndirectCoverageChanges.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/IndirectCoverageChanges.java similarity index 77% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/IndirectCoverageChanges.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/IndirectCoverageChanges.java index 6a0bcfe23..b397c4487 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/IndirectCoverageChanges.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/IndirectCoverageChanges.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model.visualization.dashboard; +package io.jenkins.plugins.coverage.metrics.visualization.dashboard; import java.util.Locale; import java.util.Optional; @@ -7,11 +7,11 @@ import edu.hm.hafner.metric.Metric; import edu.hm.hafner.metric.Value; -import io.jenkins.plugins.coverage.model.CoverageBuildAction; -import io.jenkins.plugins.coverage.model.CoveragePercentage; -import io.jenkins.plugins.coverage.model.Messages; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors; -import io.jenkins.plugins.coverage.model.visualization.colorization.CoverageLevel; +import io.jenkins.plugins.coverage.metrics.CoverageBuildAction; +import io.jenkins.plugins.coverage.metrics.CoveragePercentage; +import io.jenkins.plugins.coverage.metrics.Messages; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProvider.DisplayColors; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.CoverageLevel; /** * Concrete implementation of {@link CoverageColumnType} which represents the indirect coverage changes. diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ProjectCoverage.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/ProjectCoverage.java similarity index 75% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ProjectCoverage.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/ProjectCoverage.java index 953b30190..e32a64848 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ProjectCoverage.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/ProjectCoverage.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model.visualization.dashboard; +package io.jenkins.plugins.coverage.metrics.visualization.dashboard; import java.util.Locale; import java.util.Optional; @@ -7,11 +7,11 @@ import edu.hm.hafner.metric.Metric; import edu.hm.hafner.metric.Value; -import io.jenkins.plugins.coverage.model.CoverageBuildAction; -import io.jenkins.plugins.coverage.model.CoveragePercentage; -import io.jenkins.plugins.coverage.model.Messages; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors; -import io.jenkins.plugins.coverage.model.visualization.colorization.CoverageLevel; +import io.jenkins.plugins.coverage.metrics.CoverageBuildAction; +import io.jenkins.plugins.coverage.metrics.CoveragePercentage; +import io.jenkins.plugins.coverage.metrics.Messages; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProvider.DisplayColors; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.CoverageLevel; /** * Concrete implementation of {@link CoverageColumnType} which represents the project coverage. diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ProjectCoverageDelta.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/ProjectCoverageDelta.java similarity index 72% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ProjectCoverageDelta.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/ProjectCoverageDelta.java index 4667fcafc..6609ade1c 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ProjectCoverageDelta.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/ProjectCoverageDelta.java @@ -1,15 +1,15 @@ -package io.jenkins.plugins.coverage.model.visualization.dashboard; +package io.jenkins.plugins.coverage.metrics.visualization.dashboard; import java.util.Locale; import java.util.Optional; import edu.hm.hafner.metric.Metric; -import io.jenkins.plugins.coverage.model.CoverageBuildAction; -import io.jenkins.plugins.coverage.model.CoveragePercentage; -import io.jenkins.plugins.coverage.model.Messages; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors; -import io.jenkins.plugins.coverage.model.visualization.colorization.CoverageChangeTendency; +import io.jenkins.plugins.coverage.metrics.CoverageBuildAction; +import io.jenkins.plugins.coverage.metrics.CoveragePercentage; +import io.jenkins.plugins.coverage.metrics.Messages; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProvider.DisplayColors; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.CoverageChangeTendency; /** * Concrete implementation of {@link CoverageColumnType} which represents the project coverage delta. diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/package-info.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/package-info.java similarity index 77% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/package-info.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/package-info.java index fd1e99e58..49a34b0e8 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/dashboard/package-info.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/package-info.java @@ -2,7 +2,7 @@ * Contains logic and models for visualizing code coverage within a dashboard column. */ @DefaultAnnotation(NonNull.class) -package io.jenkins.plugins.coverage.model.visualization.dashboard; +package io.jenkins.plugins.coverage.metrics.visualization.dashboard; import edu.umd.cs.findbugs.annotations.DefaultAnnotation; import edu.umd.cs.findbugs.annotations.NonNull; diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/package-info.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/package-info.java similarity index 77% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/package-info.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/package-info.java index 859cd16b3..3bcf5b56a 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/package-info.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/package-info.java @@ -2,7 +2,7 @@ * Contains logic and models for visualizing code coverage. */ @DefaultAnnotation(NonNull.class) -package io.jenkins.plugins.coverage.model.visualization; +package io.jenkins.plugins.coverage.metrics.visualization; import edu.umd.cs.findbugs.annotations.DefaultAnnotation; import edu.umd.cs.findbugs.annotations.NonNull; diff --git a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/tree/TreeMapNodeConverter.java b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/tree/TreeMapNodeConverter.java similarity index 88% rename from plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/tree/TreeMapNodeConverter.java rename to plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/tree/TreeMapNodeConverter.java index b9ca5fb7e..84cc56e93 100644 --- a/plugin/src/main/java/io/jenkins/plugins/coverage/model/visualization/tree/TreeMapNodeConverter.java +++ b/plugin/src/main/java/io/jenkins/plugins/coverage/metrics/visualization/tree/TreeMapNodeConverter.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model.visualization.tree; +package io.jenkins.plugins.coverage.metrics.visualization.tree; import edu.hm.hafner.echarts.ItemStyle; import edu.hm.hafner.echarts.Label; @@ -8,9 +8,9 @@ import edu.hm.hafner.metric.Metric; import edu.hm.hafner.metric.Node; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors; -import io.jenkins.plugins.coverage.model.visualization.colorization.CoverageLevel; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProvider; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProvider.DisplayColors; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.CoverageLevel; /** * Converts a tree of {@link Node coverage nodes} to a corresponding tree of diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageBuildAction/summary.jelly b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageBuildAction/summary.jelly similarity index 100% rename from plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageBuildAction/summary.jelly rename to plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageBuildAction/summary.jelly diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageJobAction/floatingBox.jelly b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageJobAction/floatingBox.jelly similarity index 100% rename from plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageJobAction/floatingBox.jelly rename to plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageJobAction/floatingBox.jelly diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageViewModel/index.jelly b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageViewModel/index.jelly similarity index 100% rename from plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageViewModel/index.jelly rename to plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageViewModel/index.jelly diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageViewModel/index.properties b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageViewModel/index.properties similarity index 100% rename from plugin/src/main/resources/io/jenkins/plugins/coverage/model/CoverageViewModel/index.properties rename to plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/CoverageViewModel/index.properties diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/Messages.properties b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/Messages.properties index f8c65462b..b4f8fac12 100644 --- a/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/Messages.properties +++ b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/Messages.properties @@ -3,3 +3,34 @@ Recorder.Name=Record code coverage results Parser.Cobertura=Cobertura Parser.JaCoCo=JaCoCo Parser.PIT=PIT Mutation Testing + +Coverage.Not.Available=n/a +Coverage.Link.Name=Coverage Report +Coverage.Title=Coverage of ''{0}'' +Coverage_Column=Coverage +Project_Coverage_Type=Project Coverage +Project_Coverage_Delta_Type=Project Coverage Delta +Change_Coverage_Type=Change Coverage +Change_Coverage_Delta_Type=Change Coverage Delta +Indirect_Coverage_Changes_Type=Indirect Coverage Changes + +Column.File=File +Column.Package=Package +Column.LineCoverage=Line +Column.DeltaLineCoverage=Line {0} +Column.BranchCoverage=Branch +Column.DeltaBranchCoverage=Branch {0} +Column.LinesOfCode=LOC + +Metric.CONTAINER=Container +Metric.MODULE=Module +Metric.PACKAGE=Package +Metric.FILE=File +Metric.CLASS=Class +Metric.METHOD=Method +Metric.INSTRUCTION=Instruction +Metric.LINE=Line +Metric.MUTATION=Mutation +Metric.BRANCH=Branch +Metric.COMPLEXITY=Cyclomatic Complexity +Metric.LOC=Lines of Code diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/SourceViewModel/index.jelly b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/SourceViewModel/index.jelly similarity index 100% rename from plugin/src/main/resources/io/jenkins/plugins/coverage/model/SourceViewModel/index.jelly rename to plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/SourceViewModel/index.jelly diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/SourceViewModel/index.properties b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/SourceViewModel/index.properties similarity index 100% rename from plugin/src/main/resources/io/jenkins/plugins/coverage/model/SourceViewModel/index.properties rename to plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/SourceViewModel/index.properties diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumn/column.jelly b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/visualization/dashboard/CoverageColumn/column.jelly similarity index 100% rename from plugin/src/main/resources/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumn/column.jelly rename to plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/visualization/dashboard/CoverageColumn/column.jelly diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumn/columnHeader.jelly b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/visualization/dashboard/CoverageColumn/columnHeader.jelly similarity index 100% rename from plugin/src/main/resources/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumn/columnHeader.jelly rename to plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/visualization/dashboard/CoverageColumn/columnHeader.jelly diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumn/config.jelly b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/visualization/dashboard/CoverageColumn/config.jelly similarity index 100% rename from plugin/src/main/resources/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumn/config.jelly rename to plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/visualization/dashboard/CoverageColumn/config.jelly diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumn/config.properties b/plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/visualization/dashboard/CoverageColumn/config.properties similarity index 100% rename from plugin/src/main/resources/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumn/config.properties rename to plugin/src/main/resources/io/jenkins/plugins/coverage/metrics/visualization/dashboard/CoverageColumn/config.properties diff --git a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/Messages.properties b/plugin/src/main/resources/io/jenkins/plugins/coverage/model/Messages.properties deleted file mode 100644 index e6a294cd7..000000000 --- a/plugin/src/main/resources/io/jenkins/plugins/coverage/model/Messages.properties +++ /dev/null @@ -1,30 +0,0 @@ -Coverage.Not.Available=n/a -Coverage.Link.Name=Coverage Report -Coverage.Title=Coverage of ''{0}'' -Coverage_Column=Coverage -Project_Coverage_Type=Project Coverage -Project_Coverage_Delta_Type=Project Coverage Delta -Change_Coverage_Type=Change Coverage -Change_Coverage_Delta_Type=Change Coverage Delta -Indirect_Coverage_Changes_Type=Indirect Coverage Changes - -Column.File=File -Column.Package=Package -Column.LineCoverage=Line -Column.DeltaLineCoverage=Line {0} -Column.BranchCoverage=Branch -Column.DeltaBranchCoverage=Branch {0} -Column.LinesOfCode=LOC - -Metric.CONTAINER=Container -Metric.MODULE=Module -Metric.PACKAGE=Package -Metric.FILE=File -Metric.CLASS=Class -Metric.METHOD=Method -Metric.INSTRUCTION=Instruction -Metric.LINE=Line -Metric.MUTATION=Mutation -Metric.BRANCH=Branch -Metric.COMPLEXITY=Cyclomatic Complexity -Metric.LOC=Lines of Code diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/AbstractCoverageITest.java similarity index 96% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageITest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/AbstractCoverageITest.java index 0371efc24..00ded8868 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/AbstractCoverageITest.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import java.io.IOException; import java.util.List; @@ -8,8 +8,6 @@ import org.jenkinsci.plugins.workflow.job.WorkflowJob; import hudson.model.FreeStyleProject; -import io.jenkins.plugins.coverage.metrics.CoverageRecorder; -import io.jenkins.plugins.coverage.metrics.CoverageTool; import io.jenkins.plugins.coverage.metrics.CoverageTool.CoverageParser; import io.jenkins.plugins.util.IntegrationTestWithJenkinsPerSuite; diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/AbstractCoverageTest.java similarity index 97% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageTest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/AbstractCoverageTest.java index d5b7a4b56..dee03ba8d 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/AbstractCoverageTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/AbstractCoverageTest.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import java.io.IOException; import java.nio.file.Files; diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CodeDeltaCalculatorTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CodeDeltaCalculatorTest.java similarity index 98% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/CodeDeltaCalculatorTest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CodeDeltaCalculatorTest.java index 71349474e..6c131f9c0 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CodeDeltaCalculatorTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CodeDeltaCalculatorTest.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import java.nio.file.Paths; import java.util.Arrays; @@ -20,12 +20,11 @@ import hudson.model.Run; import hudson.model.TaskListener; -import io.jenkins.plugins.coverage.model.exception.CodeDeltaException; import io.jenkins.plugins.forensics.delta.model.Delta; import io.jenkins.plugins.forensics.delta.model.FileChanges; import io.jenkins.plugins.forensics.delta.model.FileEditType; -import static io.jenkins.plugins.coverage.model.CodeDeltaCalculator.*; +import static io.jenkins.plugins.coverage.metrics.CodeDeltaCalculator.*; import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageBuildActionTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CoverageBuildActionTest.java similarity index 98% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageBuildActionTest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CoverageBuildActionTest.java index da83009a9..7e5d5f883 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageBuildActionTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CoverageBuildActionTest.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import java.util.Locale; import java.util.NavigableMap; @@ -20,8 +20,8 @@ import hudson.model.HealthReport; import hudson.model.Run; -import static io.jenkins.plugins.coverage.model.testutil.CoverageStubs.*; -import static io.jenkins.plugins.coverage.model.testutil.JobStubs.*; +import static io.jenkins.plugins.coverage.metrics.testutil.CoverageStubs.*; +import static io.jenkins.plugins.coverage.metrics.testutil.JobStubs.*; import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageJobActionTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CoverageJobActionTest.java similarity index 98% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageJobActionTest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CoverageJobActionTest.java index 3d22d4d6c..8ac91c6fa 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageJobActionTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CoverageJobActionTest.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import java.io.IOException; import java.util.ArrayList; diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePercentageTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CoveragePercentageTest.java similarity index 72% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePercentageTest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CoveragePercentageTest.java index 13233f83c..c6dd063df 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePercentageTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CoveragePercentageTest.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import java.util.Locale; @@ -10,8 +10,8 @@ import nl.jqno.equalsverifier.EqualsVerifier; +import static io.jenkins.plugins.coverage.metrics.CoveragePercentage.*; import static io.jenkins.plugins.coverage.model.Assertions.assertThat; -import static io.jenkins.plugins.coverage.model.CoveragePercentage.*; import static org.assertj.core.api.Assertions.*; /** @@ -27,58 +27,58 @@ class CoveragePercentageTest { @Test void shouldHandleOverflow() { Fraction fraction = Fraction.getFraction(Integer.MAX_VALUE - 1, Integer.MAX_VALUE - 1); - CoveragePercentage coveragePercentage = valueOf(fraction); + CoveragePercentage coveragePercentage = CoveragePercentage.valueOf(fraction); assertThat(coveragePercentage.getDoubleValue()).isEqualTo(100); } @Test void shouldCreateCoveragePercentageFromFraction() { Fraction fraction = Fraction.getFraction(COVERAGE_FRACTION); - CoveragePercentage coveragePercentage = valueOf(fraction); + CoveragePercentage coveragePercentage = CoveragePercentage.valueOf(fraction); assertThat(coveragePercentage.getDoubleValue()).isEqualTo(50.0); } @Test void shouldCreateCoveragePercentageFromDouble() { - CoveragePercentage coveragePercentage = valueOf(COVERAGE_PERCENTAGE); + CoveragePercentage coveragePercentage = CoveragePercentage.valueOf(COVERAGE_PERCENTAGE); assertThat(coveragePercentage.getDoubleValue()).isEqualTo(50.0); } @Test void shouldCreateCoveragePercentageFromNumeratorAndDenominator() { - CoveragePercentage coveragePercentage = valueOf(50, 1); + CoveragePercentage coveragePercentage = CoveragePercentage.valueOf(50, 1); assertThat(coveragePercentage.getDoubleValue()).isEqualTo(50.0); } @Test void shouldNotCreateCoveragePercentageFromNumeratorAndZeroDenominator() { - assertThatThrownBy(() -> valueOf(50, 0)) + assertThatThrownBy(() -> CoveragePercentage.valueOf(50, 0)) .isInstanceOf(IllegalArgumentException.class) .hasMessage(DENOMINATOR_ZERO_MESSAGE); } @Test void shouldHaveWorkingGetters() { - CoveragePercentage coveragePercentage = valueOf(COVERAGE_PERCENTAGE); + CoveragePercentage coveragePercentage = CoveragePercentage.valueOf(COVERAGE_PERCENTAGE); assertThat(coveragePercentage.getNumerator()).isEqualTo(50); assertThat(coveragePercentage.getDenominator()).isEqualTo(1); } @Test void shouldGetDoubleValue() { - CoveragePercentage coveragePercentage = valueOf(COVERAGE_PERCENTAGE); + CoveragePercentage coveragePercentage = CoveragePercentage.valueOf(COVERAGE_PERCENTAGE); assertThat(coveragePercentage.getDoubleValue()).isEqualTo(50.0); } @Test void shouldFormatPercentage() { - CoveragePercentage coveragePercentage = valueOf(COVERAGE_PERCENTAGE); + CoveragePercentage coveragePercentage = CoveragePercentage.valueOf(COVERAGE_PERCENTAGE); assertThat(coveragePercentage.formatPercentage(LOCALE)).isEqualTo("50,00%"); } @Test void shouldFormatDeltaPercentage() { - CoveragePercentage coveragePercentage = valueOf(COVERAGE_PERCENTAGE); + CoveragePercentage coveragePercentage = CoveragePercentage.valueOf(COVERAGE_PERCENTAGE); assertThat(coveragePercentage.formatDeltaPercentage(LOCALE)).isEqualTo("+50,00%"); } @@ -89,7 +89,7 @@ void shouldObeyEqualsContract() { @Test void shouldSerializeInstance() { - CoveragePercentage percentage = valueOf(49, 1); + CoveragePercentage percentage = CoveragePercentage.valueOf(49, 1); assertThat(percentage.serializeToString()) .isEqualTo("49/1"); assertThat(valueOf("49/1")).isEqualTo(percentage) diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CoveragePluginITest.java similarity index 98% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CoveragePluginITest.java index 2ce899b43..484c32942 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePluginITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CoveragePluginITest.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import java.util.List; @@ -18,8 +18,6 @@ import hudson.model.Run; import jenkins.model.ParameterizedJobMixIn.ParameterizedJob; -import io.jenkins.plugins.coverage.metrics.CoverageRecorder; -import io.jenkins.plugins.coverage.metrics.CoverageTool; import io.jenkins.plugins.coverage.metrics.CoverageTool.CoverageParser; import static org.assertj.core.api.Assertions.*; diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTreeCreatorTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CoverageTreeCreatorTest.java similarity index 99% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTreeCreatorTest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CoverageTreeCreatorTest.java index 32a6b47a2..11f8dece6 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageTreeCreatorTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CoverageTreeCreatorTest.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import java.util.Optional; import java.util.SortedMap; diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageViewModelTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CoverageViewModelTest.java similarity index 94% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageViewModelTest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CoverageViewModelTest.java index 00788abf7..ba67c9bad 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageViewModelTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CoverageViewModelTest.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import java.util.List; import java.util.NoSuchElementException; @@ -10,9 +10,9 @@ import hudson.model.Run; +import static io.jenkins.plugins.coverage.metrics.CoverageViewModel.*; +import static io.jenkins.plugins.coverage.metrics.testutil.CoverageStubs.*; import static io.jenkins.plugins.coverage.model.Assertions.*; -import static io.jenkins.plugins.coverage.model.CoverageViewModel.*; -import static io.jenkins.plugins.coverage.model.testutil.CoverageStubs.*; import static net.javacrumbs.jsonunit.assertj.JsonAssertions.*; import static org.mockito.Mockito.*; diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageXmlStreamTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CoverageXmlStreamTest.java similarity index 95% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageXmlStreamTest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CoverageXmlStreamTest.java index 2c5414f2e..b03711b57 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoverageXmlStreamTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/CoverageXmlStreamTest.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import java.io.InputStreamReader; import java.nio.file.Path; diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeltaComputationITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/DeltaComputationITest.java similarity index 98% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/DeltaComputationITest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/DeltaComputationITest.java index 5e65ebcd8..e7e9cff7c 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DeltaComputationITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/DeltaComputationITest.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import org.junit.jupiter.api.Test; @@ -12,7 +12,6 @@ import hudson.model.FreeStyleProject; import hudson.model.Run; -import io.jenkins.plugins.coverage.metrics.CoverageRecorder; import io.jenkins.plugins.coverage.metrics.CoverageTool.CoverageParser; import static edu.hm.hafner.metric.Metric.*; diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerAgentSourceCodeITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/DockerAgentSourceCodeITest.java similarity index 92% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerAgentSourceCodeITest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/DockerAgentSourceCodeITest.java index 2cea5775b..0b120bdda 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/DockerAgentSourceCodeITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/DockerAgentSourceCodeITest.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import java.io.IOException; @@ -21,7 +21,7 @@ class DockerAgentSourceCodeITest extends SourceCodeITest { @Container private static final AgentContainer AGENT_CONTAINER = new AgentContainer() - .withCopyFileToContainer(MountableFile.forClasspathResource("io/jenkins/plugins/coverage/model/" + SOURCE_FILE), CONTAINER_PATH); + .withCopyFileToContainer(MountableFile.forClasspathResource("io/jenkins/plugins/coverage/metrics/" + SOURCE_FILE), CONTAINER_PATH); @Override protected Node crateCoverageAgent() { diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/FileChangesProcessorTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/FileChangesProcessorTest.java similarity index 99% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/FileChangesProcessorTest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/FileChangesProcessorTest.java index 71464952b..2f78c580c 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/FileChangesProcessorTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/FileChangesProcessorTest.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import java.util.AbstractMap.SimpleEntry; import java.util.HashMap; diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/FilePathValidatorTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/FilePathValidatorTest.java similarity index 95% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/FilePathValidatorTest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/FilePathValidatorTest.java index 99234150b..2c35be60b 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/FilePathValidatorTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/FilePathValidatorTest.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import org.junit.jupiter.api.Test; @@ -10,7 +10,7 @@ import edu.hm.hafner.metric.PackageNode; import edu.hm.hafner.util.FilteredLog; -import static io.jenkins.plugins.coverage.model.FilePathValidator.*; +import static io.jenkins.plugins.coverage.metrics.FilePathValidator.*; import static org.assertj.core.api.Assertions.*; /** diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/GitForensicsITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/GitForensicsITest.java similarity index 99% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/GitForensicsITest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/GitForensicsITest.java index 05973119d..99a3ee3ae 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/GitForensicsITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/GitForensicsITest.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import java.io.IOException; import java.util.Collections; diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/LocalAgentSourceCodeITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/LocalAgentSourceCodeITest.java similarity index 96% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/LocalAgentSourceCodeITest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/LocalAgentSourceCodeITest.java index 260c71eb0..e3bb87dfe 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/LocalAgentSourceCodeITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/LocalAgentSourceCodeITest.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import java.io.IOException; import java.nio.file.Files; diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/PluginArchitectureTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/PluginArchitectureTest.java similarity index 95% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/PluginArchitectureTest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/PluginArchitectureTest.java index e09c8a548..284f7ace6 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/PluginArchitectureTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/PluginArchitectureTest.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import com.tngtech.archunit.junit.AnalyzeClasses; import com.tngtech.archunit.junit.ArchTest; @@ -14,7 +14,7 @@ * @author Ullrich Hafner */ @SuppressWarnings("hideutilityclassconstructor") -@AnalyzeClasses(packages = "io.jenkins.plugins.coverage.model") +@AnalyzeClasses(packages = "io.jenkins.plugins.coverage.metrics") class PluginArchitectureTest { @ArchTest static final ArchRule NO_EXCEPTIONS_WITH_NO_ARG_CONSTRUCTOR = ArchitectureRules.NO_EXCEPTIONS_WITH_NO_ARG_CONSTRUCTOR; diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/SourceCodeITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/SourceCodeITest.java similarity index 99% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/SourceCodeITest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/SourceCodeITest.java index 1f932a72b..e7e30a27e 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/SourceCodeITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/SourceCodeITest.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import java.io.File; import java.io.IOException; diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/TreeMapNodeConverterTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/TreeMapNodeConverterTest.java similarity index 87% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/TreeMapNodeConverterTest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/TreeMapNodeConverterTest.java index 119593e1e..aab8529a2 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/TreeMapNodeConverterTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/TreeMapNodeConverterTest.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model; +package io.jenkins.plugins.coverage.metrics; import java.nio.file.Paths; @@ -8,12 +8,12 @@ import edu.hm.hafner.metric.Metric; import edu.hm.hafner.metric.Node; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProviderFactory; -import io.jenkins.plugins.coverage.model.visualization.colorization.CoverageLevel; -import io.jenkins.plugins.coverage.model.visualization.tree.TreeMapNodeConverter; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProvider; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProviderFactory; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.CoverageLevel; +import io.jenkins.plugins.coverage.metrics.visualization.tree.TreeMapNodeConverter; -import static io.jenkins.plugins.coverage.model.AbstractCoverageITest.*; +import static io.jenkins.plugins.coverage.metrics.AbstractCoverageITest.*; import static org.assertj.core.api.Assertions.*; /** diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/testutil/CoverageStubs.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/testutil/CoverageStubs.java similarity index 96% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/testutil/CoverageStubs.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/testutil/CoverageStubs.java index 1d7dbf465..f4e8ad472 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/testutil/CoverageStubs.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/testutil/CoverageStubs.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model.testutil; +package io.jenkins.plugins.coverage.metrics.testutil; import java.util.NavigableMap; import java.util.Optional; @@ -15,10 +15,10 @@ import edu.hm.hafner.metric.Node; import edu.hm.hafner.util.VisibleForTesting; -import io.jenkins.plugins.coverage.model.CoverageBuildAction; -import io.jenkins.plugins.coverage.model.CoveragePercentage; +import io.jenkins.plugins.coverage.metrics.CoverageBuildAction; +import io.jenkins.plugins.coverage.metrics.CoveragePercentage; -import static io.jenkins.plugins.coverage.model.CoverageBuildAction.*; +import static io.jenkins.plugins.coverage.metrics.CoverageBuildAction.*; import static org.mockito.Mockito.*; /** diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/testutil/JobStubs.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/testutil/JobStubs.java similarity index 94% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/testutil/JobStubs.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/testutil/JobStubs.java index 1838fdc47..e26d63945 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/testutil/JobStubs.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/testutil/JobStubs.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model.testutil; +package io.jenkins.plugins.coverage.metrics.testutil; import java.util.Arrays; @@ -8,7 +8,7 @@ import hudson.model.Job; import hudson.model.Run; -import io.jenkins.plugins.coverage.model.CoverageBuildAction; +import io.jenkins.plugins.coverage.metrics.CoverageBuildAction; import static org.mockito.Mockito.*; diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/charts/CoverageSeriesBuilderTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/charts/CoverageSeriesBuilderTest.java similarity index 94% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/charts/CoverageSeriesBuilderTest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/charts/CoverageSeriesBuilderTest.java index 50eb7d81f..5a43b0fa4 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/charts/CoverageSeriesBuilderTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/charts/CoverageSeriesBuilderTest.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model.visualization.charts; +package io.jenkins.plugins.coverage.metrics.visualization.charts; import java.util.ArrayList; import java.util.Collections; @@ -13,9 +13,9 @@ import edu.hm.hafner.metric.Coverage.CoverageBuilder; import edu.hm.hafner.metric.Metric; -import io.jenkins.plugins.coverage.model.CoverageBuildAction; +import io.jenkins.plugins.coverage.metrics.CoverageBuildAction; -import static io.jenkins.plugins.coverage.model.testutil.CoverageStubs.*; +import static io.jenkins.plugins.coverage.metrics.testutil.CoverageStubs.*; import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodeFacadeTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/code/SourceCodeFacadeTest.java similarity index 98% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodeFacadeTest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/code/SourceCodeFacadeTest.java index 0a2e62aa8..29345875c 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/code/SourceCodeFacadeTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/code/SourceCodeFacadeTest.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model.visualization.code; +package io.jenkins.plugins.coverage.metrics.visualization.code; import java.io.IOException; import java.nio.file.Files; diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/colorization/ColorProviderFactoryTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/ColorProviderFactoryTest.java similarity index 94% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/colorization/ColorProviderFactoryTest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/ColorProviderFactoryTest.java index 37bd62094..fc48f17c0 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/colorization/ColorProviderFactoryTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/ColorProviderFactoryTest.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model.visualization.colorization; +package io.jenkins.plugins.coverage.metrics.visualization.colorization; import java.awt.*; import java.util.HashMap; @@ -6,7 +6,7 @@ import org.junit.jupiter.api.Test; -import static io.jenkins.plugins.coverage.model.visualization.colorization.CoverageColorJenkinsId.*; +import static io.jenkins.plugins.coverage.metrics.visualization.colorization.CoverageColorJenkinsId.*; import static org.assertj.core.api.Assertions.*; /** diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/colorization/ColorProviderTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/ColorProviderTest.java similarity index 95% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/colorization/ColorProviderTest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/ColorProviderTest.java index 278fe2ee4..5ba44bfe3 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/colorization/ColorProviderTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/ColorProviderTest.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model.visualization.colorization; +package io.jenkins.plugins.coverage.metrics.visualization.colorization; import java.awt.*; @@ -6,7 +6,7 @@ import nl.jqno.equalsverifier.EqualsVerifier; -import static io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.*; +import static io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProvider.*; import static org.assertj.core.api.Assertions.*; /** diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageChangeLevelTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/CoverageChangeLevelTest.java similarity index 93% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageChangeLevelTest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/CoverageChangeLevelTest.java index 5cb5c846e..50edb0eab 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageChangeLevelTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/CoverageChangeLevelTest.java @@ -1,10 +1,10 @@ -package io.jenkins.plugins.coverage.model.visualization.colorization; +package io.jenkins.plugins.coverage.metrics.visualization.colorization; import java.awt.*; import org.junit.jupiter.api.Test; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProvider.DisplayColors; import static org.assertj.core.api.Assertions.*; diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageChangeTendencyTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/CoverageChangeTendencyTest.java similarity index 94% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageChangeTendencyTest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/CoverageChangeTendencyTest.java index c73acf5ba..08c177ae5 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageChangeTendencyTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/CoverageChangeTendencyTest.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model.visualization.colorization; +package io.jenkins.plugins.coverage.metrics.visualization.colorization; import org.junit.jupiter.api.Test; diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageColorJenkinsIdTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/CoverageColorJenkinsIdTest.java similarity index 71% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageColorJenkinsIdTest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/CoverageColorJenkinsIdTest.java index 8f94e7929..9e38adf01 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageColorJenkinsIdTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/CoverageColorJenkinsIdTest.java @@ -1,8 +1,8 @@ -package io.jenkins.plugins.coverage.model.visualization.colorization; +package io.jenkins.plugins.coverage.metrics.visualization.colorization; import org.junit.jupiter.api.Test; -import static io.jenkins.plugins.coverage.model.visualization.colorization.CoverageColorJenkinsId.*; +import static io.jenkins.plugins.coverage.metrics.visualization.colorization.CoverageColorJenkinsId.*; import static org.assertj.core.api.Assertions.*; /** diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageLevelTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/CoverageLevelTest.java similarity index 90% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageLevelTest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/CoverageLevelTest.java index 010df7a62..4d1356b01 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/colorization/CoverageLevelTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/colorization/CoverageLevelTest.java @@ -1,10 +1,10 @@ -package io.jenkins.plugins.coverage.model.visualization.colorization; +package io.jenkins.plugins.coverage.metrics.visualization.colorization; import java.awt.*; import org.junit.jupiter.api.Test; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProvider.DisplayColors; import static org.assertj.core.api.Assertions.*; diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ChangeCoverageDeltaTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/ChangeCoverageDeltaTest.java similarity index 80% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ChangeCoverageDeltaTest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/ChangeCoverageDeltaTest.java index 597441d49..2bca06888 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ChangeCoverageDeltaTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/ChangeCoverageDeltaTest.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model.visualization.dashboard; +package io.jenkins.plugins.coverage.metrics.visualization.dashboard; import java.util.Optional; @@ -6,10 +6,10 @@ import edu.hm.hafner.metric.Metric; -import io.jenkins.plugins.coverage.model.CoverageBuildAction; -import io.jenkins.plugins.coverage.model.CoveragePercentage; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorId; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors; +import io.jenkins.plugins.coverage.metrics.CoverageBuildAction; +import io.jenkins.plugins.coverage.metrics.CoveragePercentage; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorId; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProvider.DisplayColors; import static org.assertj.core.api.Assertions.*; diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ChangeCoverageTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/ChangeCoverageTest.java similarity index 79% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ChangeCoverageTest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/ChangeCoverageTest.java index a7e908c98..6f5d37a84 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ChangeCoverageTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/ChangeCoverageTest.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model.visualization.dashboard; +package io.jenkins.plugins.coverage.metrics.visualization.dashboard; import java.util.Optional; @@ -6,10 +6,10 @@ import edu.hm.hafner.metric.Metric; -import io.jenkins.plugins.coverage.model.CoverageBuildAction; -import io.jenkins.plugins.coverage.model.CoveragePercentage; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorId; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors; +import io.jenkins.plugins.coverage.metrics.CoverageBuildAction; +import io.jenkins.plugins.coverage.metrics.CoveragePercentage; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorId; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProvider.DisplayColors; import static org.assertj.core.api.Assertions.*; diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumnTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/CoverageColumnTest.java similarity index 91% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumnTest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/CoverageColumnTest.java index e6d0448a9..c9319fe9c 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumnTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/CoverageColumnTest.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model.visualization.dashboard; +package io.jenkins.plugins.coverage.metrics.visualization.dashboard; import java.util.Optional; @@ -11,16 +11,16 @@ import hudson.Functions; import hudson.model.Job; -import io.jenkins.plugins.coverage.model.CoverageBuildAction; -import io.jenkins.plugins.coverage.model.CoveragePercentage; -import io.jenkins.plugins.coverage.model.Messages; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProviderFactory; -import io.jenkins.plugins.coverage.model.visualization.colorization.CoverageChangeTendency; -import io.jenkins.plugins.coverage.model.visualization.colorization.CoverageLevel; +import io.jenkins.plugins.coverage.metrics.CoverageBuildAction; +import io.jenkins.plugins.coverage.metrics.CoveragePercentage; +import io.jenkins.plugins.coverage.metrics.Messages; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProvider; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProviderFactory; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.CoverageChangeTendency; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.CoverageLevel; -import static io.jenkins.plugins.coverage.model.testutil.CoverageStubs.*; -import static io.jenkins.plugins.coverage.model.testutil.JobStubs.*; +import static io.jenkins.plugins.coverage.metrics.testutil.CoverageStubs.*; +import static io.jenkins.plugins.coverage.metrics.testutil.JobStubs.*; import static org.assertj.core.api.Assertions.*; /** diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumnTypeTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/CoverageColumnTypeTest.java similarity index 85% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumnTypeTest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/CoverageColumnTypeTest.java index 865cc31e4..f3641771e 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/CoverageColumnTypeTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/CoverageColumnTypeTest.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model.visualization.dashboard; +package io.jenkins.plugins.coverage.metrics.visualization.dashboard; import java.util.List; import java.util.Locale; @@ -8,12 +8,12 @@ import edu.hm.hafner.metric.Metric; -import io.jenkins.plugins.coverage.model.CoverageBuildAction; -import io.jenkins.plugins.coverage.model.CoveragePercentage; -import io.jenkins.plugins.coverage.model.Messages; -import io.jenkins.plugins.coverage.model.testutil.CoverageStubs; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProviderFactory; +import io.jenkins.plugins.coverage.metrics.CoverageBuildAction; +import io.jenkins.plugins.coverage.metrics.CoveragePercentage; +import io.jenkins.plugins.coverage.metrics.Messages; +import io.jenkins.plugins.coverage.metrics.testutil.CoverageStubs; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProvider; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProviderFactory; import static org.assertj.core.api.Assertions.*; diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/IndirectCoverageChangesTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/IndirectCoverageChangesTest.java similarity index 80% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/IndirectCoverageChangesTest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/IndirectCoverageChangesTest.java index 0dff97b71..158dfa2c3 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/IndirectCoverageChangesTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/IndirectCoverageChangesTest.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model.visualization.dashboard; +package io.jenkins.plugins.coverage.metrics.visualization.dashboard; import java.util.Optional; @@ -6,10 +6,10 @@ import edu.hm.hafner.metric.Metric; -import io.jenkins.plugins.coverage.model.CoverageBuildAction; -import io.jenkins.plugins.coverage.model.CoveragePercentage; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorId; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors; +import io.jenkins.plugins.coverage.metrics.CoverageBuildAction; +import io.jenkins.plugins.coverage.metrics.CoveragePercentage; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorId; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProvider.DisplayColors; import static org.assertj.core.api.Assertions.*; diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ProjectCoverageDeltaTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/ProjectCoverageDeltaTest.java similarity index 80% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ProjectCoverageDeltaTest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/ProjectCoverageDeltaTest.java index 16521bd74..9bc01a763 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ProjectCoverageDeltaTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/ProjectCoverageDeltaTest.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model.visualization.dashboard; +package io.jenkins.plugins.coverage.metrics.visualization.dashboard; import java.util.Optional; @@ -6,10 +6,10 @@ import edu.hm.hafner.metric.Metric; -import io.jenkins.plugins.coverage.model.CoverageBuildAction; -import io.jenkins.plugins.coverage.model.CoveragePercentage; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorId; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors; +import io.jenkins.plugins.coverage.metrics.CoverageBuildAction; +import io.jenkins.plugins.coverage.metrics.CoveragePercentage; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorId; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProvider.DisplayColors; import static org.assertj.core.api.Assertions.*; diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ProjectCoverageTest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/ProjectCoverageTest.java similarity index 79% rename from plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ProjectCoverageTest.java rename to plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/ProjectCoverageTest.java index 1cf2f26ff..9f3f9f9f7 100644 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/visualization/dashboard/ProjectCoverageTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/coverage/metrics/visualization/dashboard/ProjectCoverageTest.java @@ -1,4 +1,4 @@ -package io.jenkins.plugins.coverage.model.visualization.dashboard; +package io.jenkins.plugins.coverage.metrics.visualization.dashboard; import java.util.Optional; @@ -6,10 +6,10 @@ import edu.hm.hafner.metric.Metric; -import io.jenkins.plugins.coverage.model.CoverageBuildAction; -import io.jenkins.plugins.coverage.model.CoveragePercentage; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorId; -import io.jenkins.plugins.coverage.model.visualization.colorization.ColorProvider.DisplayColors; +import io.jenkins.plugins.coverage.metrics.CoverageBuildAction; +import io.jenkins.plugins.coverage.metrics.CoveragePercentage; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorId; +import io.jenkins.plugins.coverage.metrics.visualization.colorization.ColorProvider.DisplayColors; import static org.assertj.core.api.Assertions.*; diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePlugin0vsOkITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePlugin0vsOkITest.java deleted file mode 100644 index dcf5df3cf..000000000 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/CoveragePlugin0vsOkITest.java +++ /dev/null @@ -1,184 +0,0 @@ -package io.jenkins.plugins.coverage.model; - -import java.util.Collections; - -import org.junit.jupiter.api.Test; - -import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; -import org.jenkinsci.plugins.workflow.job.WorkflowJob; -import hudson.model.FreeStyleProject; - -import io.jenkins.plugins.coverage.CoveragePublisher; -import io.jenkins.plugins.coverage.adapter.JacocoReportAdapter; -import io.jenkins.plugins.util.IntegrationTestWithJenkinsPerSuite; - -/** - * Tests if 0 reports fail vs ok. - */ -class CoveragePlugin0vsOkITest extends IntegrationTestWithJenkinsPerSuite { - private static final String JACOCO_ANALYSIS_MODEL_FILE = "jacoco-analysis-model.xml"; - - /** - * Adapter reads no file and failNoReports is set true in freestyle project. - */ - @Test - void freeStyleNoFileAndFailNoReportsTrue() { - FreeStyleProject project = createFreeStyleProjectWithFailNoReports(true, ""); - - buildSuccessfully(project); - } - - /** - * Adapter reads no file and failNoReports is set false in freestyle project. - */ - @Test - void freeStyleNoFileAndFailNoReportsFalse() { - FreeStyleProject project = createFreeStyleProjectWithFailNoReports(false, ""); - - buildSuccessfully(project); - } - - /** - * Adapter reads one file and failNoReports is set true in freestyle project. - */ - @Test - void freeStyleWithFileAndFailNoReportsTrue() { - FreeStyleProject project = createFreeStyleProjectWithFailNoReports(true, JACOCO_ANALYSIS_MODEL_FILE); - - buildSuccessfully(project); - } - - /** - * Adapter reads one file and failNoReports is set false in freestyle project. - */ - @Test - void freeStyleWithFileAndFailNoReportsFalse() { - FreeStyleProject project = createFreeStyleProjectWithFailNoReports(false, JACOCO_ANALYSIS_MODEL_FILE); - - buildSuccessfully(project); - } - - /** - * Adapter reads one file and failNoReports is set false in freestyle project. - */ - @Test - void freeStyleWithFileWildcardAndFailNoReportsTrue() { - FreeStyleProject project = createFreeStyleProjectWithFailNoReports(true, "**/*.xml"); - - buildSuccessfully(project); - } - - /** - * Adapter reads one file and failNoReports is set false in freestyle project. - */ - @Test - void freeStyleWithFileWildcardAndFailNoReportsFalse() { - FreeStyleProject project = createFreeStyleProjectWithFailNoReports(false, "**/*.xml"); - - buildSuccessfully(project); - } - - /** - * Adapter reads no file and failNoReports is set true in pipeline project. - */ - @Test - void pipelineNoFileAndFailNoReportsTrue() { - WorkflowJob job = getPipelineProjectWithJacoco(true, ""); - - buildSuccessfully(job); - } - - /** - * Adapter reads no file and failNoReports is set false in pipeline project. - */ - @Test - void pipelineNoFileAndFailNoReportsFalse() { - WorkflowJob job = getPipelineProjectWithJacoco(false, ""); - - buildSuccessfully(job); - } - - /** - * Adapter reads one file and failNoReports is set true in pipeline project. - */ - @Test - void pipelineWithFileAndFailNoReportsTrue() { - WorkflowJob job = getPipelineProjectWithJacoco(true, JACOCO_ANALYSIS_MODEL_FILE); - - buildSuccessfully(job); - } - - /** - * Adapter reads one file and failNoReports is set false in pipeline project. - */ - @Test - void pipelineWithFileAndFailNoReportsFalse() { - WorkflowJob job = getPipelineProjectWithJacoco(false, JACOCO_ANALYSIS_MODEL_FILE); - - buildSuccessfully(job); - } - - /** - * Adapter reads one file and failNoReports is set false in pipeline project. - */ - @Test - void pipelineWithFileWildcardAndFailNoReportsTrue() { - WorkflowJob job = getPipelineProjectWithJacoco(true, "**/*.xml"); - - buildSuccessfully(job); - } - - /** - * Adapter reads one file and failNoReports is set false in pipeline project. - */ - @Test - void pipelineWithFileWildcardAndFailNoReportsFalse() { - WorkflowJob job = getPipelineProjectWithJacoco(false, "**/*.xml"); - - buildSuccessfully(job); - } - - /** - * Creates a freestyle project with set fail with no reports and path to jacoco file. - * - * @param setFailNoReports - * sets FailNoReports to this value - * @param pathToJacoco - * path to jacoco file - * - * @return {@link FreeStyleProject} with set parameters - */ - private FreeStyleProject createFreeStyleProjectWithFailNoReports(final boolean setFailNoReports, - final String pathToJacoco) { - FreeStyleProject project = createFreeStyleProject(); - copyFilesToWorkspace(project, JACOCO_ANALYSIS_MODEL_FILE); - CoveragePublisher coveragePublisher = new CoveragePublisher(); - JacocoReportAdapter jacocoReportAdapter = new JacocoReportAdapter(pathToJacoco); - coveragePublisher.setAdapters(Collections.singletonList(jacocoReportAdapter)); - coveragePublisher.setFailNoReports(setFailNoReports); - project.getPublishersList().add(coveragePublisher); - return project; - } - - /** - * Creates a pipeline project with set fail with no reports and path to jacoco file. - * - * @param setFailNoReports - * sets FailNoReports to this value - * @param pathToJacoco - * path to jacoco file - * - * @return pipeline project with set parameters - */ - private WorkflowJob getPipelineProjectWithJacoco(final boolean setFailNoReports, - final String pathToJacoco) { - WorkflowJob job = createPipelineWithWorkspaceFiles(JACOCO_ANALYSIS_MODEL_FILE); - - job.setDefinition(new CpsFlowDefinition("node {" - + " publishCoverage adapters: [jacocoAdapter('" + pathToJacoco + "')]\n " - + " failNoReports: " + setFailNoReports - + "}", true)); - - return job; - } -} diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/FailBuildIfCoverageDecreasesITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/FailBuildIfCoverageDecreasesITest.java deleted file mode 100644 index 5d24bb291..000000000 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/FailBuildIfCoverageDecreasesITest.java +++ /dev/null @@ -1,139 +0,0 @@ -package io.jenkins.plugins.coverage.model; - -import java.io.IOException; -import java.util.Collections; - -import org.junit.jupiter.api.Test; - -import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; -import org.jenkinsci.plugins.workflow.job.WorkflowJob; -import hudson.model.FreeStyleProject; -import hudson.model.Result; - -import io.jenkins.plugins.coverage.CoveragePublisher; -import io.jenkins.plugins.coverage.adapter.CoberturaReportAdapter; -import io.jenkins.plugins.util.IntegrationTestWithJenkinsPerSuite; - -/** - * Integration test for checking if build failes when coverage decreases. - */ -class FailBuildIfCoverageDecreasesITest extends IntegrationTestWithJenkinsPerSuite { - private static final String COBERTURA_HIGHER_COVERAGE = "cobertura-higher-coverage.xml"; - private static final String COBERTURA_LOWER_COVERAGE = "cobertura-lower-coverage.xml"; - - /** - * Integration test freestyle projects for checking if build failes when coverage decreases. - * - * @throws IOException - * if creating Project throws Exception - */ - @Test - void freestyleProjectTestBuildResultDependingOnFailBuildIfCoverageDecreases() throws IOException { - FreeStyleProject freestyleProjectIfDecreasesSetFailTrue = createFreestyleProjectWithDecreasedCoverage( - COBERTURA_HIGHER_COVERAGE, - COBERTURA_LOWER_COVERAGE, CoverageDecreasedAction.FAIL_BUILD); - buildWithResult(freestyleProjectIfDecreasesSetFailTrue, Result.FAILURE); - FreeStyleProject projectIfDecreasesSetFailFalse = createFreestyleProjectWithDecreasedCoverage( - COBERTURA_HIGHER_COVERAGE, - COBERTURA_LOWER_COVERAGE, CoverageDecreasedAction.DONT_FAIL_BUILD); - buildWithResult(projectIfDecreasesSetFailFalse, Result.SUCCESS); - - } - - /** - * Integration test for pipeline projects for checking if build failes when coverage decreases. - */ - @Test - void pipelineProjectTestBuildResultDependingOnFailBuildIfCoverageDecreases() { - WorkflowJob pipelineProjectIfDecreasesSetFailTrue = createPipelineProjectWithDecreasedCoverage( - COBERTURA_HIGHER_COVERAGE, - COBERTURA_LOWER_COVERAGE, CoverageDecreasedAction.FAIL_BUILD); - buildWithResult(pipelineProjectIfDecreasesSetFailTrue, Result.FAILURE); - - WorkflowJob pipelineProjectIfDecreasesSetFailFalse = createPipelineProjectWithDecreasedCoverage( - COBERTURA_HIGHER_COVERAGE, - COBERTURA_LOWER_COVERAGE, CoverageDecreasedAction.DONT_FAIL_BUILD); - buildWithResult(pipelineProjectIfDecreasesSetFailFalse, Result.SUCCESS); - } - - /** - * Creates freestyle-project with second build containing decreased coverage. - * - * @param filename - * with higher coverage - * @param filenameOfDecreasedCoverage - * with decreased coverage - * @param coverageDecreased - * to set if build should fail when coverage decreases - * - * @return {@link FreeStyleProject} with decreased Coverage - * @throws IOException - * if publisher list of project is missing - */ - FreeStyleProject createFreestyleProjectWithDecreasedCoverage(final String filename, - final String filenameOfDecreasedCoverage, - final CoverageDecreasedAction coverageDecreased) - throws IOException { - - FreeStyleProject project = createFreeStyleProject(); - copyFilesToWorkspace(project, filename); - CoveragePublisher coveragePublisher = new CoveragePublisher(); - - CoberturaReportAdapter coberturaReportAdapter = new CoberturaReportAdapter(filename); - coveragePublisher.setAdapters(Collections.singletonList(coberturaReportAdapter)); - project.getPublishersList().add(coveragePublisher); - - //run first build - buildSuccessfully(project); - - //prepare second build - copyFilesToWorkspace(project, filenameOfDecreasedCoverage); - - CoberturaReportAdapter reportAdapter2 = new CoberturaReportAdapter( - filenameOfDecreasedCoverage); - - //set FailBuildIfCoverageDecreasedInChangeRequest on true - coveragePublisher.setFailBuildIfCoverageDecreasedInChangeRequest( - coverageDecreased == CoverageDecreasedAction.FAIL_BUILD); - - coveragePublisher.setAdapters(Collections.singletonList(reportAdapter2)); - project.getPublishersList().replace(coveragePublisher); - - return project; - } - - /** - * Creates pipeline-project with second build containing decreased coverage. - * - * @param filename - * with higher coverage - * @param filenameOfDecreasedCoverage - * with decreased coverage - * @param coverageDecreased - * to set if build should fail when coverage decreases - * - * @return {@link WorkflowJob} with decreased coverage - */ - WorkflowJob createPipelineProjectWithDecreasedCoverage(final String filename, - final String filenameOfDecreasedCoverage, - final CoverageDecreasedAction coverageDecreased) { - - WorkflowJob job = createPipelineWithWorkspaceFiles(filename, filenameOfDecreasedCoverage); - job.setDefinition(new CpsFlowDefinition("node {" - + " publishCoverage adapters: [istanbulCoberturaAdapter('" + filename + "')]" - + "}", true)); - buildSuccessfully(job); - - job.setDefinition(new CpsFlowDefinition("node {" - + " publishCoverage adapters: [istanbulCoberturaAdapter('" + filenameOfDecreasedCoverage + "')]," - + " failBuildIfCoverageDecreasedInChangeRequest: " + (coverageDecreased - == CoverageDecreasedAction.FAIL_BUILD) - + "}", true)); - - return job; - } - - enum CoverageDecreasedAction { FAIL_BUILD, DONT_FAIL_BUILD} -} - - diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/HealthReportITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/HealthReportITest.java deleted file mode 100644 index 7d061ef6c..000000000 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/HealthReportITest.java +++ /dev/null @@ -1,139 +0,0 @@ -package io.jenkins.plugins.coverage.model; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.Test; - -import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; -import org.jenkinsci.plugins.workflow.job.WorkflowJob; -import hudson.model.FreeStyleProject; -import hudson.model.HealthReport; -import hudson.model.Result; -import hudson.model.Run; - -import io.jenkins.plugins.coverage.CoveragePublisher; -import io.jenkins.plugins.coverage.adapter.JacocoReportAdapter; -import io.jenkins.plugins.coverage.threshold.Threshold; -import io.jenkins.plugins.util.IntegrationTestWithJenkinsPerSuite; - -import static org.assertj.core.api.Assertions.*; - -/** - * Integration Test for HealthReports. - */ -class HealthReportITest extends IntegrationTestWithJenkinsPerSuite { - private static final String JACOCO_FILE_NAME = "jacoco-analysis-model.xml"; - private static final int UNSTABLE_LINE_THRESHOLD = 100; - - /** - * No Build should succeed, no thresholds set, HealthScore should be 100%. - */ - @Test - void freestyleProjectShouldReturnSuccess() { - FreeStyleProject project = createFreeStyleProject(Thresholds.DONT_SET_ANY_THRESHOLDS); - - Run build = buildWithResult(project, Result.SUCCESS); - - verifyHealthReportBasedOnHighOrNoThresholdsSet(build, Thresholds.DONT_SET_ANY_THRESHOLDS); - } - - /** - * Build should be unstable, thresholds set, HealthScore is 0%. - */ - @Test - void freestyleProjectShouldReturnUnstable() { - FreeStyleProject project = createFreeStyleProject(Thresholds.SET_THRESHOLDS_TO_RETURN_UNSTABLE_BUILD); - - Run build = buildWithResult(project, Result.UNSTABLE); - verifyHealthReportBasedOnHighOrNoThresholdsSet(build, Thresholds.SET_THRESHOLDS_TO_RETURN_UNSTABLE_BUILD); - - } - - /** - * Used to create a freestyle project and apply/don't apply thresholds. - * - * @param thresholdsApplied - * to apply/don't apply thresholds - * - * @return project - */ - private FreeStyleProject createFreeStyleProject(final Thresholds thresholdsApplied) { - FreeStyleProject project = createFreeStyleProjectWithWorkspaceFiles(JACOCO_FILE_NAME); - CoveragePublisher coveragePublisher = new CoveragePublisher(); - JacocoReportAdapter jacocoReportAdapter = new JacocoReportAdapter(JACOCO_FILE_NAME); - coveragePublisher.setAdapters(Collections.singletonList(jacocoReportAdapter)); - - if (thresholdsApplied == Thresholds.SET_THRESHOLDS_TO_RETURN_UNSTABLE_BUILD) { - List thresholds = new ArrayList<>(); - Threshold lineThreshold = new Threshold("Line"); - lineThreshold.setUnstableThreshold(UNSTABLE_LINE_THRESHOLD); - thresholds.add(lineThreshold); - coveragePublisher.setGlobalThresholds(thresholds); - } - project.getPublishersList().add(coveragePublisher); - return project; - } - - /** - * Build of pipeline project should succeed and HealthScore is 100%. - */ - @Test - void pipelineShouldReturnSuccess() { - WorkflowJob job = createPipelineWithWorkspaceFiles(JACOCO_FILE_NAME); - job.setDefinition(new CpsFlowDefinition("node {" - + " publishCoverage adapters: [jacocoAdapter('" + JACOCO_FILE_NAME + "')]," - + " globalThresholds: [[failUnhealthy: false, thresholdTarget: 'Line']]" - + "}", true)); - - Run build = buildWithResult(job, Result.SUCCESS); - - verifyHealthReportBasedOnHighOrNoThresholdsSet(build, Thresholds.DONT_SET_ANY_THRESHOLDS); - } - - /** - * Build of pipeline project should be unstable and HealthScore is 0%. - */ - @Test - void pipelineShouldReturnUnstable() { - WorkflowJob job = createPipelineWithWorkspaceFiles(JACOCO_FILE_NAME); - job.setDefinition(new CpsFlowDefinition("node {" - + " publishCoverage adapters: [jacocoAdapter('" + JACOCO_FILE_NAME + "')]," - + " globalThresholds: [[failUnhealthy: true, thresholdTarget: 'Line', " + ", unstableThreshold: " - + UNSTABLE_LINE_THRESHOLD + "]]" - + "}", true)); - - Run build = buildWithResult(job, Result.UNSTABLE); - verifyHealthReportBasedOnHighOrNoThresholdsSet(build, Thresholds.SET_THRESHOLDS_TO_RETURN_UNSTABLE_BUILD); - } - - /** - * Verifies details of health report based on thresholds used or not. - * - * @param build - * to be verified - * @param wereThresholdsSet - * to verify health report depending on thresholds applied or not. - */ - private void verifyHealthReportBasedOnHighOrNoThresholdsSet(final Run build, - final Thresholds wereThresholdsSet) { - HealthReport healthReport = build.getAction(CoverageBuildAction.class).getBuildHealth(); - if (wereThresholdsSet == Thresholds.DONT_SET_ANY_THRESHOLDS) { - assertThat(healthReport.getScore()).isEqualTo(100); - assertThat(healthReport.getLocalizableDescription().toString()).isEqualTo("Coverage Healthy score is 100%"); - assertThat(healthReport.getIconUrl()).isEqualTo("health-80plus.png"); - } - else { - assertThat(healthReport.getScore()).isEqualTo(0); - assertThat(healthReport.getLocalizableDescription().toString()).isEqualTo("Coverage Healthy score is 0%"); - assertThat(healthReport.getIconUrl()).isEqualTo("health-00to19.png"); - } - } - - /** - * Enum for Thresholds being set or not to get different HealthReports in tests. - */ - - enum Thresholds { SET_THRESHOLDS_TO_RETURN_UNSTABLE_BUILD, DONT_SET_ANY_THRESHOLDS} -} diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/JobDslITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/JobDslITest.java deleted file mode 100644 index 7ae565bf2..000000000 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/JobDslITest.java +++ /dev/null @@ -1,60 +0,0 @@ -package io.jenkins.plugins.coverage.model; - -import org.junit.jupiter.api.Test; - -import edu.hm.hafner.metric.Metric; - -import hudson.model.View; -import hudson.views.ListViewColumn; - -import io.jenkins.plugins.casc.ConfigurationAsCode; -import io.jenkins.plugins.casc.ConfiguratorException; -import io.jenkins.plugins.coverage.model.visualization.dashboard.CoverageColumn; -import io.jenkins.plugins.util.IntegrationTestWithJenkinsPerTest; - -import static io.jenkins.plugins.coverage.model.Assertions.*; - -/** - * Tests the Job DSL Plugin. - * - * @author Ullrich Hafner - */ -class JobDslITest extends IntegrationTestWithJenkinsPerTest { - /** - * Creates a freestyle job from a YAML file and verifies that issue recorder finds warnings. - */ - @Test - void shouldCreateFreestyleJobUsingJobDslAndVerifyIssueRecorderWithDefaultConfiguration() { - configureJenkins("column-dsl.yaml"); - - View view = getJenkins().getInstance().getView("dsl-view"); - - assertThat(view).isNotNull(); - - assertThat(view.getColumns()) - .extracting(ListViewColumn::getColumnCaption) - .contains(new CoverageColumn().getColumnCaption()); - - assertThat(view.getColumns()).first() - .isInstanceOfSatisfying(CoverageColumn.class, - c -> { - assertThat(c).hasColumnCaption(Messages.Coverage_Column()) - .hasCoverageMetric(Metric.LINE.name()); - }); - } - - /** - * Helper method to get jenkins configuration file. - * - * @param fileName - * file with configuration. - */ - private void configureJenkins(final String fileName) { - try { - ConfigurationAsCode.get().configure(getResourceAsFile(fileName).toUri().toString()); - } - catch (ConfiguratorException e) { - throw new AssertionError(e); - } - } -} diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/MultipleInvocationsOfStepITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/MultipleInvocationsOfStepITest.java deleted file mode 100644 index e8ab97530..000000000 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/MultipleInvocationsOfStepITest.java +++ /dev/null @@ -1,131 +0,0 @@ -package io.jenkins.plugins.coverage.model; - -import java.util.List; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import edu.hm.hafner.metric.Coverage; -import edu.hm.hafner.metric.Coverage.CoverageBuilder; -import edu.hm.hafner.metric.Metric; - -import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; -import org.jenkinsci.plugins.workflow.job.WorkflowJob; -import hudson.model.Result; -import hudson.model.Run; - -import io.jenkins.plugins.util.IntegrationTestWithJenkinsPerSuite; - -import static org.assertj.core.api.Assertions.*; - -/** - * Test multiple invocations of step. - */ -class MultipleInvocationsOfStepITest extends IntegrationTestWithJenkinsPerSuite { - private static final String JACOCO_LOWER_BRANCH_COVERAGE = "jacoco-analysis-model.xml"; - private static final String JACOCO_HIGHER_BRANCH_COVERAGE = "jacoco-codingstyle.xml"; - private static final int JACOCO_HIGHER_BRANCH_COVERAGE_COVERED_VALUE = 109; - private static final int JACOCO_HIGHER_BRANCH_COVERAGE_MISSED_VALUE = 7; - private static final int JACOCO_LOWER_BRANCH_COVERAGE_COVERED_VALUE = 1661; - private static final int JACOCO_LOWER_BRANCH_COVERAGE_MISSED_VALUE = 214; - - /** - * Pipeline with multiple invocations of step, no tag set and higher coverage file first. - */ - @Test - void withoutTagFirstHigherFile() { - WorkflowJob job = createPipelineWithWorkspaceFiles(JACOCO_LOWER_BRANCH_COVERAGE, JACOCO_HIGHER_BRANCH_COVERAGE); - - job.setDefinition(new CpsFlowDefinition("node {" - + " publishCoverage adapters: [jacocoAdapter('" + JACOCO_HIGHER_BRANCH_COVERAGE + "')]\n" - + " publishCoverage adapters: [jacocoAdapter('" + JACOCO_LOWER_BRANCH_COVERAGE + "')]" - + "}", true)); - - List buildAction = getCoverageBuildActions(job, 1); - assertThat(buildAction.get(0).getBranchCoverage()) - .isEqualTo(new CoverageBuilder().setMetric(Metric.BRANCH).setCovered(JACOCO_LOWER_BRANCH_COVERAGE_COVERED_VALUE) - .setMissed(JACOCO_LOWER_BRANCH_COVERAGE_MISSED_VALUE) - .build()); - - } - - private List getCoverageBuildActions(final WorkflowJob job, final int numberOfBuildActions) { - Run build = buildWithResult(job, Result.SUCCESS); - List buildAction = build.getActions(CoverageBuildAction.class); - - assertThat(buildAction).hasSize(numberOfBuildActions); - return buildAction; - } - - /** - * Pipeline with multiple invocations of step, no tag set and lower coverage file first. - */ - @Test - void withoutTagFirstLowerFile() { - WorkflowJob job = createPipelineWithWorkspaceFiles(JACOCO_LOWER_BRANCH_COVERAGE, JACOCO_HIGHER_BRANCH_COVERAGE); - - job.setDefinition(new CpsFlowDefinition("node {" - + " publishCoverage adapters: [jacocoAdapter('" + JACOCO_LOWER_BRANCH_COVERAGE + "')]\n" - + " publishCoverage adapters: [jacocoAdapter('" + JACOCO_HIGHER_BRANCH_COVERAGE + "')]" - + "}", true)); - - List buildAction = getCoverageBuildActions(job, 1); - assertThat(buildAction.get(0).getBranchCoverage()) - .isEqualTo(new Coverage.CoverageBuilder().setCovered(JACOCO_HIGHER_BRANCH_COVERAGE_COVERED_VALUE) - .setMissed(JACOCO_HIGHER_BRANCH_COVERAGE_MISSED_VALUE) - .build()); - } - - /** - * Pipeline with multiple invocations of step and tag set. - */ - @Test - @Disabled("Bug") - void withDifferentTag() { - WorkflowJob job = createPipelineWithAdaptersAndTags("t2"); - - List buildAction = getCoverageBuildActions(job, 2); - assertThat(buildAction.get(0).getBranchCoverage()) - .isEqualTo(new Coverage.CoverageBuilder().setCovered(JACOCO_LOWER_BRANCH_COVERAGE_COVERED_VALUE) - .setMissed(JACOCO_LOWER_BRANCH_COVERAGE_MISSED_VALUE) - .build()); - assertThat(buildAction.get(1).getBranchCoverage()) - .isEqualTo(new Coverage.CoverageBuilder().setCovered(JACOCO_HIGHER_BRANCH_COVERAGE_COVERED_VALUE) - .setMissed(JACOCO_HIGHER_BRANCH_COVERAGE_MISSED_VALUE) - .build()); - } - - /** - * Pipeline with multiple invocations of step and tag set. - */ - @Test - void witSameTag() { - WorkflowJob job = createPipelineWithAdaptersAndTags("t1"); - - List buildAction = getCoverageBuildActions(job, 1); - assertThat(buildAction.get(0).getBranchCoverage()) - .isEqualTo(new Coverage.CoverageBuilder().setCovered(JACOCO_HIGHER_BRANCH_COVERAGE_COVERED_VALUE) - .setMissed(JACOCO_HIGHER_BRANCH_COVERAGE_MISSED_VALUE) - .build()); - } - - /** - * Creates pipeline project with jacoco adapters and tag 't1' and adapter with variable tag. - * - * @param tagTwo - * tag of second adapter - * - * @return Pipeline job with adapters and their tags - */ - private WorkflowJob createPipelineWithAdaptersAndTags(final String tagTwo) { - WorkflowJob job = createPipelineWithWorkspaceFiles(JACOCO_LOWER_BRANCH_COVERAGE, JACOCO_HIGHER_BRANCH_COVERAGE); - - job.setDefinition(new CpsFlowDefinition("node {" - + " publishCoverage adapters: [jacocoAdapter('" + JACOCO_LOWER_BRANCH_COVERAGE + "')]," - + " tag: 't1'\n" - + " publishCoverage adapters: [jacocoAdapter('" + JACOCO_HIGHER_BRANCH_COVERAGE + "')]," - + " tag: '" + tagTwo + "'\n" - + "}", true)); - return job; - } -} diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/QualityGatesITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/QualityGatesITest.java deleted file mode 100644 index 87d177d03..000000000 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/QualityGatesITest.java +++ /dev/null @@ -1,168 +0,0 @@ -package io.jenkins.plugins.coverage.model; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.Test; - -import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; -import org.jenkinsci.plugins.workflow.job.WorkflowJob; -import hudson.model.FreeStyleProject; -import hudson.model.Result; -import hudson.model.Run; -import jenkins.model.ParameterizedJobMixIn.ParameterizedJob; - -import io.jenkins.plugins.coverage.CoverageAction; -import io.jenkins.plugins.coverage.CoveragePublisher; -import io.jenkins.plugins.coverage.adapter.JacocoReportAdapter; -import io.jenkins.plugins.coverage.threshold.Threshold; -import io.jenkins.plugins.util.IntegrationTestWithJenkinsPerSuite; - -import static org.assertj.core.api.Assertions.*; - -/** - * Integration test for QualityGates/thresholds being respected. - */ -class QualityGatesITest extends IntegrationTestWithJenkinsPerSuite { - private static final float NOT_ACHIEVED_UNHEALTHY_THRESHOLD = 99.9f; - private static final float NOT_ACHIEVED_UNSTABLE_THRESHOLD = 99.9f; - private static final float ACHIEVED_UNHEALTHY_THRESHOLD = 50f; - private static final float ACHIEVED_UNSTABLE_THRESHOLD = 80f; - - private static final String JACOCO_FILE_NAME = "jacoco-analysis-model.xml"; - - /** - * Tests if when QualityGates being fulfilled, build returns success without failure message. - */ - @Test - void freeStyleShouldMeetQualityTargets() { - FreeStyleProject project = createFreeStyleProjectWithOneLineThresholds(ACHIEVED_UNHEALTHY_THRESHOLD, - ACHIEVED_UNSTABLE_THRESHOLD); - buildWithSuccess(project); - } - - /** - * Tests if when QualityGates for unstable not being fulfilled, build returns unstable with failure message. - */ - @Test - void freeStyleShouldNotMeetQualityTargets() { - - FreeStyleProject project = createFreeStyleProjectWithOneLineThresholds(NOT_ACHIEVED_UNHEALTHY_THRESHOLD, - NOT_ACHIEVED_UNSTABLE_THRESHOLD); - - verifiesBuildStatusAndFailMessage(NOT_ACHIEVED_UNHEALTHY_THRESHOLD, NOT_ACHIEVED_UNSTABLE_THRESHOLD, project); - } - - /** - * Creates a freestyle project with one line threshold. - * - * @param unhealthy - * threshold for line coverage - * @param unstable - * threshold for line coverage - * - * @return freestyle project with one line threshold - */ - FreeStyleProject createFreeStyleProjectWithOneLineThresholds(final float unhealthy, final float unstable) { - FreeStyleProject project = createFreeStyleProject(); - copyFilesToWorkspace(project, JACOCO_FILE_NAME); - CoveragePublisher coveragePublisher = new CoveragePublisher(); - JacocoReportAdapter jacocoReportAdapter = new JacocoReportAdapter(JACOCO_FILE_NAME); - coveragePublisher.setAdapters(Collections.singletonList(jacocoReportAdapter)); - coveragePublisher.setGlobalThresholds(createThresholdsContainingOneLineThreshold(unhealthy, unstable)); - project.getPublishersList().add(coveragePublisher); - return project; - } - - /** - * Creates threshold containing one line threshold. - * - * @param unhealthy - * threshold for line coverage - * @param unstable - * threshold for line coverage - * - * @return threshold containing one line threshold - */ - List createThresholdsContainingOneLineThreshold(final float unhealthy, final float unstable) { - List thresholds = new ArrayList<>(); - Threshold lineThreshold = new Threshold("Line"); - lineThreshold.setUnhealthyThreshold(unhealthy); - lineThreshold.setUnstableThreshold(unstable); - thresholds.add(lineThreshold); - return thresholds; - } - - /** - * Tests if build succeeds, when line thresholds within range. - */ - @Test - void pipelineShouldMeetQualityTargets() { - WorkflowJob job = createPipelineWithLineThreshold(ACHIEVED_UNHEALTHY_THRESHOLD, ACHIEVED_UNSTABLE_THRESHOLD); - buildWithSuccess(job); - } - - /** - * Tests if build is unstable, when line thresholds above coverage. - */ - @Test - void pipelineShouldNotMeetQualityTargets() { - - WorkflowJob job = createPipelineWithLineThreshold(NOT_ACHIEVED_UNHEALTHY_THRESHOLD, - NOT_ACHIEVED_UNSTABLE_THRESHOLD); - verifiesBuildStatusAndFailMessage(NOT_ACHIEVED_UNHEALTHY_THRESHOLD, NOT_ACHIEVED_UNSTABLE_THRESHOLD, job); - } - - /** - * Verifies if build is successful and has no fail message. - * - * @param job - * job to fest with - */ - private void buildWithSuccess(final ParameterizedJob job) { - buildWithResult(job, Result.SUCCESS); - } - - /** - * Verifies build status and fail message of job. - * - * @param unhealthyThreshold - * value of unhealthy threshold - * @param unstableThreshold - * value of unstable threshold - * @param job - * job to test with - */ - private void verifiesBuildStatusAndFailMessage(final float unhealthyThreshold, final float unstableThreshold, - final ParameterizedJob job) { - Run build = buildWithResult(job, Result.UNSTABLE); - - //test should run successfully too by using CoverageBuildAction.class instead of CoverageAction.class - String message = build.getAction(CoverageAction.class).getFailMessage(); - assertThat(message).isEqualTo( - "Build unstable because following metrics did not meet stability target: [Line {unstableThreshold=" - + unstableThreshold - + ", unhealthyThreshold=" + unhealthyThreshold + "}]."); - } - - /** - * Creates pipeline project with jacoco adapter and line thresholds. - * - * @param unhealthyThreshold - * value of unhealthy threshold - * @param unstableThreshold - * value of unstable threshold - * - * @return pipeline project - */ - private WorkflowJob createPipelineWithLineThreshold(final float unhealthyThreshold, final float unstableThreshold) { - WorkflowJob job = createPipelineWithWorkspaceFiles(JACOCO_FILE_NAME); - job.setDefinition(new CpsFlowDefinition("node {" - + " publishCoverage adapters: [jacocoAdapter('" + JACOCO_FILE_NAME + "')]," - + " globalThresholds: [[failUnhealthy: true, thresholdTarget: 'Line', unhealthyThreshold: " - + unhealthyThreshold + ", unstableThreshold: " + unstableThreshold + "]]" - + "}", true)); - return job; - } -} diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/ReportAggregationITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/ReportAggregationITest.java deleted file mode 100644 index d3e118cb1..000000000 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/ReportAggregationITest.java +++ /dev/null @@ -1,212 +0,0 @@ -package io.jenkins.plugins.coverage.model; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; -import org.jenkinsci.plugins.workflow.job.WorkflowJob; -import hudson.model.FreeStyleProject; -import hudson.model.Run; - -import io.jenkins.plugins.coverage.CoverageProcessor; -import io.jenkins.plugins.coverage.CoveragePublisher; -import io.jenkins.plugins.coverage.adapter.CoberturaReportAdapter; -import io.jenkins.plugins.coverage.adapter.CoverageAdapter; -import io.jenkins.plugins.coverage.adapter.JacocoReportAdapter; -import io.jenkins.plugins.coverage.adapter.JavaXMLCoverageReportAdapter; -import io.jenkins.plugins.coverage.targets.CoverageElement; -import io.jenkins.plugins.coverage.targets.CoverageResult; -import io.jenkins.plugins.util.IntegrationTestWithJenkinsPerSuite; - -import static org.assertj.core.api.Assertions.*; - -/** - * Integration test for report aggregation. - */ -class ReportAggregationITest extends IntegrationTestWithJenkinsPerSuite { - private static final String COBERTURA_LOWER_COVERAGE_XML = "cobertura-lower-coverage.xml"; - private static final String COBERTURA_LOTS_OF_DATA_XML = "cobertura-lots-of-data.xml"; - - private static final String JACOCO_ANALYSIS_MODEL_XML = "jacoco-analysis-model.xml"; - private static final String JACOCO_CODINGSTYLE_XML = "jacoco-codingstyle.xml"; - - /** - * Checks aggregated coverage result for freestyle job with two jacoco files. - * - * @throws IOException - * due to verifyJacocoReportAggregation() - * @throws ClassNotFoundException - * due to verifyJacocoReportAggregation() - */ - @Test - void freeStyleProjectCheckReportAggregationWithJacocoFiles() throws ClassNotFoundException, IOException { - FreeStyleProject project = createFreeStyleProjectWithSpecifiedAdapterAndFiles(UsedAdapter.JACOCO, - JACOCO_CODINGSTYLE_XML, JACOCO_ANALYSIS_MODEL_XML); - - Run build = buildSuccessfully(project); - verifyJacocoReportAggregation(build); - } - - /** - * Checks aggregated coverage result for pipeline job with two jacoco files. - * - * @throws IOException - * due to verifyJacocoReportAggregation() - * @throws ClassNotFoundException - * due to verifyJacocoReportAggregation() - */ - @Test - void pipelineProjectCheckReportAggregationWithJacocoFiles() throws IOException, ClassNotFoundException { - WorkflowJob job = createPipelineProjectWithSpecifiedAdapterAndFiles(UsedAdapter.JACOCO, JACOCO_CODINGSTYLE_XML, - JACOCO_ANALYSIS_MODEL_XML); - - Run build = buildSuccessfully(job); - verifyJacocoReportAggregation(build); - } - - /** - * Verifies result of aggregated report of two jacoco files. - * - * @param build - * which contains aggregated report - * - * @throws IOException - * due to recoverCoverageResult() - * @throws ClassNotFoundException - * due to recoverCoverageResult() - */ - private void verifyJacocoReportAggregation(final Run build) throws IOException, ClassNotFoundException { - CoverageResult aggregatedResult = CoverageProcessor.recoverCoverageResult(build); - aggregatedResult.isAggregatedLevel(); - assertThat(aggregatedResult.getCoverage(CoverageElement.LINE).toString()).isEqualTo("095.16 (5825/6121)"); - assertThat(aggregatedResult.getCoverage(CoverageElement.CONDITIONAL).toString()).isEqualTo( - "088.63 (1653/1865)"); - } - - /** - * Checks aggregated coverage result for freestyle job with two jacoco files. - * - * @throws IOException - * due to verifyJacocoReportAggregation() - * @throws ClassNotFoundException - * due to verifyJacocoReportAggregation() - */ - @Test - @Disabled("Bug") - void freeStyleProjectCheckReportAggregationWithCoberturaFiles() throws IOException, ClassNotFoundException { - FreeStyleProject project = createFreeStyleProjectWithSpecifiedAdapterAndFiles(UsedAdapter.COBERTURA, - COBERTURA_LOWER_COVERAGE_XML, COBERTURA_LOTS_OF_DATA_XML); - Run build = buildSuccessfully(project); - verifyCoberturaReportAggregation(build); - } - - /** - * Checks aggregated coverage result for pipeline job with two cobertura files. - * - * @throws IOException - * due to verifyCoberturaReportAggregation() - * @throws ClassNotFoundException - * due to verifyCoberturaReportAggregation() - */ - @Test - void pipelineProjectCheckReportAggregationWithCoberturaFiles() throws IOException, ClassNotFoundException { - WorkflowJob job = createPipelineProjectWithSpecifiedAdapterAndFiles(UsedAdapter.COBERTURA, - COBERTURA_LOWER_COVERAGE_XML, COBERTURA_LOTS_OF_DATA_XML); - - Run build = buildSuccessfully(job); - verifyCoberturaReportAggregation(build); - } - - /** - * Verifies result of aggregated report of two cobertura files. - * - * @param build - * which contains aggregated report - * - * @throws IOException - * due to recoverCoverageResult() - * @throws ClassNotFoundException - * due to recoverCoverageResult() - */ - private void verifyCoberturaReportAggregation(final Run build) throws IOException, ClassNotFoundException { - CoverageResult aggregatedResult = CoverageProcessor.recoverCoverageResult(build); - aggregatedResult.isAggregatedLevel(); - assertThat(aggregatedResult.getCoverage(CoverageElement.LINE).toString()).isEqualTo("065.18 (526/807)"); - assertThat(aggregatedResult.getCoverage(CoverageElement.CONDITIONAL).toString()).isEqualTo("048.50 (259/534)"); - - } - - /** - * Used to create a freestyle project which creates a build with an aggregated report. - * - * @param usedAdapter - * which is used fo - * @param firstFile - * for build - * @param secondFile - * for build - * - * @return project with merged reports - */ - private FreeStyleProject createFreeStyleProjectWithSpecifiedAdapterAndFiles(final UsedAdapter usedAdapter, - final String firstFile, final String secondFile) { - FreeStyleProject project = createFreeStyleProject(); - copyFilesToWorkspace(project, firstFile, secondFile); - List coverageAdapters = new ArrayList<>(); - JavaXMLCoverageReportAdapter adapter1; - JavaXMLCoverageReportAdapter adapter2; - if (usedAdapter == UsedAdapter.COBERTURA) { - adapter1 = new CoberturaReportAdapter(firstFile); - adapter2 = new CoberturaReportAdapter(secondFile); - } - else { - adapter1 = new JacocoReportAdapter(firstFile); - adapter2 = new JacocoReportAdapter(secondFile); - } - - adapter1.setMergeToOneReport(true); - adapter2.setMergeToOneReport(true); - - coverageAdapters.add(adapter1); - coverageAdapters.add(adapter2); - - CoveragePublisher coveragePublisher = new CoveragePublisher(); - coveragePublisher.setAdapters(coverageAdapters); - - project.getPublishersList().add(coveragePublisher); - return project; - } - - /** - * Used to create a pipeline project which creates a build with an aggregated reports. - * - * @param usedAdapter - * which is used fo - * @param firstFile - * for build - * @param secondFile - * for build - * - * @return pipeline project with merged reports - */ - private WorkflowJob createPipelineProjectWithSpecifiedAdapterAndFiles(final UsedAdapter usedAdapter, - final String firstFile, final String secondFile) { - WorkflowJob job = createPipelineWithWorkspaceFiles(firstFile, secondFile); - String adapterValue = (usedAdapter == UsedAdapter.JACOCO) ? "jacocoAdapter" : "istanbulCoberturaAdapter"; - job.setDefinition(new CpsFlowDefinition("node {" - + " publishCoverage adapters: [" + adapterValue + "('mergeToOneReport: true," + firstFile - + "')," - + adapterValue + "('mergeToOneReport: true," + secondFile + "')]" - + "}", true)); - return job; - } - - /** - * Enum for setting adapter in freestyle and pipeline projects. - */ - enum UsedAdapter { COBERTURA, JACOCO} -} diff --git a/plugin/src/test/java/io/jenkins/plugins/coverage/model/SkipPublishingOfChecksITest.java b/plugin/src/test/java/io/jenkins/plugins/coverage/model/SkipPublishingOfChecksITest.java deleted file mode 100644 index 7717e9bd7..000000000 --- a/plugin/src/test/java/io/jenkins/plugins/coverage/model/SkipPublishingOfChecksITest.java +++ /dev/null @@ -1,136 +0,0 @@ -package io.jenkins.plugins.coverage.model; - -import java.util.Collections; - -import org.junit.jupiter.api.Test; - -import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; -import org.jenkinsci.plugins.workflow.job.WorkflowJob; -import hudson.model.FreeStyleProject; -import hudson.model.Run; - -import io.jenkins.plugins.coverage.CoveragePublisher; -import io.jenkins.plugins.coverage.adapter.JacocoReportAdapter; -import io.jenkins.plugins.util.IntegrationTestWithJenkinsPerSuite; - -import static org.assertj.core.api.Assertions.*; - -/** - * Tests if publishing of checks can be skipped. - */ -class SkipPublishingOfChecksITest extends IntegrationTestWithJenkinsPerSuite { - private static final String JACOCO_FILENAME = "jacoco-analysis-model.xml"; - - /** - * Tests publishing of checks in freestyle-project when skip publishing checks is false. - */ - @Test - void freeStylePublishingOfChecks() { - FreeStyleProject project = getFreeStyleProjectWithJacoco(Checks.PUBLISH_CHECKS); - checkConsoleLog(buildSuccessfully(project), Checks.PUBLISH_CHECKS); - } - - /** - * Tests publishing of checks in pipeline-project when skip publishing checks is false. - */ - @Test - void pipelinePublishingOfChecks() { - WorkflowJob job = getPipelineProjectWithJacoco(Checks.PUBLISH_CHECKS); - checkConsoleLog(buildSuccessfully(job), Checks.PUBLISH_CHECKS); - } - - /** - * Tests publishing of checks in freestyle-project is skipped when skip publishing checks is true. - */ - @Test - void freeStyleSkipPublishingOfChecks() { - FreeStyleProject project = getFreeStyleProjectWithJacoco(Checks.SKIP_CHECKS); - checkConsoleLog(buildSuccessfully(project), Checks.SKIP_CHECKS); - } - - /** - * Tests publishing of checks in pipeline-project is skipped when skip publishing checks is true. - */ - @Test - void pipelineSkipPublishingOfChecks() { - WorkflowJob job = getPipelineProjectWithJacoco(Checks.SKIP_CHECKS); - checkConsoleLog(buildSuccessfully(job), Checks.SKIP_CHECKS); - } - - /** - * Checks console log. - * - * @param build - * the successful build - * @param skipPublishingChecks - * if publishing checks should be skipped - */ - private void checkConsoleLog(final Run build, final Checks skipPublishingChecks) { - String consoleLog = getConsoleLog(build); - if (skipPublishingChecks == Checks.PUBLISH_CHECKS) { - assertThat(consoleLog).contains("[Checks API] No suitable checks publisher found."); - } - else if (skipPublishingChecks == Checks.SKIP_CHECKS) { - assertThat(consoleLog).contains("Publishing Coverage report...."); - - } - } - - /** - * Creates freestyle project with jacoco file and adapter. - * - * @param skipPublishingChecks - * if publishing checks should be skipped - * - * @return {@link FreeStyleProject} with jacoco file and adapter - */ - private FreeStyleProject getFreeStyleProjectWithJacoco(final Checks skipPublishingChecks) { - FreeStyleProject project = createFreeStyleProjectWithWorkspaceFiles(JACOCO_FILENAME); - - CoveragePublisher coveragePublisher = new CoveragePublisher(); - JacocoReportAdapter jacocoReportAdapter = new JacocoReportAdapter(JACOCO_FILENAME); - if (skipPublishingChecks == Checks.SKIP_CHECKS) { - coveragePublisher.setSkipPublishingChecks(true); - assertThat(coveragePublisher.isSkipPublishingChecks()).isEqualTo(true); - } - else if (skipPublishingChecks == Checks.PUBLISH_CHECKS) { - coveragePublisher.setSkipPublishingChecks(false); - assertThat(coveragePublisher.isSkipPublishingChecks()).isEqualTo(false); - } - coveragePublisher.setAdapters(Collections.singletonList(jacocoReportAdapter)); - project.getPublishersList().add(coveragePublisher); - return project; - } - - /** - * Creates a build of a pipeline project with jacoco file and adapter. - * - * @param skipPublishingChecks - * to set if publishing checks should be skipped - * - * @return build of project with scm - */ - private WorkflowJob getPipelineProjectWithJacoco(final Checks skipPublishingChecks) { - String skipPublishingChecksValue = ""; - - if (skipPublishingChecks == Checks.SKIP_CHECKS) { - skipPublishingChecksValue = "true"; - } - else if (skipPublishingChecks == Checks.PUBLISH_CHECKS) { - skipPublishingChecksValue = "false"; - } - - WorkflowJob job = createPipelineWithWorkspaceFiles(JACOCO_FILENAME); - job.setDefinition(new CpsFlowDefinition("node {" - + " publishCoverage adapters: [jacocoAdapter('" + JACOCO_FILENAME + "')],\n" - + " skipPublishingChecks: " + skipPublishingChecksValue - + "}", true)); - - return job; - } - - /** - * Enum to set skipping of publishing of checks. - */ - enum Checks { PUBLISH_CHECKS, SKIP_CHECKS} -} diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/model/AcuCobolParser.java.txt b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/AcuCobolParser.java.txt similarity index 100% rename from plugin/src/test/resources/io/jenkins/plugins/coverage/model/AcuCobolParser.java.txt rename to plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/AcuCobolParser.java.txt diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/model/cobertura-higher-coverage.xml b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/cobertura-higher-coverage.xml similarity index 100% rename from plugin/src/test/resources/io/jenkins/plugins/coverage/model/cobertura-higher-coverage.xml rename to plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/cobertura-higher-coverage.xml diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/model/cobertura-lots-of-data.xml b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/cobertura-lots-of-data.xml similarity index 100% rename from plugin/src/test/resources/io/jenkins/plugins/coverage/model/cobertura-lots-of-data.xml rename to plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/cobertura-lots-of-data.xml diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/model/cobertura-lower-coverage.xml b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/cobertura-lower-coverage.xml similarity index 100% rename from plugin/src/test/resources/io/jenkins/plugins/coverage/model/cobertura-lower-coverage.xml rename to plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/cobertura-lower-coverage.xml diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/model/cobertura-npe.xml b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/cobertura-npe.xml similarity index 100% rename from plugin/src/test/resources/io/jenkins/plugins/coverage/model/cobertura-npe.xml rename to plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/cobertura-npe.xml diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/model/column-dsl.yaml b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/column-dsl.yaml similarity index 100% rename from plugin/src/test/resources/io/jenkins/plugins/coverage/model/column-dsl.yaml rename to plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/column-dsl.yaml diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/model/file-changes-test-after.xml b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/file-changes-test-after.xml similarity index 100% rename from plugin/src/test/resources/io/jenkins/plugins/coverage/model/file-changes-test-after.xml rename to plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/file-changes-test-after.xml diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/model/file-changes-test-before.xml b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/file-changes-test-before.xml similarity index 100% rename from plugin/src/test/resources/io/jenkins/plugins/coverage/model/file-changes-test-before.xml rename to plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/file-changes-test-before.xml diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/model/forensics_integration.xml b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/forensics_integration.xml similarity index 100% rename from plugin/src/test/resources/io/jenkins/plugins/coverage/model/forensics_integration.xml rename to plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/forensics_integration.xml diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/model/forensics_integration_reference.xml b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/forensics_integration_reference.xml similarity index 100% rename from plugin/src/test/resources/io/jenkins/plugins/coverage/model/forensics_integration_reference.xml rename to plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/forensics_integration_reference.xml diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/model/jacoco-acu-cobol-parser.xml b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/jacoco-acu-cobol-parser.xml similarity index 100% rename from plugin/src/test/resources/io/jenkins/plugins/coverage/model/jacoco-acu-cobol-parser.xml rename to plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/jacoco-acu-cobol-parser.xml diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/model/jacoco-analysis-model.xml b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/jacoco-analysis-model.xml similarity index 100% rename from plugin/src/test/resources/io/jenkins/plugins/coverage/model/jacoco-analysis-model.xml rename to plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/jacoco-analysis-model.xml diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/model/jacoco-codingstyle.xml b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/jacoco-codingstyle.xml similarity index 100% rename from plugin/src/test/resources/io/jenkins/plugins/coverage/model/jacoco-codingstyle.xml rename to plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/jacoco-codingstyle.xml diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/model/mutations.xml b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/mutations.xml similarity index 100% rename from plugin/src/test/resources/io/jenkins/plugins/coverage/model/mutations.xml rename to plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/mutations.xml diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/model/visualization/code/SourcecodeTest.html b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/visualization/code/SourcecodeTest.html similarity index 100% rename from plugin/src/test/resources/io/jenkins/plugins/coverage/model/visualization/code/SourcecodeTest.html rename to plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/visualization/code/SourcecodeTest.html diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/model/visualization/code/SourcecodeTestCC.html b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/visualization/code/SourcecodeTestCC.html similarity index 100% rename from plugin/src/test/resources/io/jenkins/plugins/coverage/model/visualization/code/SourcecodeTestCC.html rename to plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/visualization/code/SourcecodeTestCC.html diff --git a/plugin/src/test/resources/io/jenkins/plugins/coverage/model/visualization/code/SourcecodeTestICC.html b/plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/visualization/code/SourcecodeTestICC.html similarity index 100% rename from plugin/src/test/resources/io/jenkins/plugins/coverage/model/visualization/code/SourcecodeTestICC.html rename to plugin/src/test/resources/io/jenkins/plugins/coverage/metrics/visualization/code/SourcecodeTestICC.html From c97644790e7bbb15e3184aa1a0994be2a97c51fa Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Sun, 6 Nov 2022 23:39:02 +0100 Subject: [PATCH 29/29] Bump version of coverage model to 0.1.0. --- plugin/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/pom.xml b/plugin/pom.xml index c97ba29b6..e945c30d1 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -68,8 +68,8 @@ edu.hm.hafner - metric-model - 1.0.0-SNAPSHOT + coverage-model + 0.1.0 io.jenkins.plugins