Skip to content

Commit

Permalink
Rewrite reference finder.
Browse files Browse the repository at this point in the history
  • Loading branch information
uhafner committed Feb 7, 2024
1 parent 8087575 commit 208ef28
Show file tree
Hide file tree
Showing 11 changed files with 205 additions and 307 deletions.
6 changes: 3 additions & 3 deletions plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>org.jvnet.hudson.plugins</groupId>
<artifactId>analysis-pom</artifactId>
<version>6.17.0</version>
<version>7.1.0</version>
<relativePath />
</parent>

Expand Down Expand Up @@ -41,8 +41,8 @@
<testcontainers.version>1.19.3</testcontainers.version>

<!-- Jenkins Plug-in Dependencies Versions -->
<forensics-api.version>2.4.0-rc1560.b_4b_993ec6a_9f</forensics-api.version>
<git-forensics.version>2.1.0-20240205.180020-1</git-forensics.version>
<forensics-api.version>2.4.0-rc1561.1f746b_248f63</forensics-api.version>
<git-forensics.version>2.1.0-20240207.074913-2</git-forensics.version>

<jenkins-maven-plugin.version>3.22</jenkins-maven-plugin.version>
<job-dsl.version>1.84</job-dsl.version>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,17 @@
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;

import hudson.model.Result;
import hudson.model.Run;

import io.jenkins.plugins.analysis.core.charts.JenkinsBuild;
import io.jenkins.plugins.analysis.core.util.AnalysisBuildResult;

import static io.jenkins.plugins.analysis.core.model.AnalysisHistory.JobResultEvaluationMode.*;
import static io.jenkins.plugins.analysis.core.model.AnalysisHistory.QualityGateEvaluationMode.*;

/**
* Provides a history of static analysis results. The history starts from a baseline build and provides access to a
* historical build result of the same type (or to all historical results using the provided {@link
* AnalysisResultIterator interator} implementation). The results are filtered by a {@link ResultSelector}, so a history
* returns only results of the same type. This history can be configured to ignore the overall result of the associated
* Jenkins builds (see {@link JobResultEvaluationMode JobResultEvaluationMode}). Additionally, this history can be
* configured to ignore the builds that did not pass the quality gate (see {@link QualityGateEvaluationMode
* returns only results of the same type. This history can be configured to ignore the builds that did not pass the quality
* gate (see {@link QualityGateEvaluationMode
* QualityGateEvaluationMode}). Note that the baseline run might still be in progress and thus has not yet a result
* attached: i.e., the result of the {@code getPrevious*} methods may return different results on subsequent calls.
*
Expand All @@ -38,81 +33,17 @@ public class AnalysisHistory implements History {
/** Selects a result of the same type. */
private final ResultSelector selector;

private final QualityGateEvaluationMode qualityGateEvaluationMode;
private final JobResultEvaluationMode jobResultEvaluationMode;

/**
* Determines how the evaluation of the quality gates is taken into account when the previous result is
* searched for.
*/
public enum QualityGateEvaluationMode {
/**
* The quality gate result is ignored. The previous build with results of the same type is selected.
*/
IGNORE_QUALITY_GATE,
/**
* The quality gate result must be successful. I.e., the history is searched for a
* build that either passed the quality gate or has deactivated the quality gate.
*/
SUCCESSFUL_QUALITY_GATE
}

/**
* Determines how the overall build {@link Result} of the {@link Run} is taken into account when the previous result
* is searched for.
*/
public enum JobResultEvaluationMode {
/**
* Only those jobs are considered that did not fail. I.e., jobs with result {@link Result#UNSTABLE} or {@link
* Result#SUCCESS}.
*/
NO_JOB_FAILURE,

/**
* All jobs are considered regardless of the result. If the job has an overall result of {@link Result#FAILURE}
* then it will be considered as well.
*/
IGNORE_JOB_RESULT
}

/**
* Creates a new instance of {@link AnalysisHistory}. This history ignores the results of the
* quality gate and the {@link Result} of the associated {@link Run}.
* Creates a new instance of {@link AnalysisHistory}.
*
* @param baseline
* the build to start the history from
* @param selector
* selects the associated action from a build
*/
public AnalysisHistory(final Run<?, ?> baseline, final ResultSelector selector) {
this(baseline, selector, IGNORE_QUALITY_GATE, IGNORE_JOB_RESULT);
}

/**
* Creates a new instance of {@link AnalysisHistory}.
*
* @param baseline
* the run to start the history from
* @param selector
* selects the type of the result (to get a result for the same type of static analysis)
* @param qualityGateEvaluationMode
* If set to {@link QualityGateEvaluationMode#IGNORE_QUALITY_GATE}, then the result of the quality gate is
* ignored when selecting a reference build. If set to {@link QualityGateEvaluationMode#SUCCESSFUL_QUALITY_GATE}
* a failing quality gate will be passed from build to build until the original reason for the failure has
* been resolved.
* @param jobResultEvaluationMode
* If set to {@link JobResultEvaluationMode#NO_JOB_FAILURE}, then only successful or unstable reference
* builds will be considered (since analysis results might be inaccurate if the build failed). If set to
* {@link JobResultEvaluationMode#IGNORE_JOB_RESULT}, then every build that contains a static analysis
* result is considered, even if the build failed.
*/
public AnalysisHistory(final Run<?, ?> baseline, final ResultSelector selector,
final QualityGateEvaluationMode qualityGateEvaluationMode,
final JobResultEvaluationMode jobResultEvaluationMode) {
this.baseline = baseline;
this.selector = selector;
this.qualityGateEvaluationMode = qualityGateEvaluationMode;
this.jobResultEvaluationMode = jobResultEvaluationMode;
}

@Override
Expand Down Expand Up @@ -141,46 +72,24 @@ public Report getIssues() {
}

private Optional<ResultAction> getPreviousAction() {
Optional<Run<?, ?>> run = getRunWithResult(baseline, selector, qualityGateEvaluationMode,
jobResultEvaluationMode);
Optional<Run<?, ?>> run = getRunWithResult(baseline, selector);
if (run.isPresent()) {
return selector.get(run.get());
}
return Optional.empty();
}

private static Optional<Run<?, ?>> getRunWithResult(final @CheckForNull Run<?, ?> start,
final ResultSelector selector,
final QualityGateEvaluationMode qualityGateEvaluationMode,
final JobResultEvaluationMode jobResultEvaluationMode) {
private static Optional<Run<?, ?>> getRunWithResult(
final @CheckForNull Run<?, ?> start, final ResultSelector selector) {
for (Run<?, ?> run = start; run != null; run = run.getPreviousBuild()) {
Optional<ResultAction> action = selector.get(run);
if (action.isPresent()) {
ResultAction resultAction = action.get();
if (hasCorrectJobResult(run, jobResultEvaluationMode)
&& hasCorrectQualityGateStatus(resultAction, qualityGateEvaluationMode)) {
return Optional.of(run);
}
return Optional.of(run);
}
}
return Optional.empty();
}

private static boolean hasCorrectQualityGateStatus(final ResultAction action,
final QualityGateEvaluationMode qualityGateEvaluationMode) {
return action.isSuccessful() || qualityGateEvaluationMode == IGNORE_QUALITY_GATE;
}

private static boolean hasCorrectJobResult(final Run<?, ?> run,
final JobResultEvaluationMode jobResultEvaluationMode) {
if (jobResultEvaluationMode == NO_JOB_FAILURE) {
Result result = run.getResult();

return result != null && result.isBetterThan(Result.FAILURE);
}
return true;
}

@Override
public boolean hasMultipleResults() {
Iterator<BuildResult<AnalysisBuildResult>> iterator = iterator();
Expand All @@ -195,7 +104,7 @@ public boolean hasMultipleResults() {

@Override
public String toString() {
return String.format("%s - %s", jobResultEvaluationMode, qualityGateEvaluationMode);
return String.format("%s - %s", baseline.getFullDisplayName(), selector);
}

@Override
Expand All @@ -221,7 +130,7 @@ private static class AnalysisResultIterator implements Iterator<BuildResult<Anal
* selects the associated action from a build
*/
AnalysisResultIterator(final Run<?, ?> baseline, final ResultSelector selector) {
cursor = getRunWithResult(baseline, selector, IGNORE_QUALITY_GATE, IGNORE_JOB_RESULT);
cursor = getRunWithResult(baseline, selector);
this.selector = selector;
}

Expand All @@ -236,7 +145,7 @@ public BuildResult<AnalysisBuildResult> next() {
Run<?, ?> run = cursor.get();
Optional<ResultAction> resultAction = selector.get(run);

cursor = getRunWithResult(run.getPreviousBuild(), selector, IGNORE_QUALITY_GATE, IGNORE_JOB_RESULT);
cursor = getRunWithResult(run.getPreviousBuild(), selector);

if (resultAction.isPresent()) {
return new BuildResult<>(new JenkinsBuild(run), resultAction.get().getResult());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package io.jenkins.plugins.analysis.core.model;

/**
* Determines how the evaluation of the quality gates is taken into account when the previous result is searched for.
*/
public enum QualityGateEvaluationMode {
/**
* The quality gate result is ignored. The previous build with results of the same type is selected.
*/
IGNORE_QUALITY_GATE,
/**
* The quality gate result must be successful. I.e., the history is searched for a build that either passed the
* quality gate or has deactivated the quality gate.
*/
SUCCESSFUL_QUALITY_GATE
}
Loading

0 comments on commit 208ef28

Please sign in to comment.