From 48b46c855d6d06230f5d1689f387bdbbd2567886 Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Sun, 4 Feb 2024 17:42:21 +0100 Subject: [PATCH] Use the new reference job API of the forensics plugin. Remove the warnings plugin specific reference job implementation. --- doc/Documentation.md | 144 ++++-------------- plugin/pom.xml | 5 +- .../analysis/core/model/DeltaReport.java | 66 ++++---- .../analysis/core/steps/IssuesPublisher.java | 78 +++------- .../analysis/core/steps/IssuesRecorder.java | 59 +------ .../core/steps/PublishIssuesStep.java | 46 +----- .../analysis/core/steps/RecordIssuesStep.java | 61 +------- .../analysis/core/model/DeltaReportTest.java | 18 +-- .../steps/WarningChecksPublisherITest.java | 18 ++- .../testutil/WarningsIntegrationTest.java | 4 +- .../warnings/integrations/JobDslITest.java | 1 - .../integrations/TokenMacroITest.java | 1 + .../steps/AffectedFilesResolverITest.java | 6 +- .../warnings/steps/GitForensicsITest.java | 8 +- .../steps/MiscIssuesRecorderITest.java | 39 ++--- .../warnings/steps/QualityGateITest.java | 53 ++++--- .../warnings/steps/ReferenceFinderITest.java | 80 ++++------ .../analysis/warnings/steps/StepsITest.java | 23 +-- .../integrations/job-dsl-warnings-ng.yaml | 1 - 19 files changed, 200 insertions(+), 511 deletions(-) diff --git a/doc/Documentation.md b/doc/Documentation.md index 9038beb195..56359bf14b 100644 --- a/doc/Documentation.md +++ b/doc/Documentation.md @@ -31,15 +31,15 @@ There are more than hundred [report formats](../SUPPORTED-FORMATS.md) supported. 2. The plugin publishes a report of the issues found in your build, so you can navigate to a summary report from the main build page. From there you can also dive into the details: - distribution of new, fixed and outstanding issues - - distribution of the issues by severity, category, type, module, or package + - distribution of issues in modified files + - distribution of issues by severity, category, type, module, or package - list of all issues including helpful comments from the reporting tool - annotated source code of the affected files - trend charts of the issues - blames that identify the origin (commit, author, etc.) of a warning - forensics that give insight to your SCM history -:exclamation: The plugin does not run the static analysis, it just visualizes the results reported by such tools. -You still need to enable and configure the static analysis tool in your build file or Jenkinsfile. +:exclamation: The plugin does not run the static analysis, it just visualizes the results reported by such tools. You still need to enable and configure the static analysis tool in your build file or Jenkinsfile. ## Table of Contents @@ -65,7 +65,7 @@ You still need to enable and configure the static analysis tool in your build fi * [Simple Pipeline configuration](#simple-pipeline-configuration) * [Declarative Pipeline configuration](#declarative-pipeline-configuration) * [Advanced Pipeline configuration](#advanced-pipeline-configuration) -* [New features](#new-features) +* [Feature Overview](#new-features) * [Issues history: new, fixed, and outstanding issues](#issues-history-new-fixed-and-outstanding-issues) * [Severities](#severities) * [Build trend](#build-trend) @@ -90,10 +90,6 @@ You still need to enable and configure the static analysis tool in your build fi * [Details of the analysis result](#details-of-the-analysis-result) * [Token macro support](#token-macro-support) * [Warnings Checks (for GitHub projects)](#warnings-checks-for-github-projects) -* [Transition from the static analysis suite](#transition-from-the-static-analysis-suite) - * [Migration of Pipelines](#migration-of-pipelines) - * [Migration of all other jobs](#migration-of-all-other-jobs) - * [Migration of plugins depending on analysis-core](#migration-of-plugins-depending-on-analysis-core) Created by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc) @@ -200,7 +196,7 @@ of the already supported formats. E.g., several tools export their issues into t want to use all features of the Warnings Plugin it would be even better if you would export the information into the *native* [XML](../plugin/src/test/resources/io/jenkins/plugins/analysis/warnings/steps/warnings-issues.xml) or [JSON](../plugin/src/test/resources/io/jenkins/plugins/analysis/warnings/steps/issues.json) format (this parser uses the ID `issues`). -These formats are already registered in the user interface and you can use them out-of-the-box. You can even provide +These formats are already registered in the user interface, and you can use them out-of-the-box. You can even provide issues in a simple log file that contains single lines of JSON issues, see [example](../plugin/src/test/resources/io/jenkins/plugins/analysis/warnings/steps/json-issues.log). @@ -227,8 +223,7 @@ text into an issue instance. Here is an example of such a Groovy based parser: #### Creating a Groovy parser programmatically -The Groovy based parser can also be created using a Groovy script from within a pipeline, a Jenkins startup script or -the script console. +The Groovy based parser can also be created using a Groovy script from within a pipeline, a Jenkins startup script or the script console. See the following example: @@ -293,7 +288,7 @@ recordIssues sourceCodeEncoding: 'UTF-8', In order to let the plugin parse and display your source code files it is required to set the correct encoding for these files. Additionally, if your source code is not located in the workspace (e.g. it has been checked out into a shared agent folder), the plugin will not automatically find your source files. -In order to let the plugin display those files you can add an additional source directory: +In order to let the plugin display those files, you can add a source directory: ![affected files configuration](images/affected-files.png) @@ -308,7 +303,7 @@ recordIssues sourceCodeEncoding: 'ISO-8859-1', sourceDirectory: '/path/to/source ``` Note that the content of files outside of the workspace might be sensitive. In order -to prevent showing such files by accident you need to provide an allowlist of allowed source code directories +to prevent showing such files by accident, you need to provide an allowlist of allowed source code directories in Jenkins system configuration screen: ![source directory configuration](images/source-directories-white-list.png) @@ -334,40 +329,17 @@ One unique feature of the Warnings Next Generation plugin is the classification Using this classification one can see what new issues have been caused by a pull request, or if new and fixed issues are in balance or if the quality of the project is degenerating or improving. -In order to compute this classification, the plugin requires a reference build (baseline). New, fixed, and outstanding -issues are then computed by comparing the issues in the current build and in the baseline. -Starting with release 8.5.0 the selection of the reference build is delegated to the -[Forensics API plugin](https://github.com/jenkinsci/forensics-api-plugin) and the corresponding Git implementation in the -[Git Forensics plugin](https://github.com/jenkinsci/git-forensics-plugin). This new implementation in the Git Forensics -plugin is much more versatile than the old default implementation that simply returned a previous build of a configurable -Jenkins job. +To compute this classification, the plugin requires a reference build (baseline). New, fixed, and outstanding issues are then computed by comparing the issues in the current build and in the baseline. The selection of the reference build is delegated to the [Forensics API plugin](https://github.com/jenkinsci/forensics-api-plugin) and the corresponding Git implementation in the [Git Forensics plugin](https://github.com/jenkinsci/git-forensics-plugin). So make sure that you run the corresponding steps or post build actions in your build. When selecting a baseline, we need to distinguish two different use cases, which are documented in the next sections. ### Selecting a baseline from the current job -When a team wants to investigate how the quality of the project changes over the time, we need to simply -look back in the history of the same Jenkins job and select another build that we can use to compare the results -with. Such a Jenkins job typically build the main branch of the source control system. -This behavior was part of the Warnings' Plugin since the beginning and is still available. You do not need to -configure anything to get this functionality: just skip the computation of the reference build (see next section). -You can affect the selection of the reference build with the following properties: - -- `ignoreQualityGate`: This option is only available if you have a quality gate enabled for your job. Then you can - enable this option to always select the previous build as baseline. That means, that the plugin marks only those - issues as new, that have been submitted in the current build. Previously new issues in older builds will the plugin - convert and classify as outstanding issues. Think carefully before selecting this option because then manually - starting a new build (without source code changes) simply will convert all previously new issues to outstanding. - Therefore, this option is disabled by default: a reference build is always a build that passed all quality gates. As - soon as a build does not pass the quality gate, the reference will be frozen until all new issues will be resolved - again. This means, that new issues will be aggregated from build to build until the original cause for the failure - and all those additional new issues have been resolved. This helps much more to keep your project clean: as soon as - there are new issues, Jenkins will mark all builds as unstable until the issues have been resolved. - -- `ignoreFailedBuilds`: This option determines if failed builds should be selected as baseline or not. - This option is enabled by default, since analysis results might be inaccurate if the build failed. - If unchecked, every build that contains a static analysis result will be considered as a baseline, - even if the build failed. +When a team wants to investigate how the quality of the project changes over time, we need to look back in the history of the same Jenkins job. In this case, we need to select a previous build of the same job that we can use to compare the results with. Such a Jenkins job typically builds the main branch of the source control system. To get this functionality, you need to invoke the [discoverReferenceBuild](https://www.jenkins.io/doc/pipeline/steps/forensics-api/#discoverreferencebuild-discover-reference-build) step of the forensics plugin before running one of the steps of the warnings plugin. You do not need to specify a name for the reference job, as by default, the current job will be used. + +You can affect the selection of the reference build with the following property: + +- `ignoreQualityGate`: This option is only available if you have a quality gate enabled for your job. Then you can enable this option to always select the previous build as baseline. That means that the plugin marks only those issues as new that have been submitted in the current build. Previously new issues in older builds will the plugin convert and classify as outstanding issues. Think carefully before selecting this option because then manually starting a new build (without source code changes) will convert all previously new issues to outstanding. Therefore, this option is disabled by default: a reference build is always a build that passed all quality gates. As soon as a build does not pass the quality gate, the reference will be frozen until all new issues will be resolved again. This means, that new issues will be aggregated from build to build until the original cause for the failure, and all those additional new issues have been resolved. This helps much more to keep your project clean: as soon as there are new issues, Jenkins will mark all builds as unstable until the issues have been resolved. You can use checkboxes to change the default values of these options, see image below. @@ -376,6 +348,7 @@ You can use checkboxes to change the default values of these options, see image These options are available for pipelines as well: ```groovy +discoverReferenceBuild() recordIssues tool: java(), ignoreQualityGate: false, ignoreFailedBuilds: true ``` @@ -384,47 +357,30 @@ recordIssues tool: java(), ignoreQualityGate: false, ignoreFailedBuilds: true :warning: This feature requires the installation of an additional plugin: [Git Forensics Plugin](https://github.com/jenkinsci/git-forensics-plugin). -For more complex branch source projects (i.e., projects that build several branches and pull requests in a -connected job hierarchy) it makes more sense to select a reference build from a job -that builds the actual target branch (i.e., the branch the current changes will be merged into). Here one typically is -interested what changed in the branch or pull request over the main branch (or any other -target branch). That means we want to see what new warnings will be submitted by a branch or pull request -if the team merges the changes. +For more complex [multibranch projects](https://www.jenkins.io/doc/book/pipeline/multibranch/), it makes more sense to select a reference build from a job that builds the actual target branch (i.e., the branch the current changes will be merged into). Such multibranch Pipelines build several branches and pull requests in a connected job hierarchy. -If you are using a Git branch source project, the Jenkins job that builds the target branch -will be selected automatically by running the reference recorder step. Simply call the step `discoverGitReferenceBuild` -before any of static analysis steps: +Here one typically is interested what changed in the branch or pull request over the main branch (or any other target branch). That means we want to see what new warnings will be submitted by a branch or pull request if the team merges the changes. + +If you are using a Git branch source project, the Jenkins job that builds the target branch will be selected automatically by running the reference recorder step. Simply call the step [discoverGitReferenceBuild](https://www.jenkins.io/doc/pipeline/steps/git-forensics/#discovergitreferencebuild-discover-git-reference-build) before any of static analysis steps: ```groovy discoverGitReferenceBuild() recordIssues tool: checkStyle(pattern: 'checkstyle-result.xml') ``` -Selecting the correct reference build is not that easy as it looks, since the main branch of a project will evolve -more frequently than a specific feature or bugfix branch. That means if we want to compare the results of a pull -request with the results of the main branch we need to select a build from the main branch that contains only -commits that are also part of the branch of the associated pull request. +Selecting the correct reference build is not that easy as it looks, since the main branch of a project will evolve more frequently than a specific feature or bugfix branch. That means if we want to compare the results of a pull request with the results of the main branch, we need to select a build carefully. Such a build must be in the job for the main branch and must contain only commits that are also part of the branch of the associated pull request. -Therefore, the Git Forensics plugin automatically tracks all commits of a Jenkins build and uses this information to -identify a build in the target branch that matches best with the commits in the current branch. Please have a look -at the [documentation of the Git Forensics plugin](https://github.com/jenkinsci/git-forensics-plugin) to see how +Therefore, the Git Forensics plugin automatically tracks all commits of a Jenkins build and uses this information to identify a build in the target branch that matches best with the commits in the current branch. Please have a look at the [documentation of the Git Forensics plugin](https://github.com/jenkinsci/git-forensics-plugin) to see how this is achieved in detail. -This algorithm can be used for plain Git SCM freestyle projects or pipelines as well. In this case, we cannot get -the target branch information automatically from the Git branch source API. Therefore, you need to manually -specify the Jenkins job that builds the target branch in the parameter `referenceJob`. See the following sample pipeline -snippet for an example on how to discover a baseline from such a reference job: +This algorithm can be used for plain Git SCM freestyle projects or pipelines as well. In this case, we cannot get the target branch information automatically from the Git branch source API. Therefore, you need to manually specify the Jenkins job that builds the target branch in the parameter `referenceJob`. See the following sample pipeline snippet for an example on how to discover a baseline from such a reference job: ```groovy discoverGitReferenceBuild referenceJob: 'my-reference-job' recordIssues tool: checkStyle(pattern: 'checkstyle-result.xml') ``` -Please note that the parameter `referenceJob` can be used even for non Git projects: in that case, the algorithm selects -the baseline from the specified job using the same techniques as described in -[the previous section](#selecting-a-baseline-from-the-current-job). However, this may lead to inaccurate results if the -main branch diverged too heavily from the starting point of the current branch. See the following sample -snippet that shows the relevant options for this feature. +Please note that the parameter `referenceJob` can be used even for non-Git projects: in that case, the algorithm selects the baseline from the specified job using the same techniques as described in [the previous section](#selecting-a-baseline-from-the-current-job). However, this may lead to inaccurate results if the main branch diverged too heavily from the starting point of the current branch. See the following sample snippet that shows the relevant options for this feature. ```groovy discoverGitReferenceBuild referenceJob: 'my-project/main-branch' @@ -438,9 +394,7 @@ A corresponding Freestyle configuration is given in the following screenshot. ### Filtering issues -The created report of issues can be filtered afterwards. You can specify an arbitrary number of include or exclude -filters. Currently, there is support for filtering issues by module name, package or namespace name, file name, -category or type. +The created report of issues can be filtered afterward. You can specify an arbitrary number of include or exclude filters. Currently, there is support for filtering issues by module name, package or namespace name, file name, category or type. ![filter configuration](images/filter.png) @@ -614,9 +568,9 @@ node { } ``` -## New features +## Feature Overview -The most important new features are described in the following sections. +The most important features are described in the following sections. ### Issues history: new, fixed, and outstanding issues @@ -1107,49 +1061,3 @@ withChecks('My Custom Checks Name') { recordIssues tool: java(pattern: '*.log') } ``` - -## Transition from the static analysis suite - -Previously the same set of features has been provided by the plugins of the static analysis suite -(CheckStyle, PMD, FindBugs, Static Analysis Utilities, Analysis Collector, Task Scanner, Warnings etc.). -In order to simplify the user experience and the development process, these -plugins and the core functionality have been merged into the Warnings Next Generation plugin. -These old static analysis plugins are not required anymore and are now end-of-life. -If you currently use one of these old plugins you should migrate -to the new recorders and steps as soon as possible. I will still maintain the old code for a while, -but the main development effort will be spent into the new code base. - -### Migration of Pipelines - -Pipelines calling the old static analysis steps (e.g., `findbugs`, `checkstyle`, etc.) need to call the new `recordIssues` -step now. The same step is used for all static analysis tools, the actual parser is selected -by using the step property `tools`. For more details on the set of available parameters please see section -[Configuration](#configuration). - -### Migration of all other jobs - -Freestyle, Matrix or Maven Jobs using the old API used a so called **Post Build Action** that was provided by -each individual plugin. E.g., the FindBugs plugin did provide the post build action -*"Publish FindBugs analysis results"*. These old plugin specific actions are not supported anymore, -they are now marked with *\[Deprecated\]* in the user interface. -Now you need to add a new post build step - this step now is called -*"Record compiler warnings and static analysis results"* for all static analysis tools. The selection of the tool is part of the configuration of this post build step. -Note: the warnings produced by a post build step using the old API cannot not be read by the new post build actions. -I.e., you can't see a combined history of the old and new results - you simply see two unrelated results. There is -also no automatic conversion of results stored in the old format available. - -### Migration of plugins depending on analysis-core - -The following plugins have been integrated into this new version of the Warnings plugin: - -- Android-Lint Plugin -- Analysis Collector Plugin -- CheckStyle Plugin -- CCM Plugin -- Dry Plugin -- PMD Plugin -- FindBugs Plugin -- Tasks Scanner Plugin -- Warnings Plugin - -All other plugins still need to be integrated or need to be refactored to use the new API. diff --git a/plugin/pom.xml b/plugin/pom.xml index d25d156d3e..6b829fd05d 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -41,7 +41,9 @@ 1.19.3 - 2.0.0 + 2.4.0-SNAPSHOT + 2.1.0-SNAPSHOT + 3.22 1.84 0.15.2 @@ -171,6 +173,7 @@ io.jenkins.plugins forensics-api + ${forensics-api.version} diff --git a/plugin/src/main/java/io/jenkins/plugins/analysis/core/model/DeltaReport.java b/plugin/src/main/java/io/jenkins/plugins/analysis/core/model/DeltaReport.java index 953293756d..1b0c2a1c79 100644 --- a/plugin/src/main/java/io/jenkins/plugins/analysis/core/model/DeltaReport.java +++ b/plugin/src/main/java/io/jenkins/plugins/analysis/core/model/DeltaReport.java @@ -28,42 +28,52 @@ public class DeltaReport { private final Report referenceIssues; private final String referenceBuildId; + /** + * Creates a new {@link DeltaReport}. Since no reference build is available, all issues are considered outstanding. + * + * @param report + * the current report + * @param currentBuildNumber + * the number of the current build, the reference of all new warnings will be set to this number + */ + public DeltaReport(final Report report, final int currentBuildNumber) { + allIssues = report; + outstandingIssues = report; + referenceIssues = EMPTY_REPORT; + newIssues = EMPTY_REPORT; + fixedIssues = EMPTY_REPORT; + referenceBuildId = StringUtils.EMPTY; + + report.logInfo("No valid reference build found"); + report.logInfo("All reported issues will be considered outstanding"); + report.forEach(issue -> issue.setReference(String.valueOf(currentBuildNumber))); + } + /** * Creates a new instance of {@link DeltaReport}. * * @param report * the current report - * @param history - * the history that will provide the reference build (if there is any) + * @param referenceBuild + * the reference build * @param currentBuildNumber * the number of the current build, the reference of all new warnings will be set to this number + * @param referenceIssues + * the issues in the reference build */ - public DeltaReport(final Report report, final History history, final int currentBuildNumber) { + public DeltaReport(final Report report, final Run referenceBuild, final int currentBuildNumber, final Report referenceIssues) { + report.logInfo("Using reference build '%s' to compute new, fixed, and outstanding issues", + referenceBuild); + allIssues = report; - if (history.getBuild().isPresent()) { - Run build = history.getBuild().get(); - report.logInfo("Using reference build '%s' to compute new, fixed, and outstanding issues", - build); - - referenceIssues = history.getIssues(); - IssueDifference difference = new IssueDifference(report, String.valueOf(currentBuildNumber), referenceIssues); - outstandingIssues = difference.getOutstandingIssues(); - newIssues = difference.getNewIssues(); - fixedIssues = difference.getFixedIssues(); - report.logInfo("Issues delta (vs. reference build): outstanding: %d, new: %d, fixed: %d", - outstandingIssues.size(), newIssues.size(), fixedIssues.size()); - referenceBuildId = build.getExternalizableId(); - } - else { - report.logInfo("No valid reference build found that meets the criteria (%s)", history); - report.logInfo("All reported issues will be considered outstanding"); - report.forEach(issue -> issue.setReference(String.valueOf(currentBuildNumber))); - outstandingIssues = report; - referenceIssues = EMPTY_REPORT; - newIssues = EMPTY_REPORT; - fixedIssues = EMPTY_REPORT; - referenceBuildId = StringUtils.EMPTY; - } + this.referenceIssues = referenceIssues; + IssueDifference difference = new IssueDifference(report, String.valueOf(currentBuildNumber), referenceIssues); + outstandingIssues = difference.getOutstandingIssues(); + newIssues = difference.getNewIssues(); + fixedIssues = difference.getFixedIssues(); + report.logInfo("Issues delta (vs. reference build): outstanding: %d, new: %d, fixed: %d", + outstandingIssues.size(), newIssues.size(), fixedIssues.size()); + referenceBuildId = referenceBuild.getExternalizableId(); } /** @@ -124,7 +134,7 @@ public Report getFixedIssues() { /** * Returns statistics for the number of issues (total, new, delta). - * + * * @return the issues statistics */ public IssuesStatistics getStatistics() { diff --git a/plugin/src/main/java/io/jenkins/plugins/analysis/core/steps/IssuesPublisher.java b/plugin/src/main/java/io/jenkins/plugins/analysis/core/steps/IssuesPublisher.java index 3c64b25361..4b1cf57379 100644 --- a/plugin/src/main/java/io/jenkins/plugins/analysis/core/steps/IssuesPublisher.java +++ b/plugin/src/main/java/io/jenkins/plugins/analysis/core/steps/IssuesPublisher.java @@ -7,7 +7,6 @@ import edu.hm.hafner.analysis.Report; import edu.hm.hafner.util.FilteredLog; -import hudson.model.Job; import hudson.model.Result; import hudson.model.Run; @@ -28,7 +27,6 @@ import io.jenkins.plugins.analysis.core.util.WarningsQualityGate; import io.jenkins.plugins.analysis.core.util.WarningsQualityGateEvaluator; import io.jenkins.plugins.forensics.reference.ReferenceFinder; -import io.jenkins.plugins.util.JenkinsFacade; import io.jenkins.plugins.util.LogHandler; import io.jenkins.plugins.util.QualityGateResult; import io.jenkins.plugins.util.ResultHandler; @@ -50,8 +48,6 @@ class IssuesPublisher { private final String name; private final Charset sourceCodeEncoding; private final List qualityGates; - private final String referenceJobName; - private final String referenceBuildId; private final QualityGateEvaluationMode qualityGateEvaluationMode; private final JobResultEvaluationMode jobResultEvaluationMode; private final LogHandler logger; @@ -61,9 +57,8 @@ class IssuesPublisher { @SuppressWarnings("ParameterNumber") IssuesPublisher(final Run run, final AnnotatedReport report, final HealthDescriptor healthDescriptor, final List qualityGates, - final String name, final String referenceJobName, final String referenceBuildId, - final boolean ignoreQualityGate, - final boolean ignoreFailedBuilds, final Charset sourceCodeEncoding, final LogHandler logger, + final String name, final boolean ignoreQualityGate, final boolean ignoreFailedBuilds, + final Charset sourceCodeEncoding, final LogHandler logger, final ResultHandler notifier, final boolean failOnErrors) { this.report = report; this.run = run; @@ -71,8 +66,6 @@ class IssuesPublisher { this.name = name; this.sourceCodeEncoding = sourceCodeEncoding; this.qualityGates = qualityGates; - this.referenceJobName = referenceJobName; - this.referenceBuildId = referenceBuildId; qualityGateEvaluationMode = ignoreQualityGate ? IGNORE_QUALITY_GATE : SUCCESSFUL_QUALITY_GATE; jobResultEvaluationMode = ignoreFailedBuilds ? NO_JOB_FAILURE : IGNORE_JOB_RESULT; this.logger = logger; @@ -97,7 +90,14 @@ ResultAction attachAction(final TrendChartType trendChartType) { ResultSelector selector = ensureThatIdIsUnique(); Report issues = report.getReport(); - DeltaReport deltaReport = new DeltaReport(issues, createAnalysisHistory(selector, issues), run.getNumber()); + var history = createAnalysisHistory(selector, issues); + DeltaReport deltaReport; + if (history.getBuild().isPresent()) { + deltaReport = new DeltaReport(issues, history.getBuild().get(), run.getNumber(), history.getIssues()); + } + else { + deltaReport = new DeltaReport(issues, run.getNumber()); + } QualityGateResult qualityGateResult = evaluateQualityGate(issues, deltaReport); reportHealth(issues); @@ -171,63 +171,19 @@ private QualityGateResult evaluateQualityGate(final Report issues, final DeltaRe } private History createAnalysisHistory(final ResultSelector selector, final Report issues) { - if (isValidReference(referenceJobName)) { - return findConfiguredReference(selector, issues); - } - - return new AnalysisHistory(findReference(issues), selector, - determineQualityGateEvaluationMode(issues), jobResultEvaluationMode); - } - - private boolean isValidReference(final String referenceName) { - return !IssuesRecorder.NO_REFERENCE_DEFINED.equals(referenceName); - } - - private History findConfiguredReference(final ResultSelector selector, final Report issues) { - final String message = "Setting the reference job has been deprecated, please use the new reference recorder"; - if (failOnErrors) { - // Log at info level otherwise this will fail the step, even if everything else is ok. - issues.logInfo(message); + var reference = findReference(issues); + if (reference.isPresent()) { + return new AnalysisHistory(reference.get(), selector, + determineQualityGateEvaluationMode(issues), jobResultEvaluationMode); } else { - issues.logError(message); - } - - Optional> referenceJob = new JenkinsFacade().getJob(referenceJobName); - if (referenceJob.isPresent()) { - Job job = referenceJob.get(); - - Run baseline; - if (isValidReference(referenceBuildId)) { - baseline = job.getBuild(referenceBuildId); - if (baseline == null) { - issues.logError("Reference job '%s' does not contain configured build '%s'", - job.getFullDisplayName(), referenceBuildId); - return new NullAnalysisHistory(); - } - } - else { - baseline = job.getLastCompletedBuild(); - if (baseline == null) { - issues.logInfo("Reference job '%s' has no completed build yet", job.getFullDisplayName()); - return new NullAnalysisHistory(); - } - } - return new AnalysisHistory(baseline, selector, determineQualityGateEvaluationMode(issues), - jobResultEvaluationMode); + return new NullAnalysisHistory(); } - issues.logError("Configured reference job '%s' does not exist", referenceJobName); - return new NullAnalysisHistory(); } - private Run findReference(final Report issues) { - ReferenceFinder referenceFinder = new ReferenceFinder(); + private Optional> findReference(final Report issues) { FilteredLog log = new FilteredLog("Errors while resolving the reference build:"); - Run reference = referenceFinder.findReference(run, log) - .orElseGet(() -> { - log.logInfo("Obtaining reference build from same job (%s)", run.getParent().getDisplayName()); - return run; - }); + Optional> reference = new ReferenceFinder().findReference(run, log); issues.mergeLogMessages(log); return reference; } diff --git a/plugin/src/main/java/io/jenkins/plugins/analysis/core/steps/IssuesRecorder.java b/plugin/src/main/java/io/jenkins/plugins/analysis/core/steps/IssuesRecorder.java index a90077115e..a4c454f258 100644 --- a/plugin/src/main/java/io/jenkins/plugins/analysis/core/steps/IssuesRecorder.java +++ b/plugin/src/main/java/io/jenkins/plugins/analysis/core/steps/IssuesRecorder.java @@ -98,8 +98,6 @@ public class IssuesRecorder extends Recorder { private boolean ignoreQualityGate = false; // by default, a successful quality gate is mandatory; private boolean ignoreFailedBuilds = true; // by default, failed builds are ignored; - private String referenceJobName; - private String referenceBuildId; private boolean failOnError = false; @@ -531,61 +529,6 @@ public boolean getIgnoreFailedBuilds() { return ignoreFailedBuilds; } - /** - * Sets the reference job to get the results for the issue difference computation. - * - * @param referenceJobName - * the name of reference job - */ - @DataBoundSetter - public void setReferenceJobName(final String referenceJobName) { - if (NO_REFERENCE_DEFINED.equals(referenceJobName)) { - this.referenceJobName = StringUtils.EMPTY; - } - this.referenceJobName = referenceJobName; - } - - /** - * Returns the reference job to get the results for the issue difference computation. If the job is not defined, - * then {@link #NO_REFERENCE_DEFINED} is returned. - * - * @return the name of reference job, or {@link #NO_REFERENCE_DEFINED} if undefined - */ - public String getReferenceJobName() { - if (StringUtils.isBlank(referenceJobName)) { - return NO_REFERENCE_DEFINED; - } - return referenceJobName; - } - - /** - * Sets the reference build id to get the results for the issue difference computatation. - * - * @param referenceBuildId - * the build id of the reference job - */ - public void setReferenceBuildId(final String referenceBuildId) { - if (NO_REFERENCE_DEFINED.equals(referenceBuildId)) { - this.referenceBuildId = StringUtils.EMPTY; - } - else { - this.referenceBuildId = referenceBuildId; - } - } - - /** - * Returns the reference build id to get the results for the issue difference computation. If the build id not - * defined, then {@link #NO_REFERENCE_DEFINED} is returned. - * - * @return the build id of the reference job, or {@link #NO_REFERENCE_DEFINED} if undefined. - */ - public String getReferenceBuildId() { - if (StringUtils.isBlank(referenceBuildId)) { - return NO_REFERENCE_DEFINED; - } - return referenceBuildId; - } - public int getHealthy() { return healthy; } @@ -804,7 +747,7 @@ AnalysisResult publishResult(final Run run, final TaskListener listener, f IssuesPublisher publisher = new IssuesPublisher(run, annotatedReport, new HealthDescriptor(healthy, unhealthy, minimumSeverity), qualityGates, - reportName, getReferenceJobName(), getReferenceBuildId(), ignoreQualityGate, ignoreFailedBuilds, + reportName, ignoreQualityGate, ignoreFailedBuilds, getSourceCodeCharset(), logHandler, resultHandler, failOnError); ResultAction action = publisher.attachAction(trendChartType); diff --git a/plugin/src/main/java/io/jenkins/plugins/analysis/core/steps/PublishIssuesStep.java b/plugin/src/main/java/io/jenkins/plugins/analysis/core/steps/PublishIssuesStep.java index 6da1f008b4..2f41e08ceb 100644 --- a/plugin/src/main/java/io/jenkins/plugins/analysis/core/steps/PublishIssuesStep.java +++ b/plugin/src/main/java/io/jenkins/plugins/analysis/core/steps/PublishIssuesStep.java @@ -57,7 +57,7 @@ public class PublishIssuesStep extends Step implements Serializable { private boolean ignoreQualityGate = false; // by default, a successful quality gate is mandatory private boolean ignoreFailedBuilds = true; // by default, failed builds are ignored private String referenceJobName = StringUtils.EMPTY; - private String referenceBuildId = StringUtils.EMPTY; + private final String referenceBuildId = StringUtils.EMPTY; private boolean failOnError = false; // by default, it should not fail on error private boolean skipPublishingChecks; // by default, warnings should be published to SCM platforms @@ -253,48 +253,6 @@ public void setReferenceJobName(final String referenceJobName) { this.referenceJobName = referenceJobName; } - /** - * Returns the reference job to get the results for the issue difference computation. If the job is not defined, - * then {@link IssuesRecorder#NO_REFERENCE_DEFINED} is returned. - * - * @return the name of reference job, or {@link IssuesRecorder#NO_REFERENCE_DEFINED} if undefined - */ - public String getReferenceJobName() { - if (StringUtils.isBlank(referenceJobName)) { - return IssuesRecorder.NO_REFERENCE_DEFINED; - } - return referenceJobName; - } - - /** - * Sets the reference build id of the reference job for the issue difference computation. - * - * @param referenceBuildId - * the build id of the reference job - */ - @DataBoundSetter - public void setReferenceBuildId(final String referenceBuildId) { - if (IssuesRecorder.NO_REFERENCE_DEFINED.equals(referenceBuildId)) { - this.referenceBuildId = StringUtils.EMPTY; - } - else { - this.referenceBuildId = referenceBuildId; - } - } - - /** - * Returns the reference build id of the reference job to get the results for the issue difference computation. - * If the build id is not defined, then {@link IssuesRecorder#NO_REFERENCE_DEFINED} is returned. - * - * @return the reference build id, or {@link IssuesRecorder#NO_REFERENCE_DEFINED} if undefined - */ - public String getReferenceBuildId() { - if (StringUtils.isBlank(referenceBuildId)) { - return IssuesRecorder.NO_REFERENCE_DEFINED; - } - return referenceBuildId; - } - @CheckForNull public String getSourceCodeEncoding() { return sourceCodeEncoding; @@ -453,7 +411,7 @@ protected ResultAction run() throws IOException, InterruptedException, IllegalSt IssuesPublisher publisher = new IssuesPublisher(getRun(), report, new HealthDescriptor(step.getHealthy(), step.getUnhealthy(), step.getMinimumSeverityAsSeverity()), step.getQualityGates(), - StringUtils.defaultString(step.getName()), step.getReferenceJobName(), step.getReferenceBuildId(), + StringUtils.defaultString(step.getName()), step.getIgnoreQualityGate(), step.getIgnoreFailedBuilds(), getCharset(step.getSourceCodeEncoding()), getLogger(report), notifier, step.getFailOnError()); ResultAction action = publisher.attachAction(step.getTrendChartType()); diff --git a/plugin/src/main/java/io/jenkins/plugins/analysis/core/steps/RecordIssuesStep.java b/plugin/src/main/java/io/jenkins/plugins/analysis/core/steps/RecordIssuesStep.java index 82b73a10a5..cdc98e808a 100644 --- a/plugin/src/main/java/io/jenkins/plugins/analysis/core/steps/RecordIssuesStep.java +++ b/plugin/src/main/java/io/jenkins/plugins/analysis/core/steps/RecordIssuesStep.java @@ -75,8 +75,7 @@ public class RecordIssuesStep extends Step implements Serializable { private boolean ignoreQualityGate = false; // by default, a successful quality gate is mandatory; private boolean ignoreFailedBuilds = true; // by default, failed builds are ignored; - private String referenceJobName = StringUtils.EMPTY; - private String referenceBuildId = StringUtils.EMPTY; + private final String referenceBuildId = StringUtils.EMPTY; private int healthy; private int unhealthy; @@ -507,62 +506,6 @@ public boolean getFailOnError() { return failOnError; } - /** - * Sets the reference job to get the results for the issue difference computation. - * - * @param referenceJobName - * the name of reference job - */ - @DataBoundSetter - public void setReferenceJobName(final String referenceJobName) { - if (IssuesRecorder.NO_REFERENCE_DEFINED.equals(referenceJobName)) { - this.referenceJobName = StringUtils.EMPTY; - } - this.referenceJobName = referenceJobName; - } - - /** - * Returns the reference job to get the results for the issue difference computation. If the job is not defined, - * then {@link IssuesRecorder#NO_REFERENCE_DEFINED} is returned. - * - * @return the name of reference job, or {@link IssuesRecorder#NO_REFERENCE_DEFINED} if undefined - */ - public String getReferenceJobName() { - if (StringUtils.isBlank(referenceJobName)) { - return IssuesRecorder.NO_REFERENCE_DEFINED; - } - return referenceJobName; - } - - /** - * Sets the reference build id of the reference job for the issue difference computation. - * - * @param referenceBuildId - * the build id of the reference job - */ - @DataBoundSetter - public void setReferenceBuildId(final String referenceBuildId) { - if (IssuesRecorder.NO_REFERENCE_DEFINED.equals(referenceBuildId)) { - this.referenceBuildId = StringUtils.EMPTY; - } - else { - this.referenceBuildId = referenceBuildId; - } - } - - /** - * Returns the reference build id of the reference job to get the results for the issue difference computation. If - * the build id is not defined, then {@link IssuesRecorder#NO_REFERENCE_DEFINED} is returned. - * - * @return the reference build id, or {@link IssuesRecorder#NO_REFERENCE_DEFINED} if undefined - */ - public String getReferenceBuildId() { - if (StringUtils.isBlank(referenceBuildId)) { - return IssuesRecorder.NO_REFERENCE_DEFINED; - } - return referenceBuildId; - } - public int getHealthy() { return healthy; } @@ -660,8 +603,6 @@ protected List run() throws IOException, InterruptedException { recorder.setSourceCodeEncoding(step.getSourceCodeEncoding()); recorder.setIgnoreQualityGate(step.getIgnoreQualityGate()); recorder.setIgnoreFailedBuilds(step.getIgnoreFailedBuilds()); - recorder.setReferenceJobName(step.getReferenceJobName()); - recorder.setReferenceBuildId(step.getReferenceBuildId()); recorder.setHealthy(step.getHealthy()); recorder.setUnhealthy(step.getUnhealthy()); recorder.setMinimumSeverity(step.getMinimumSeverity()); diff --git a/plugin/src/test/java/io/jenkins/plugins/analysis/core/model/DeltaReportTest.java b/plugin/src/test/java/io/jenkins/plugins/analysis/core/model/DeltaReportTest.java index 82b40b6447..6cb264ec82 100644 --- a/plugin/src/test/java/io/jenkins/plugins/analysis/core/model/DeltaReportTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/analysis/core/model/DeltaReportTest.java @@ -33,7 +33,7 @@ void shouldHaveEmptyReports() { when(history.getBuild()).thenReturn(Optional.empty()); Report report = new Report(); - DeltaReport deltaReport = new DeltaReport(report, history, 0); + DeltaReport deltaReport = new DeltaReport(report, 0); assertThat(deltaReport) .isEmpty() .hasNoAllIssues() @@ -48,9 +48,6 @@ void shouldHaveCorrectReports() { Run run = mock(Run.class); when(run.getExternalizableId()).thenReturn(REFERENCE_BUILD_ID); - History history = mock(History.class); - when(history.getBuild()).thenReturn(Optional.of(run)); - Issue issue = getIssue("issue"); Issue fixedIssue = getIssue("fixedIssue"); Issue newIssue = getIssue("newIssue"); @@ -58,13 +55,12 @@ void shouldHaveCorrectReports() { Report referenceIssues = new Report(); referenceIssues.add(issue); referenceIssues.add(fixedIssue); - when(history.getIssues()).thenReturn(referenceIssues); Report report = new Report(); report.add(issue); report.add(newIssue); - DeltaReport deltaReport = new DeltaReport(report, history, 0); + DeltaReport deltaReport = new DeltaReport(report, run, 0, referenceIssues); assertThat(deltaReport) .hasAllIssues(issue, newIssue) .hasOutstandingIssues(issue) @@ -105,7 +101,7 @@ void shouldCreateIssuesStatistics() { .setFixedSize(1) .build(); - DeltaReport deltaReport = new DeltaReport(report, history, 0); + DeltaReport deltaReport = new DeltaReport(report, run, 0, referenceIssues); IssuesStatistics issuesStatistics = deltaReport.getStatistics(); IssuesStatisticsAssert.assertThat(issuesStatistics) .isNotNull().usingRecursiveComparison() @@ -113,10 +109,14 @@ void shouldCreateIssuesStatistics() { } private Issue getIssue(final String name) { - return new IssueBuilder().setFileName(name).setFingerprint(name).build(); + try (var builder = new IssueBuilder()) { + return builder.setFileName(name).setFingerprint(name).build(); + } } private Issue getIssueWithSeverity(final String name, final Severity severity) { - return new IssueBuilder().setFileName(name).setFingerprint(name).setSeverity(severity).build(); + try (var builder = new IssueBuilder()) { + return builder.setFileName(name).setFingerprint(name).setSeverity(severity).build(); + } } } diff --git a/plugin/src/test/java/io/jenkins/plugins/analysis/core/steps/WarningChecksPublisherITest.java b/plugin/src/test/java/io/jenkins/plugins/analysis/core/steps/WarningChecksPublisherITest.java index 8b3f0a946a..d7e802f313 100644 --- a/plugin/src/test/java/io/jenkins/plugins/analysis/core/steps/WarningChecksPublisherITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/analysis/core/steps/WarningChecksPublisherITest.java @@ -37,6 +37,7 @@ import io.jenkins.plugins.checks.api.ChecksStatus; import io.jenkins.plugins.checks.util.CapturingChecksPublisher; import io.jenkins.plugins.checks.util.CapturingChecksPublisher.Factory; +import io.jenkins.plugins.forensics.reference.SimpleReferenceRecorder; import io.jenkins.plugins.util.QualityGate.QualityGateCriticality; import static io.jenkins.plugins.analysis.core.assertions.Assertions.*; @@ -98,6 +99,7 @@ void shouldCreateChecksDetailsWithNewIssuesAsAnnotations() { private void configureScanner(final WorkflowJob job, final String fileName, final String parameters) { job.setDefinition(new CpsFlowDefinition("node {\n" + " stage ('Integration Test') {\n" + + " discoverReferenceBuild()\n" + " recordIssues tool: checkStyle(pattern: '**/" + fileName + "-*') " + parameters + "\n" @@ -143,7 +145,7 @@ void shouldConcludeChecksAsFailureWhenQualityGateResultIsUnstable() { */ @Test void shouldParseHtmlMessage() { - FreeStyleProject project = createFreeStyleProject(); + FreeStyleProject project = getFreeStyleJob(); enableWarnings(project, new PVSStudio()); buildSuccessfully(project); @@ -168,7 +170,7 @@ void shouldParseHtmlMessage() { */ @Test void shouldReportNoIssuesInTitle() { - FreeStyleProject project = createFreeStyleProject(); + FreeStyleProject project = getFreeStyleJob(); enableCheckStyleWarnings(project); Run run = buildSuccessfully(project); @@ -209,7 +211,7 @@ void shouldReportOnlyTotalIssuesInTitleWhenNoNewIssues() { */ @Test void shouldReportOnlyNewIssuesInTitleWhenAllIssuesAreNew() { - FreeStyleProject project = createFreeStyleProject(); + FreeStyleProject project = getFreeStyleJob(); enableCheckStyleWarnings(project); Run reference = buildSuccessfully(project); @@ -235,7 +237,7 @@ void shouldReportOnlyNewIssuesInTitleWhenAllIssuesAreNew() { */ @Test void shouldIgnoreColumnsWhenBuildMultipleLineAnnotation() { - FreeStyleProject project = createFreeStyleProject(); + FreeStyleProject project = getFreeStyleJob(); enableWarnings(project, new Pmd()); buildSuccessfully(project); @@ -371,7 +373,7 @@ void shouldHonorWithChecksContextRecordIssues() { */ @Test void shouldFallbackToIssueCategory() { - FreeStyleProject project = createFreeStyleProject(); + FreeStyleProject project = getFreeStyleJob(); enableWarnings(project, createTool(new MsBuild(), "msbuild.log")); buildSuccessfully(project); @@ -386,6 +388,12 @@ void shouldFallbackToIssueCategory() { .hasFieldOrPropertyWithValue("title", Optional.of("C4101")); } + private FreeStyleProject getFreeStyleJob() { + var project = createFreeStyleProject(); + project.getPublishersList().add(new SimpleReferenceRecorder()); + return project; + } + private ChecksDetails createExpectedCheckStyleDetails() { ChecksDetailsBuilder builder = new ChecksDetailsBuilder() .withName("CheckStyle") diff --git a/plugin/src/test/java/io/jenkins/plugins/analysis/core/testutil/WarningsIntegrationTest.java b/plugin/src/test/java/io/jenkins/plugins/analysis/core/testutil/WarningsIntegrationTest.java index fb31977cfb..396d4ca681 100644 --- a/plugin/src/test/java/io/jenkins/plugins/analysis/core/testutil/WarningsIntegrationTest.java +++ b/plugin/src/test/java/io/jenkins/plugins/analysis/core/testutil/WarningsIntegrationTest.java @@ -57,6 +57,8 @@ public abstract class WarningsIntegrationTest extends IntegrationTest { /** Step to publish a set of issues. Uses default options for all options. */ protected static final String PUBLISH_ISSUES_STEP = "publishIssues issues:[issues]"; + /** Step to record the reference builds. */ + protected static final String REFERENCE_BUILD = "discoverReferenceBuild()"; /** * Copies the specified files to the workspace using a generated file name that uses the same suffix. So the pattern @@ -131,7 +133,7 @@ protected MavenModuleSet createMavenJob() { * @return the pipeline script */ protected CpsFlowDefinition createPipelineScriptWithScanAndPublishSteps(final AnalysisModelParser tool) { - return asStage(createScanForIssuesStep(tool), PUBLISH_ISSUES_STEP); + return asStage(REFERENCE_BUILD, createScanForIssuesStep(tool), PUBLISH_ISSUES_STEP); } /** diff --git a/plugin/src/test/java/io/jenkins/plugins/analysis/warnings/integrations/JobDslITest.java b/plugin/src/test/java/io/jenkins/plugins/analysis/warnings/integrations/JobDslITest.java index 0d4fecd15c..b06aa1c561 100644 --- a/plugin/src/test/java/io/jenkins/plugins/analysis/warnings/integrations/JobDslITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/analysis/warnings/integrations/JobDslITest.java @@ -108,7 +108,6 @@ void shouldCreateFreestyleJobUsingJobDslAndVerifyIssueRecorderWithValuesSet() { assertThat(recorder.getName()).isEqualTo("test-name"); assertThat(recorder.getSourceCodeEncoding()).isEqualTo("UTF-8"); assertThat(recorder.getUnhealthy()).isEqualTo(50); - assertThat(recorder.getReferenceJobName()).isEqualTo("test-job"); assertThat(recorder.getQualityGates()).hasSize(1) .first().satisfies(gate -> { assertThat(gate.getThreshold()).isEqualTo(10.0); diff --git a/plugin/src/test/java/io/jenkins/plugins/analysis/warnings/integrations/TokenMacroITest.java b/plugin/src/test/java/io/jenkins/plugins/analysis/warnings/integrations/TokenMacroITest.java index 20fe7c9985..c89defe613 100644 --- a/plugin/src/test/java/io/jenkins/plugins/analysis/warnings/integrations/TokenMacroITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/analysis/warnings/integrations/TokenMacroITest.java @@ -93,6 +93,7 @@ private void verifyConsoleLog(final AnalysisResult baseline, final int totalSize private void configureToken(final WorkflowJob job, final String fileName) { job.setDefinition(new CpsFlowDefinition("node {\n" + " stage ('Integration Test') {\n" + + " discoverReferenceBuild()\n" + " recordIssues tool: checkStyle(pattern: '**/" + fileName + "*')\n" + " def total = tm('${ANALYSIS_ISSUES_COUNT}')\n" + " def checkstyle = tm('${ANALYSIS_ISSUES_COUNT, tool=\"checkstyle\"}')\n" diff --git a/plugin/src/test/java/io/jenkins/plugins/analysis/warnings/steps/AffectedFilesResolverITest.java b/plugin/src/test/java/io/jenkins/plugins/analysis/warnings/steps/AffectedFilesResolverITest.java index cca4b2b59d..21e5419177 100644 --- a/plugin/src/test/java/io/jenkins/plugins/analysis/warnings/steps/AffectedFilesResolverITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/analysis/warnings/steps/AffectedFilesResolverITest.java @@ -53,6 +53,7 @@ class AffectedFilesResolverITest extends IntegrationTestWithJenkinsPerSuite { private static final String ECLIPSE_REPORT_ONE_AFFECTED_AFFECTED_FILE = FOLDER + "/eclipseOneAffectedFile.txt"; private static final int ROW_NUMBER_ACTUAL_AFFECTED_FILE = 0; private static final String COPY_FILES = "Copying affected files to Jenkins' build folder"; + private static final String ZIP = ".zip"; /** * Verifies that the affected source code is copied and shown in the source code view. If the file is deleted in the @@ -107,7 +108,8 @@ void shouldShowNoLinkIfSourceCodeHasBeenMadeUnreadable() { } private void makeAffectedFilesInBuildFolderUnreadable(final AnalysisResult result) { - makeFileUnreadable(AffectedFilesResolver.getFile(result.getOwner(), getIssueWithSource(result).getFileName())); + makeFileUnreadable(AffectedFilesResolver.getFile(result.getOwner(), + getIssueWithSource(result).getFileName() + ZIP)); } private Issue getIssueWithSource(final AnalysisResult result) { @@ -120,7 +122,7 @@ private Issue getIssueWithSource(final AnalysisResult result) { private void deleteAffectedFilesInBuildFolder(final AnalysisResult result) { Set files = result.getIssues().getFiles(); for (String fileName : files) { - Path file = AffectedFilesResolver.getFile(result.getOwner(), fileName); + Path file = AffectedFilesResolver.getFile(result.getOwner(), fileName + ZIP); try { Files.delete(file); } diff --git a/plugin/src/test/java/io/jenkins/plugins/analysis/warnings/steps/GitForensicsITest.java b/plugin/src/test/java/io/jenkins/plugins/analysis/warnings/steps/GitForensicsITest.java index 56a65e96d3..b9552f452d 100644 --- a/plugin/src/test/java/io/jenkins/plugins/analysis/warnings/steps/GitForensicsITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/analysis/warnings/steps/GitForensicsITest.java @@ -45,7 +45,6 @@ class GitForensicsITest extends IntegrationTestWithJenkinsPerSuite { */ @Test void shouldObtainBlamesAndForensicsWithScanAndPublishIssuesSteps() { - runStepAndVerifyBlamesAndForensics(createScanForIssuesStep("sourceDirectory: 'forensics-api'")); runStepAndVerifyBlamesAndForensics(createScanForIssuesStep("sourceDirectories: [[path: 'forensics-api']]")); runStepAndVerifyBlamesAndForensics(createScanForIssuesStep("sourceDirectories: [[path: 'does-not-exist'], [path: 'forensics-api']]")); } @@ -63,7 +62,6 @@ private String createScanForIssuesStep(final String sourceDirectories) { */ @Test void shouldObtainBlamesAndForensicsWithRecordIssuesStep() { - runStepAndVerifyBlamesAndForensics(createRecordIssuesStep("sourceDirectory: 'forensics-api'")); runStepAndVerifyBlamesAndForensics(createRecordIssuesStep("sourceDirectories: [[path: 'forensics-api']]")); runStepAndVerifyBlamesAndForensics(createRecordIssuesStep("sourceDirectories: [[path: 'does-not-exist'], [path: 'forensics-api']]")); } @@ -99,7 +97,7 @@ private void runStepAndVerifyBlamesAndForensics(final String step) { createFileInWorkspace(job, "java-issues.txt", createJavaWarning(SCM_RESOLVER, AFFECTED_LINE)); - job.setDefinition(asStage(CHECKOUT_FORENSICS_API, MINE_REPOSITORY, step)); + job.setDefinition(asStage(CHECKOUT_FORENSICS_API, MINE_REPOSITORY, "discoverReferenceBuild()", step)); verifyBlaming(job); } @@ -130,7 +128,7 @@ private void verifyBlaming(final ParameterizedJob job) { @Test void shouldSkipBlamesAndForensicsWithScanAndPublishIssuesSteps() { runStepAndVerifyScmSkipping("def issues = scanForIssues " - + "sourceDirectory: 'forensics-api'," + + "sourceDirectories: [[path: 'forensics-api']]," + "scm: 'nothing', " + "tool: java(pattern:'**/*issues.txt', reportEncoding:'UTF-8')\n" + PUBLISH_ISSUES_STEP); @@ -143,7 +141,7 @@ void shouldSkipBlamesAndForensicsWithScanAndPublishIssuesSteps() { @Test void shouldSkipBlamesAndForensicsWithRecordIssuesStep() { runStepAndVerifyScmSkipping("recordIssues " - + "sourceDirectory: 'forensics-api'," + + "sourceDirectories: [[path: 'forensics-api']]," + "scm: 'nothing', " + "tool: java(pattern:'**/*issues.txt', reportEncoding:'UTF-8')"); } diff --git a/plugin/src/test/java/io/jenkins/plugins/analysis/warnings/steps/MiscIssuesRecorderITest.java b/plugin/src/test/java/io/jenkins/plugins/analysis/warnings/steps/MiscIssuesRecorderITest.java index e00c109135..8fb0aafa26 100644 --- a/plugin/src/test/java/io/jenkins/plugins/analysis/warnings/steps/MiscIssuesRecorderITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/analysis/warnings/steps/MiscIssuesRecorderITest.java @@ -37,6 +37,7 @@ import io.jenkins.plugins.analysis.warnings.Pmd; import io.jenkins.plugins.analysis.warnings.RegisteredParser; import io.jenkins.plugins.analysis.warnings.tasks.OpenTasks; +import io.jenkins.plugins.forensics.reference.SimpleReferenceRecorder; import io.jenkins.plugins.util.QualityGateStatus; import static io.jenkins.plugins.analysis.core.assertions.Assertions.*; @@ -59,7 +60,7 @@ class MiscIssuesRecorderITest extends IntegrationTestWithJenkinsPerSuite { */ @Test @org.junitpioneer.jupiter.Issue("JENKINS-55514") void shouldMapSeverityFilterForFindBugs() { - FreeStyleProject project = createFreeStyleProjectWithWorkspaceFilesWithSuffix("findbugs-severities.xml"); + FreeStyleProject project = createFreestyleJob("findbugs-severities.xml"); FindBugs findbugs = new FindBugs(); findbugs.setUseRankAsPriority(true); @@ -96,7 +97,7 @@ void shouldCreateEmptyResult() { */ @Test void shouldCreateResultWithWarnings() { - FreeStyleProject project = createFreeStyleProjectWithWorkspaceFilesWithSuffix("eclipse.txt"); + FreeStyleProject project = createFreestyleJob("eclipse.txt"); enableEclipseWarnings(project); AnalysisResult result = scheduleBuildAndAssertStatus(project, Result.SUCCESS); @@ -114,7 +115,7 @@ void shouldCreateResultWithWarnings() { */ @Test void shouldScanForOpenTasks() { - FreeStyleProject project = createFreeStyleProjectWithWorkspaceFilesWithSuffix("eclipse.txt"); + FreeStyleProject project = createFreestyleJob("eclipse.txt"); OpenTasks tasks = new OpenTasks(); tasks.setIncludePattern("**/*.txt"); String tag = "WARNING"; @@ -138,7 +139,7 @@ void shouldScanForOpenTasks() { */ @Test void shouldCreateResultWithDifferentNameAndId() { - FreeStyleProject project = createFreeStyleProjectWithWorkspaceFilesWithSuffix("eclipse.txt"); + FreeStyleProject project = createFreestyleJob("eclipse.txt"); ReportScanningTool configuration = configurePattern(new Eclipse()); String id = "new-id"; configuration.setId(id); @@ -227,8 +228,7 @@ private List runJobWithAggregation(final boolean isAggregationEn */ @Test void shouldHaveOriginsIfBuildContainsWarnings() { - FreeStyleProject project = createFreeStyleProjectWithWorkspaceFilesWithSuffix("checkstyle.xml", - "pmd-warnings.xml"); + FreeStyleProject project = createFreestyleJob("checkstyle.xml", "pmd-warnings.xml"); enableWarnings(project, recorder -> { recorder.setAggregatingResults(true); @@ -290,8 +290,7 @@ void shouldThrowExceptionIfSameToolIsConfiguredTwice() { */ @Test void shouldUseSameToolTwice() { - FreeStyleProject project = createFreeStyleProjectWithWorkspaceFilesWithSuffix("checkstyle.xml", - "checkstyle-twice.xml"); + FreeStyleProject project = createFreestyleJob("checkstyle.xml", "checkstyle-twice.xml"); ReportScanningTool first = createTool(new CheckStyle(), "**/checkstyle-issues.txt"); ReportScanningTool second = createTool(new CheckStyle(), "**/checkstyle-twice-issues.txt"); second.setId("second"); @@ -322,8 +321,7 @@ void shouldAggregateMultipleConfigurationsOfSameTool() { } private Run runJobWithCheckStyleTwice(final boolean isAggregationEnabled, final Result result) { - FreeStyleProject project = createFreeStyleProjectWithWorkspaceFilesWithSuffix("checkstyle.xml", - "checkstyle-twice.xml"); + FreeStyleProject project = createFreestyleJob("checkstyle.xml", "checkstyle-twice.xml"); enableWarnings(project, recorder -> recorder.setAggregatingResults(isAggregationEnabled), createTool(new CheckStyle(), "**/checkstyle-issues.txt"), createTool(new CheckStyle(), "**/checkstyle-twice-issues.txt")); @@ -338,8 +336,8 @@ void shouldAggregateMultipleConfigurationsOfSameTool() { */ @Test void shouldCreateFixedWarnings() { - FreeStyleProject project = createFreeStyleProjectWithWorkspaceFilesWithSuffix("eclipse_8_Warnings.txt", - "eclipse_5_Warnings.txt"); + FreeStyleProject project = createFreestyleJob("eclipse_8_Warnings.txt", "eclipse_5_Warnings.txt"); + IssuesRecorder recorder = enableGenericWarnings(project, createEclipse("eclipse_8_Warnings-issues.txt")); // First build: baseline @@ -373,6 +371,12 @@ void shouldCreateFixedWarnings() { verifyNoNewWarningsPortletModel(portlet, 5, 3); } + private FreeStyleProject createFreestyleJob(final String... strings) { + var project = createFreeStyleProjectWithWorkspaceFilesWithSuffix(strings); + project.getPublishersList().add(new SimpleReferenceRecorder()); + return project; + } + private void verifyNoNewWarningsPortletModel(final PullRequestMonitoringPortlet portlet, final int expectedOutstandingWarnings, final int expectedFixedWarnings) { assertThat(portlet.hasNoNewWarnings()).isTrue(); @@ -405,8 +409,7 @@ private PullRequestMonitoringPortlet createPortlet(final FreeStyleProject projec */ @Test void shouldCreateNewWarnings() { - FreeStyleProject project = createFreeStyleProjectWithWorkspaceFilesWithSuffix("eclipse_5_Warnings.txt", - "eclipse_8_Warnings.txt"); + FreeStyleProject project = createFreestyleJob("eclipse_5_Warnings.txt", "eclipse_8_Warnings.txt"); IssuesRecorder recorder = enableWarnings(project, createEclipse("eclipse_5_Warnings-issues.txt")); // First build: baseline @@ -442,7 +445,7 @@ void shouldCreateNewWarnings() { */ @Test void shouldCreateNoFixedWarningsOrNewWarnings() { - FreeStyleProject project = createFreeStyleProjectWithWorkspaceFilesWithSuffix("eclipse_8_Warnings.txt"); + FreeStyleProject project = createFreestyleJob("eclipse_8_Warnings.txt"); ReportScanningTool eclipse = createEclipse("eclipse_8_Warnings-issues.txt"); IssuesRecorder recorder = enableWarnings(project, eclipse); @@ -481,8 +484,7 @@ void shouldCreateNoFixedWarningsOrNewWarnings() { */ @Test void shouldCreateSomeNewWarningsAndSomeFixedWarnings() { - FreeStyleProject project = createFreeStyleProjectWithWorkspaceFilesWithSuffix("eclipse_5_Warnings.txt", - "eclipse_4_Warnings.txt"); + FreeStyleProject project = createFreestyleJob("eclipse_5_Warnings.txt", "eclipse_4_Warnings.txt"); IssuesRecorder recorder = enableWarnings(project, createEclipse("eclipse_5_Warnings-issues.txt")); // First build: baseline @@ -529,8 +531,7 @@ void shouldFindNewCheckStyleWarnings() { } private void shouldFindNewCheckStyleWarnings(final Supplier tool) { - FreeStyleProject project = createFreeStyleProjectWithWorkspaceFilesWithSuffix("checkstyle1.xml", - "checkstyle2.xml"); + FreeStyleProject project = createFreestyleJob("checkstyle1.xml", "checkstyle2.xml"); buildWithResult(project, Result.SUCCESS); // dummy build to ensure that the first CheckStyle build starts at #2 diff --git a/plugin/src/test/java/io/jenkins/plugins/analysis/warnings/steps/QualityGateITest.java b/plugin/src/test/java/io/jenkins/plugins/analysis/warnings/steps/QualityGateITest.java index 4579ea98a2..e3d2053a6b 100644 --- a/plugin/src/test/java/io/jenkins/plugins/analysis/warnings/steps/QualityGateITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/analysis/warnings/steps/QualityGateITest.java @@ -25,6 +25,7 @@ import io.jenkins.plugins.analysis.core.util.WarningsQualityGate; import io.jenkins.plugins.analysis.core.util.WarningsQualityGate.QualityGateType; import io.jenkins.plugins.analysis.warnings.CheckStyle; +import io.jenkins.plugins.forensics.reference.SimpleReferenceRecorder; import io.jenkins.plugins.util.QualityGate.QualityGateCriticality; import io.jenkins.plugins.util.QualityGateEvaluator; import io.jenkins.plugins.util.QualityGateResult.QualityGateResultItem; @@ -72,7 +73,7 @@ void shouldUseTwoQualityGates() { @Test @Issue("JENKINS-58635") void shouldBePassedForFirstBuildWithDelta() { - FreeStyleProject project = createFreeStyleProject(); + FreeStyleProject project = createJobWithReferenceFinder(); enableAndConfigureCheckstyle(project, recorder -> recorder.setQualityGates(List.of( new WarningsQualityGate(11, QualityGateType.DELTA, QualityGateCriticality.UNSTABLE)))); @@ -87,7 +88,7 @@ void shouldBePassedForFirstBuildWithDelta() { */ @Test void shouldBePassedForFirstBuildWithNew() { - FreeStyleProject project = createFreeStyleProject(); + FreeStyleProject project = createJobWithReferenceFinder(); enableAndConfigureCheckstyle(project, recorder -> recorder.setQualityGates(List.of( new WarningsQualityGate(11, QualityGateType.NEW, QualityGateCriticality.UNSTABLE)))); @@ -118,7 +119,7 @@ void shouldCreateUnstableResult() { */ @Test void shouldBeUnstableWhenUnstableDeltaAllIsReachedNew() { - FreeStyleProject project = createFreeStyleProject(); + FreeStyleProject project = createJobWithReferenceFinder(); enableAndConfigureCheckstyle(project, recorder -> recorder.setQualityGates(List.of( new WarningsQualityGate(11, QualityGateType.DELTA, QualityGateCriticality.UNSTABLE)))); @@ -130,7 +131,7 @@ void shouldBeUnstableWhenUnstableDeltaAllIsReachedNew() { */ @Test void shouldBeUnstableWhenUnstableNewAllIsReachedNew() { - FreeStyleProject project = createFreeStyleProject(); + FreeStyleProject project = createJobWithReferenceFinder(); enableAndConfigureCheckstyle(project, recorder -> recorder.setQualityGates(List.of( new WarningsQualityGate(11, QualityGateType.NEW, QualityGateCriticality.UNSTABLE)))); @@ -142,7 +143,7 @@ void shouldBeUnstableWhenUnstableNewAllIsReachedNew() { */ @Test void shouldBeUnstableWhenUnstableDeltaErrorIsReachedNew() { - FreeStyleProject project = createFreeStyleProject(); + FreeStyleProject project = createJobWithReferenceFinder(); enableAndConfigureCheckstyle(project, recorder -> recorder.setQualityGates(List.of( new WarningsQualityGate(6, QualityGateType.DELTA_ERROR, QualityGateCriticality.UNSTABLE)))); @@ -155,7 +156,7 @@ void shouldBeUnstableWhenUnstableDeltaErrorIsReachedNew() { */ @Test void shouldBeUnstableWhenUnstableNewErrorIsReachedNew() { - FreeStyleProject project = createFreeStyleProject(); + FreeStyleProject project = createJobWithReferenceFinder(); enableAndConfigureCheckstyle(project, recorder -> recorder.setQualityGates(List.of( new WarningsQualityGate(6, QualityGateType.NEW_ERROR, QualityGateCriticality.UNSTABLE)))); @@ -167,7 +168,7 @@ void shouldBeUnstableWhenUnstableNewErrorIsReachedNew() { */ @Test void shouldBeUnstableWhenUnstableDeltaNormalIsReachedNew() { - FreeStyleProject project = createFreeStyleProject(); + FreeStyleProject project = createJobWithReferenceFinder(); enableAndConfigureCheckstyle(project, recorder -> recorder.setQualityGates(List.of( new WarningsQualityGate(2, QualityGateType.DELTA_NORMAL, QualityGateCriticality.UNSTABLE)))); @@ -180,7 +181,7 @@ void shouldBeUnstableWhenUnstableDeltaNormalIsReachedNew() { */ @Test void shouldBeUnstableWhenUnstableNewNormalIsReachedNew() { - FreeStyleProject project = createFreeStyleProject(); + FreeStyleProject project = createJobWithReferenceFinder(); enableAndConfigureCheckstyle(project, recorder -> recorder.setQualityGates(List.of( new WarningsQualityGate(2, QualityGateType.NEW_NORMAL, QualityGateCriticality.UNSTABLE)))); @@ -193,7 +194,7 @@ void shouldBeUnstableWhenUnstableNewNormalIsReachedNew() { */ @Test void shouldBeUnstableWhenUnstableDeltaLowIsReachedNew() { - FreeStyleProject project = createFreeStyleProject(); + FreeStyleProject project = createJobWithReferenceFinder(); enableAndConfigureCheckstyle(project, recorder -> recorder.setQualityGates(List.of( new WarningsQualityGate(3, QualityGateType.DELTA_LOW, QualityGateCriticality.UNSTABLE)))); @@ -206,7 +207,7 @@ void shouldBeUnstableWhenUnstableDeltaLowIsReachedNew() { */ @Test void shouldBeUnstableWhenUnstableNewLowIsReachedNew() { - FreeStyleProject project = createFreeStyleProject(); + FreeStyleProject project = createJobWithReferenceFinder(); enableAndConfigureCheckstyle(project, recorder -> recorder.setQualityGates(List.of( new WarningsQualityGate(3, QualityGateType.NEW_LOW, QualityGateCriticality.UNSTABLE)))); @@ -218,7 +219,7 @@ void shouldBeUnstableWhenUnstableNewLowIsReachedNew() { */ @Test void shouldBeUnstableWhenUnstableTotalAllIsReachedNew() { - FreeStyleProject project = createFreeStyleProject(); + FreeStyleProject project = createJobWithReferenceFinder(); enableAndConfigureCheckstyle(project, recorder -> recorder.setQualityGates(List.of( new WarningsQualityGate(11, QualityGateType.TOTAL, QualityGateCriticality.UNSTABLE)))); @@ -231,7 +232,7 @@ void shouldBeUnstableWhenUnstableTotalAllIsReachedNew() { */ @Test void shouldBeUnstableWhenUnstableTotalErrorIsReachedNew() { - FreeStyleProject project = createFreeStyleProject(); + FreeStyleProject project = createJobWithReferenceFinder(); enableAndConfigureCheckstyle(project, recorder -> recorder.setQualityGates(List.of( new WarningsQualityGate(6, QualityGateType.TOTAL_ERROR, QualityGateCriticality.UNSTABLE)))); @@ -244,7 +245,7 @@ void shouldBeUnstableWhenUnstableTotalErrorIsReachedNew() { */ @Test void shouldBeUnstableWhenUnstableTotalNormalIsReachedNew() { - FreeStyleProject project = createFreeStyleProject(); + FreeStyleProject project = createJobWithReferenceFinder(); enableAndConfigureCheckstyle(project, recorder -> recorder.setQualityGates(List.of( new WarningsQualityGate(2, QualityGateType.TOTAL_NORMAL, QualityGateCriticality.UNSTABLE)))); @@ -257,19 +258,25 @@ void shouldBeUnstableWhenUnstableTotalNormalIsReachedNew() { */ @Test void shouldBeUnstableWhenUnstableTotalLowIsReachedNew() { - FreeStyleProject project = createFreeStyleProject(); + FreeStyleProject project = createJobWithReferenceFinder(); enableAndConfigureCheckstyle(project, recorder -> recorder.setQualityGates(List.of( new WarningsQualityGate(3, QualityGateType.TOTAL_LOW, QualityGateCriticality.UNSTABLE)))); runJobTwice(project, Result.UNSTABLE); } + private FreeStyleProject createJobWithReferenceFinder() { + var project = createFreeStyleProject(); + project.getPublishersList().add(new SimpleReferenceRecorder()); + return project; + } + /** * Tests if the build is considered a failure when its defined threshold for all new issues(overall) is reached. */ @Test void shouldBeFailureWhenFailedNewAllIsReachedNew() { - FreeStyleProject project = createFreeStyleProject(); + FreeStyleProject project = createJobWithReferenceFinder(); enableAndConfigureCheckstyle(project, recorder -> recorder.setQualityGates(List.of( new WarningsQualityGate(9, QualityGateType.NEW, QualityGateCriticality.FAILURE)))); @@ -282,7 +289,7 @@ void shouldBeFailureWhenFailedNewAllIsReachedNew() { */ @Test void shouldBeFailureWhenFailedNewErrorIsReachedNew() { - FreeStyleProject project = createFreeStyleProject(); + FreeStyleProject project = createJobWithReferenceFinder(); enableAndConfigureCheckstyle(project, recorder -> recorder.setQualityGates(List.of( new WarningsQualityGate(6, QualityGateType.NEW_ERROR, QualityGateCriticality.FAILURE)))); @@ -295,7 +302,7 @@ void shouldBeFailureWhenFailedNewErrorIsReachedNew() { */ @Test void shouldBeFailureWhenFailedNewNormalIsReachedNew() { - FreeStyleProject project = createFreeStyleProject(); + FreeStyleProject project = createJobWithReferenceFinder(); enableAndConfigureCheckstyle(project, recorder -> recorder.setQualityGates(List.of( new WarningsQualityGate(2, QualityGateType.NEW_NORMAL, QualityGateCriticality.FAILURE)))); @@ -308,7 +315,7 @@ void shouldBeFailureWhenFailedNewNormalIsReachedNew() { */ @Test void shouldBeFailureWhenFailedNewLowIsReachedNew() { - FreeStyleProject project = createFreeStyleProject(); + FreeStyleProject project = createJobWithReferenceFinder(); enableAndConfigureCheckstyle(project, recorder -> recorder.setQualityGates(List.of( new WarningsQualityGate(3, QualityGateType.NEW_LOW, QualityGateCriticality.FAILURE)))); @@ -320,7 +327,7 @@ void shouldBeFailureWhenFailedNewLowIsReachedNew() { */ @Test void shouldBeFailureWhenFailureTotalAllIsReachedNew() { - FreeStyleProject project = createFreeStyleProject(); + FreeStyleProject project = createJobWithReferenceFinder(); enableAndConfigureCheckstyle(project, recorder -> recorder.setQualityGates(List.of( new WarningsQualityGate(11, QualityGateType.TOTAL, QualityGateCriticality.FAILURE)))); @@ -332,7 +339,7 @@ void shouldBeFailureWhenFailureTotalAllIsReachedNew() { */ @Test void shouldBeFailureWhenFailureTotalErrorIsReachedNew() { - FreeStyleProject project = createFreeStyleProject(); + FreeStyleProject project = createJobWithReferenceFinder(); enableAndConfigureCheckstyle(project, recorder -> recorder.setQualityGates(List.of( new WarningsQualityGate(6, QualityGateType.TOTAL_ERROR, QualityGateCriticality.FAILURE)))); @@ -345,7 +352,7 @@ void shouldBeFailureWhenFailureTotalErrorIsReachedNew() { */ @Test void shouldBeFailureWhenFailureTotalNormalIsReachedNew() { - FreeStyleProject project = createFreeStyleProject(); + FreeStyleProject project = createJobWithReferenceFinder(); enableAndConfigureCheckstyle(project, recorder -> recorder.setQualityGates(List.of( new WarningsQualityGate(2, QualityGateType.TOTAL_NORMAL, QualityGateCriticality.FAILURE)))); @@ -357,7 +364,7 @@ void shouldBeFailureWhenFailureTotalNormalIsReachedNew() { */ @Test void shouldBeFailureWhenFailureTotalLowIsReachedLow() { - FreeStyleProject project = createFreeStyleProject(); + FreeStyleProject project = createJobWithReferenceFinder(); enableAndConfigureCheckstyle(project, recorder -> recorder.setQualityGates(List.of( new WarningsQualityGate(3, QualityGateType.TOTAL_LOW, QualityGateCriticality.FAILURE)))); @@ -369,7 +376,7 @@ void shouldBeFailureWhenFailureTotalLowIsReachedLow() { */ @Test void shouldOverrideUnstableWhenFailureAndUnstableThresholdIsReachedNew() { - FreeStyleProject project = createFreeStyleProject(); + FreeStyleProject project = createJobWithReferenceFinder(); enableAndConfigureCheckstyle(project, recorder -> recorder.setQualityGates(List.of( new WarningsQualityGate(1, QualityGateType.TOTAL, QualityGateCriticality.UNSTABLE), new WarningsQualityGate(3, QualityGateType.TOTAL_LOW, QualityGateCriticality.FAILURE)))); diff --git a/plugin/src/test/java/io/jenkins/plugins/analysis/warnings/steps/ReferenceFinderITest.java b/plugin/src/test/java/io/jenkins/plugins/analysis/warnings/steps/ReferenceFinderITest.java index e76b9aa276..b2beaeb0c1 100644 --- a/plugin/src/test/java/io/jenkins/plugins/analysis/warnings/steps/ReferenceFinderITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/analysis/warnings/steps/ReferenceFinderITest.java @@ -22,6 +22,7 @@ import io.jenkins.plugins.analysis.core.util.WarningsQualityGate; import io.jenkins.plugins.analysis.core.util.WarningsQualityGate.QualityGateType; import io.jenkins.plugins.analysis.warnings.Java; +import io.jenkins.plugins.forensics.reference.SimpleReferenceRecorder; import io.jenkins.plugins.util.QualityGate.QualityGateCriticality; import io.jenkins.plugins.util.QualityGateStatus; @@ -40,43 +41,6 @@ class ReferenceFinderITest extends IntegrationTestWithJenkinsPerTest { private static final String DISCOVER_REFERENCE_BUILD_STEP = "discoverReferenceBuild(referenceJob:'reference')"; private static final String PUBLISH_ISSUES_STEP = "publishIssues issues:[issues]"; - /** - * Creates a reference job and starts a build having 2 warnings. Then two builds for the job. Then another job is - * created that uses the first build as a reference. Verifies that the association is correctly stored. - */ - // TODO: The functionality within this test is deprecated and will be removed in a future release - @Test - void shouldUseOtherJobBuildAsReference() { - WorkflowJob reference = createPipeline("reference"); - copyMultipleFilesToWorkspaceWithSuffix(reference, JAVA_ONE_WARNING); - reference.setDefinition(createPipelineScriptWithScanAndPublishSteps(new Java())); - - AnalysisResult firstReferenceResult = scheduleSuccessfulBuild(reference); - cleanWorkspace(reference); - copyMultipleFilesToWorkspaceWithSuffix(reference, JAVA_TWO_WARNINGS); - AnalysisResult secondReferenceResult = scheduleSuccessfulBuild(reference); - - assertThat(firstReferenceResult).hasTotalSize(1); - assertThat(firstReferenceResult.getReferenceBuild()).isEmpty(); - assertThat(firstReferenceResult.getOwner().getId()).isEqualTo("1"); - - assertThat(secondReferenceResult).hasTotalSize(2).hasNewSize(1); - assertThat(secondReferenceResult.getReferenceBuild().get().getId()).isEqualTo("1"); - assertThat(secondReferenceResult.getOwner().getId()).isEqualTo("2"); - - WorkflowJob job = createPipelineWithWorkspaceFilesWithSuffix(JAVA_TWO_WARNINGS); - job.setDefinition(asStage(createScanForIssuesStep(new Java()), - "publishIssues issues:[issues], referenceJobName:'reference', referenceBuildId: '1'")); - - AnalysisResult result = scheduleSuccessfulBuild(job); - - assertThat(result.getReferenceBuild()).isPresent(); - assertThat(result.getReferenceBuild().get().getId()).isEqualTo(firstReferenceResult.getOwner().getId()); - assertThat(result.getReferenceBuild().get().getId()).isEqualTo("1"); - - assertThat(result.getNewIssues()).hasSize(1); - } - /** * Creates a reference job and starts the analysis for this job. Then another job is created that uses the first one * as reference. Verifies that the association is correctly stored. @@ -104,7 +68,7 @@ void shouldUseOtherJobAsReference() { assertThat(getConsoleLog(result)).contains( "[ReferenceFinder] Configured reference job: 'reference'", - "[ReferenceFinder] Found reference build '#1' for target branch", + "[ReferenceFinder] Found reference build '#1' of reference job 'reference'", "Obtaining reference build from reference recorder", "-> Found 'reference #1'"); } @@ -132,8 +96,7 @@ void shouldHandleMissingJobBuildAsReference() { assertThat(getConsoleLog(result)).contains( "Obtaining reference build from reference recorder", "-> No reference build recorded", - "Obtaining reference build from same job", - "No valid reference build found that meets the criteria (NO_JOB_FAILURE - SUCCESSFUL_QUALITY_GATE)", + "No valid reference build found", "All reported issues will be considered outstanding"); } @@ -143,7 +106,7 @@ void shouldHandleMissingJobBuildAsReference() { @Test void shouldResetReference() { // #1 SUCCESS - FreeStyleProject project = createJob(JOB_NAME, "eclipse2Warnings.txt"); + FreeStyleProject project = createReferenceJob(); enableWarnings(project, recorder -> recorder.setQualityGates(List.of( new WarningsQualityGate(3, QualityGateType.NEW, QualityGateCriticality.UNSTABLE)))); scheduleBuildAndAssertStatus(project, Result.SUCCESS, @@ -208,7 +171,7 @@ void shouldResetReference() { @Test void shouldCreateSuccessResultWithIgnoredUnstableInBetween() { // #1 SUCCESS - FreeStyleProject project = createJob(JOB_NAME, "eclipse2Warnings.txt"); + FreeStyleProject project = createReferenceJob(); enableWarnings(project, recorder -> recorder.setQualityGates(List.of( new WarningsQualityGate(3, QualityGateType.NEW, QualityGateCriticality.UNSTABLE)))); Run expectedReference = scheduleBuildAndAssertStatus(project, Result.SUCCESS, @@ -236,6 +199,10 @@ void shouldCreateSuccessResultWithIgnoredUnstableInBetween() { .hasReferenceBuild(Optional.of(expectedReference))); } + private FreeStyleProject createReferenceJob() { + return createJob(JOB_NAME, "eclipse2Warnings.txt"); + } + private void createResetAction(final Run unstable, final String id) { ResetQualityGateCommand resetCommand = new ResetQualityGateCommand(); resetCommand.resetReferenceBuild(unstable, id); @@ -247,7 +214,7 @@ private void createResetAction(final Run unstable, final String id) { @Test void shouldCreateUnstableResultWithIgnoredUnstableInBetween() { // #1 SUCCESS - FreeStyleProject project = createJob(JOB_NAME, "eclipse2Warnings.txt"); + FreeStyleProject project = createReferenceJob(); enableWarnings(project, recorder -> recorder.setQualityGates(List.of( new WarningsQualityGate(3, QualityGateType.NEW, QualityGateCriticality.UNSTABLE)))); Run expectedReference = scheduleBuildAndAssertStatus(project, Result.SUCCESS, @@ -277,7 +244,7 @@ void shouldCreateUnstableResultWithIgnoredUnstableInBetween() { @Test void shouldCreateSuccessResultWithNotIgnoredUnstableInBetween() { // #1 SUCCESS - FreeStyleProject project = createJob(JOB_NAME, "eclipse2Warnings.txt"); + FreeStyleProject project = createReferenceJob(); enableWarnings(project, recorder -> { recorder.setIgnoreQualityGate(true); recorder.setQualityGates(List.of( @@ -345,7 +312,7 @@ void shouldCreateUnstableResultWithNotIgnoredUnstableInBetween() { @Test void shouldCreateUnstableResultWithOverAllMustBeSuccess() { // #1 SUCCESS - FreeStyleProject project = createJob(JOB_NAME, "eclipse2Warnings.txt"); + FreeStyleProject project = createReferenceJob(); enableWarnings(project, recorder -> { recorder.setIgnoreFailedBuilds(true); recorder.setEnabledForFailure(true); @@ -457,7 +424,7 @@ void shouldCreateUnstableResultWithOverAllMustNotBeSuccess() { @Test void shouldCreateSuccessResultWithOverAllMustNotBeSuccess() { // #1 SUCCESS - FreeStyleProject project = createJob(JOB_NAME, "eclipse2Warnings.txt"); + FreeStyleProject project = createReferenceJob(); enableWarnings(project, recorder -> { recorder.setIgnoreFailedBuilds(false); recorder.setEnabledForFailure(true); @@ -512,10 +479,10 @@ void shouldCreateSuccessResultWithIgnoredUnstableInBetweenWithReferenceBuild() { // #1 SUCCESS (Reference #1) FreeStyleProject project = createJob(JOB_NAME, "eclipse4Warnings.txt"); + enableWarnings(project, recorder -> { recorder.setQualityGates(List.of( new WarningsQualityGate(3, QualityGateType.NEW, QualityGateCriticality.UNSTABLE))); - recorder.setReferenceJobName(REFERENCE_JOB_NAME); recorder.setQualityGates(List.of( new WarningsQualityGate(7, QualityGateType.TOTAL, QualityGateCriticality.UNSTABLE))); }); @@ -553,10 +520,10 @@ void shouldCreateUnstableResultWithIgnoredUnstableInBetweenWithReferenceBuild() // #1 SUCCESS (Reference #1) FreeStyleProject project = createJob(JOB_NAME, "eclipse8Warnings.txt"); + enableWarnings(project, recorder -> { recorder.setQualityGates(List.of( new WarningsQualityGate(3, QualityGateType.NEW, QualityGateCriticality.UNSTABLE))); - recorder.setReferenceJobName(REFERENCE_JOB_NAME); recorder.setIgnoreQualityGate(false); }); @@ -594,10 +561,10 @@ void shouldCreateSuccessResultWithNotIgnoredUnstableInBetweenWithReferenceBuild( // #1 SUCCESS (Reference #2) FreeStyleProject project = createJob(JOB_NAME, "eclipse8Warnings.txt"); + enableWarnings(project, recorder -> { recorder.setQualityGates(List.of( new WarningsQualityGate(3, QualityGateType.NEW, QualityGateCriticality.UNSTABLE))); - recorder.setReferenceJobName(REFERENCE_JOB_NAME); recorder.setIgnoreQualityGate(true); }); scheduleBuildAndAssertStatus(project, Result.SUCCESS, analysisResult -> assertThat(analysisResult) @@ -633,11 +600,11 @@ void shouldCreateUnstableResultWithNotIgnoredUnstableInBetweenWithReferenceBuild // #1 SUCCESS (Reference #2) FreeStyleProject project = createJob(JOB_NAME, "eclipse8Warnings.txt"); + enableWarnings(project, recorder -> { recorder.setQualityGates(List.of( new WarningsQualityGate(3, QualityGateType.NEW, QualityGateCriticality.UNSTABLE), new WarningsQualityGate(9, QualityGateType.TOTAL, QualityGateCriticality.UNSTABLE))); - recorder.setReferenceJobName(REFERENCE_JOB_NAME); recorder.setIgnoreQualityGate(true); }); scheduleBuildAndAssertStatus(project, Result.UNSTABLE, analysisResult -> assertThat(analysisResult) @@ -677,10 +644,10 @@ void shouldCreateUnstableResultWithOverAllMustBeSuccessWithReferenceBuild() { // #1 SUCCESS (Reference #1) FreeStyleProject project = createJob(JOB_NAME, "eclipse6Warnings.txt"); + enableWarnings(project, recorder -> { recorder.setQualityGates(List.of( new WarningsQualityGate(3, QualityGateType.NEW, QualityGateCriticality.UNSTABLE))); - recorder.setReferenceJobName(REFERENCE_JOB_NAME); recorder.setIgnoreFailedBuilds(true); recorder.setEnabledForFailure(true); }); @@ -720,10 +687,10 @@ void shouldCreateSuccessResultWithOverAllMustBeSuccessWithReferenceBuild() { // #1 SUCCESS (Reference #1) FreeStyleProject project = createJob(JOB_NAME, "eclipse6Warnings.txt"); + enableWarnings(project, recorder -> { recorder.setQualityGates(List.of( new WarningsQualityGate(3, QualityGateType.NEW, QualityGateCriticality.UNSTABLE))); - recorder.setReferenceJobName(REFERENCE_JOB_NAME); recorder.setIgnoreFailedBuilds(true); recorder.setEnabledForFailure(true); }); @@ -764,10 +731,10 @@ void shouldCreateUnstableResultWithOverAllMustNotBeSuccessWithReferenceBuild() { // #1 UNSTABLE (Reference #2) FreeStyleProject project = createJob(JOB_NAME, "eclipse6Warnings.txt"); + enableWarnings(project, recorder -> { recorder.setQualityGates(List.of( new WarningsQualityGate(3, QualityGateType.NEW, QualityGateCriticality.UNSTABLE))); - recorder.setReferenceJobName(REFERENCE_JOB_NAME); recorder.setIgnoreFailedBuilds(false); recorder.setEnabledForFailure(true); }); @@ -809,10 +776,10 @@ void shouldCreateSuccessResultWithOverAllMustNotBeSuccessWithReferenceBuild() { // #1 UNSTABLE (Reference #2) FreeStyleProject project = createJob(JOB_NAME, "eclipse6Warnings.txt"); + enableWarnings(project, recorder -> { recorder.setQualityGates(List.of( new WarningsQualityGate(3, QualityGateType.NEW, QualityGateCriticality.UNSTABLE))); - recorder.setReferenceJobName(REFERENCE_JOB_NAME); recorder.setIgnoreFailedBuilds(false); recorder.setEnabledForFailure(true); }); @@ -848,6 +815,11 @@ private void cleanAndCopy(final FreeStyleProject project, final String fileName) private FreeStyleProject createJob(final String jobName, final String fileName) { FreeStyleProject job = createProject(FreeStyleProject.class, jobName); + var referenceRecorder = new SimpleReferenceRecorder(); + job.getPublishersList().add(referenceRecorder); + if (!referenceRecorder.equals(REFERENCE_JOB_NAME)) { + referenceRecorder.setReferenceJob(REFERENCE_JOB_NAME); + } copyMultipleFilesToWorkspaceWithSuffix(job, fileName); return job; } diff --git a/plugin/src/test/java/io/jenkins/plugins/analysis/warnings/steps/StepsITest.java b/plugin/src/test/java/io/jenkins/plugins/analysis/warnings/steps/StepsITest.java index a96e4f1f74..7c4deefe66 100644 --- a/plugin/src/test/java/io/jenkins/plugins/analysis/warnings/steps/StepsITest.java +++ b/plugin/src/test/java/io/jenkins/plugins/analysis/warnings/steps/StepsITest.java @@ -150,6 +150,7 @@ void shouldWhitelistRecorderApi() { private void configureRecorder(final WorkflowJob job, final String fileName) { job.setDefinition(new CpsFlowDefinition("node {\n" + " stage ('Integration Test') {\n" + + " discoverReferenceBuild()\n" + " def reports = recordIssues tool: checkStyle(pattern: '**/" + fileName + "*'), qualityGates: [[threshold: 4, type: 'TOTAL', unstable: true]]\n" + " echo '[reportsSize=' + reports.size() + ']' \n" @@ -264,6 +265,7 @@ private void configurePublisher(final WorkflowJob job, final String fileName, fi String qualityGateParameter = String.format("qualityGates: [%s]", qualityGate); job.setDefinition(new CpsFlowDefinition("node {\n" + " stage ('Integration Test') {\n" + + " discoverReferenceBuild()\n" + " def issues = scanForIssues tool: checkStyle(pattern: '**/" + fileName + "*')\n" + " def action = publishIssues issues:[issues], " + qualityGateParameter + "\n" + " echo '[id=' + action.getId() + ']' \n" @@ -1008,27 +1010,6 @@ private void setFilter(final WorkflowJob job, final String filters) { job.setDefinition(asStage(scanWithFilter, "publishIssues issues:[issues]")); } - /** - * Creates a reference job with a build, then builds the job, referring to a non-existing build in the reference - * job. - */ - @Test - void shouldHandleMissingJobBuildIdAsReference() { - WorkflowJob reference = createPipeline("reference"); - reference.setDefinition(createPipelineScriptWithScanAndPublishSteps(new Java())); - - WorkflowJob job = createPipelineWithWorkspaceFilesWithSuffix("java-start.txt"); - job.setDefinition(asStage(createScanForIssuesStep(new Java()), - "publishIssues issues:[issues], referenceJobName:'reference', referenceBuildId: '1'")); - - AnalysisResult result = scheduleSuccessfulBuild(job); - - assertThat(result.getReferenceBuild()).isEmpty(); - assertThat(result).hasNewSize(0).hasTotalSize(2); - assertThat(result.getErrorMessages()).contains( - "Reference job 'reference' does not contain configured build '1'"); - } - /** * Verifies that when publishIssues marks the build as unstable it also marks the step with WarningAction so that * visualizations can display the step as unstable rather than just the whole build. diff --git a/plugin/src/test/resources/io/jenkins/plugins/analysis/warnings/integrations/job-dsl-warnings-ng.yaml b/plugin/src/test/resources/io/jenkins/plugins/analysis/warnings/integrations/job-dsl-warnings-ng.yaml index f32069f8b3..a51f35a417 100644 --- a/plugin/src/test/resources/io/jenkins/plugins/analysis/warnings/integrations/job-dsl-warnings-ng.yaml +++ b/plugin/src/test/resources/io/jenkins/plugins/analysis/warnings/integrations/job-dsl-warnings-ng.yaml @@ -17,7 +17,6 @@ jobs: minimumSeverity('ERROR') sourceCodeEncoding('UTF-8') unhealthy(50) - referenceJobName('test-job') qualityGates { warningsQualityGate { threshold(10)