aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-ce-task-projectanalysis/src
diff options
context:
space:
mode:
authorDuarte Meneses <duarte.meneses@sonarsource.com>2021-01-12 15:22:28 -0600
committersonartech <sonartech@sonarsource.com>2021-01-21 20:30:29 +0000
commit3fe62fecd08570c507506bebddf96ef47d3923a5 (patch)
treeaf88af89d5641e9e98e1a6fa7775811a40da674b /server/sonar-ce-task-projectanalysis/src
parent891ed0cbda954290bbe2ce13e605506c9eb01353 (diff)
downloadsonarqube-3fe62fecd08570c507506bebddf96ef47d3923a5.tar.gz
sonarqube-3fe62fecd08570c507506bebddf96ef47d3923a5.zip
SONAR-14257 Pull requests should inherit issue state from its target branch
Diffstat (limited to 'server/sonar-ce-task-projectanalysis/src')
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java5
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/PullRequestTrackerExecution.java32
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/ReferenceBranchTrackerExecution.java3
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TargetBranchComponentUuids.java89
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TrackerTargetBranchInputFactory.java90
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IntegrateIssuesVisitorTest.java5
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/PullRequestTrackerExecutionTest.java60
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/TargetBranchComponentUuidsTest.java126
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/TrackerTargetBranchInputFactoryTest.java111
9 files changed, 492 insertions, 29 deletions
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java
index 52637abdf53..89f83b97594 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java
@@ -81,11 +81,13 @@ import org.sonar.ce.task.projectanalysis.issue.ScmAccountToUserLoader;
import org.sonar.ce.task.projectanalysis.issue.SiblingsIssueMerger;
import org.sonar.ce.task.projectanalysis.issue.SiblingsIssuesLoader;
import org.sonar.ce.task.projectanalysis.issue.SourceBranchComponentUuids;
+import org.sonar.ce.task.projectanalysis.issue.TargetBranchComponentUuids;
import org.sonar.ce.task.projectanalysis.issue.TrackerBaseInputFactory;
import org.sonar.ce.task.projectanalysis.issue.TrackerExecution;
import org.sonar.ce.task.projectanalysis.issue.TrackerRawInputFactory;
import org.sonar.ce.task.projectanalysis.issue.TrackerReferenceBranchInputFactory;
import org.sonar.ce.task.projectanalysis.issue.TrackerSourceBranchInputFactory;
+import org.sonar.ce.task.projectanalysis.issue.TrackerTargetBranchInputFactory;
import org.sonar.ce.task.projectanalysis.issue.UpdateConflictResolver;
import org.sonar.ce.task.projectanalysis.issue.commonrule.BranchCoverageRule;
import org.sonar.ce.task.projectanalysis.issue.commonrule.CommentDensityRule;
@@ -273,9 +275,10 @@ public final class ProjectAnalysisTaskContainerPopulator implements ContainerPop
NewSecurityReviewMeasuresVisitor.class,
LastCommitVisitor.class,
MeasureComputersVisitor.class,
-
+ TargetBranchComponentUuids.class,
UpdateConflictResolver.class,
TrackerBaseInputFactory.class,
+ TrackerTargetBranchInputFactory.class,
TrackerRawInputFactory.class,
TrackerReferenceBranchInputFactory.class,
TrackerSourceBranchInputFactory.class,
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/PullRequestTrackerExecution.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/PullRequestTrackerExecution.java
index eb9dbfe9e3b..5d51346180e 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/PullRequestTrackerExecution.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/PullRequestTrackerExecution.java
@@ -25,6 +25,7 @@ import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
+import org.sonar.api.issue.Issue;
import org.sonar.ce.task.projectanalysis.component.Component;
import org.sonar.ce.task.projectanalysis.source.NewLinesRepository;
import org.sonar.core.issue.DefaultIssue;
@@ -36,23 +37,40 @@ public class PullRequestTrackerExecution {
private final TrackerBaseInputFactory baseInputFactory;
private final Tracker<DefaultIssue, DefaultIssue> tracker;
private final NewLinesRepository newLinesRepository;
+ private final TrackerTargetBranchInputFactory targetInputFactory;
- public PullRequestTrackerExecution(TrackerBaseInputFactory baseInputFactory, Tracker<DefaultIssue, DefaultIssue> tracker,
- NewLinesRepository newLinesRepository) {
+ public PullRequestTrackerExecution(TrackerBaseInputFactory baseInputFactory, TrackerTargetBranchInputFactory targetInputFactory,
+ Tracker<DefaultIssue, DefaultIssue> tracker, NewLinesRepository newLinesRepository) {
this.baseInputFactory = baseInputFactory;
+ this.targetInputFactory = targetInputFactory;
this.tracker = tracker;
this.newLinesRepository = newLinesRepository;
}
public Tracking<DefaultIssue, DefaultIssue> track(Component component, Input<DefaultIssue> rawInput) {
- Input<DefaultIssue> previousAnalysisInput = baseInputFactory.create(component);
-
// Step 1: only keep issues on changed lines
List<DefaultIssue> filteredRaws = keepIssuesHavingAtLeastOneLocationOnChangedLines(component, rawInput.getIssues());
- Input<DefaultIssue> unmatchedRawsAfterChangedLineFiltering = new DefaultTrackingInput(filteredRaws, rawInput.getLineHashSequence(), rawInput.getBlockHashSequence());
+ Input<DefaultIssue> unmatchedRawsAfterChangedLineFiltering = createInput(rawInput, filteredRaws);
+
+ // Step 2: remove issues that are resolved in the target branch
+ Input<DefaultIssue> unmatchedRawsAfterTargetResolvedTracking;
+ if (targetInputFactory.hasTargetBranchAnalysis()) {
+ Input<DefaultIssue> targetInput = targetInputFactory.createForTargetBranch(component);
+ List<DefaultIssue> resolvedTargetIssues = targetInput.getIssues().stream().filter(i -> Issue.STATUS_RESOLVED.equals(i.status())).collect(Collectors.toList());
+ Input<DefaultIssue> resolvedTargetInput = createInput(targetInput, resolvedTargetIssues);
+ Tracking<DefaultIssue, DefaultIssue> prResolvedTracking = tracker.trackNonClosed(unmatchedRawsAfterChangedLineFiltering, resolvedTargetInput);
+ unmatchedRawsAfterTargetResolvedTracking = createInput(rawInput, prResolvedTracking.getUnmatchedRaws().collect(Collectors.toList()));
+ } else {
+ unmatchedRawsAfterTargetResolvedTracking = unmatchedRawsAfterChangedLineFiltering;
+ }
+
+ // Step 3: track issues with previous analysis of the current PR
+ Input<DefaultIssue> previousAnalysisInput = baseInputFactory.create(component);
+ return tracker.trackNonClosed(unmatchedRawsAfterTargetResolvedTracking, previousAnalysisInput);
+ }
- // Step 2: track issues with previous analysis of the current PR
- return tracker.trackNonClosed(unmatchedRawsAfterChangedLineFiltering, previousAnalysisInput);
+ private static Input<DefaultIssue> createInput(Input<DefaultIssue> input, Collection<DefaultIssue> issues) {
+ return new DefaultTrackingInput(issues, input.getLineHashSequence(), input.getBlockHashSequence());
}
private List<DefaultIssue> keepIssuesHavingAtLeastOneLocationOnChangedLines(Component component, Collection<DefaultIssue> issues) {
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/ReferenceBranchTrackerExecution.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/ReferenceBranchTrackerExecution.java
index 080f3ed704c..ab7735f1ff2 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/ReferenceBranchTrackerExecution.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/ReferenceBranchTrackerExecution.java
@@ -29,8 +29,7 @@ public class ReferenceBranchTrackerExecution {
private final TrackerReferenceBranchInputFactory referenceBranchInputFactory;
private final Tracker<DefaultIssue, DefaultIssue> tracker;
- public ReferenceBranchTrackerExecution(TrackerReferenceBranchInputFactory referenceBranchInputFactory,
- Tracker<DefaultIssue, DefaultIssue> tracker) {
+ public ReferenceBranchTrackerExecution(TrackerReferenceBranchInputFactory referenceBranchInputFactory, Tracker<DefaultIssue, DefaultIssue> tracker) {
this.referenceBranchInputFactory = referenceBranchInputFactory;
this.tracker = tracker;
}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TargetBranchComponentUuids.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TargetBranchComponentUuids.java
new file mode 100644
index 00000000000..88866706e87
--- /dev/null
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TargetBranchComponentUuids.java
@@ -0,0 +1,89 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.issue;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import javax.annotation.CheckForNull;
+import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.BranchDto;
+import org.sonar.db.component.ComponentDto;
+
+import static org.sonar.db.component.ComponentDto.removeBranchAndPullRequestFromKey;
+
+/**
+ * Cache a map between component keys and uuids in the merge branch and optionally the target branch (for PR and SLB, and only if this target branch is analyzed)
+ */
+public class TargetBranchComponentUuids {
+ private final AnalysisMetadataHolder analysisMetadataHolder;
+ private final DbClient dbClient;
+ private Map<String, String> targetBranchComponentsUuidsByKey;
+ private boolean hasTargetBranchAnalysis;
+ @CheckForNull
+ private String targetBranchUuid;
+
+ public TargetBranchComponentUuids(AnalysisMetadataHolder analysisMetadataHolder, DbClient dbClient) {
+ this.analysisMetadataHolder = analysisMetadataHolder;
+ this.dbClient = dbClient;
+ }
+
+ private void lazyInit() {
+ if (targetBranchComponentsUuidsByKey == null) {
+ targetBranchComponentsUuidsByKey = new HashMap<>();
+
+ if (analysisMetadataHolder.isPullRequest()) {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ initForTargetBranch(dbSession);
+ }
+ } else {
+ hasTargetBranchAnalysis = false;
+ }
+ }
+ }
+
+ private void initForTargetBranch(DbSession dbSession) {
+ Optional<BranchDto> branchDtoOpt = dbClient.branchDao().selectByBranchKey(dbSession, analysisMetadataHolder.getProject().getUuid(),
+ analysisMetadataHolder.getBranch().getTargetBranchName());
+ targetBranchUuid = branchDtoOpt.map(BranchDto::getUuid).orElse(null);
+ hasTargetBranchAnalysis = targetBranchUuid != null && dbClient.snapshotDao().selectLastAnalysisByRootComponentUuid(dbSession, targetBranchUuid).isPresent();
+ if (hasTargetBranchAnalysis) {
+ List<ComponentDto> targetComponents = dbClient.componentDao().selectByProjectUuid(targetBranchUuid, dbSession);
+ for (ComponentDto dto : targetComponents) {
+ targetBranchComponentsUuidsByKey.put(dto.getKey(), dto.uuid());
+ }
+ }
+ }
+
+ public boolean hasTargetBranchAnalysis() {
+ lazyInit();
+ return hasTargetBranchAnalysis;
+ }
+
+ @CheckForNull
+ public String getTargetBranchComponentUuid(String dbKey) {
+ lazyInit();
+ String cleanComponentKey = removeBranchAndPullRequestFromKey(dbKey);
+ return targetBranchComponentsUuidsByKey.get(cleanComponentKey);
+ }
+}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TrackerTargetBranchInputFactory.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TrackerTargetBranchInputFactory.java
new file mode 100644
index 00000000000..d5197a3f893
--- /dev/null
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TrackerTargetBranchInputFactory.java
@@ -0,0 +1,90 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.issue;
+
+import java.util.Collections;
+import java.util.List;
+import javax.annotation.Nullable;
+import org.sonar.ce.task.projectanalysis.component.Component;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.tracking.Input;
+import org.sonar.core.issue.tracking.LazyInput;
+import org.sonar.core.issue.tracking.LineHashSequence;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+
+public class TrackerTargetBranchInputFactory {
+ private static final LineHashSequence EMPTY_LINE_HASH_SEQUENCE = new LineHashSequence(Collections.emptyList());
+
+ private final ComponentIssuesLoader componentIssuesLoader;
+ private final DbClient dbClient;
+ private final TargetBranchComponentUuids targetBranchComponentUuids;
+
+ public TrackerTargetBranchInputFactory(ComponentIssuesLoader componentIssuesLoader, TargetBranchComponentUuids targetBranchComponentUuids,
+ DbClient dbClient) {
+ this.componentIssuesLoader = componentIssuesLoader;
+ this.targetBranchComponentUuids = targetBranchComponentUuids;
+ this.dbClient = dbClient;
+ // TODO detect file moves?
+ }
+
+ public boolean hasTargetBranchAnalysis() {
+ return targetBranchComponentUuids.hasTargetBranchAnalysis();
+ }
+
+ public Input<DefaultIssue> createForTargetBranch(Component component) {
+ String targetBranchComponentUuid = targetBranchComponentUuids.getTargetBranchComponentUuid(component.getDbKey());
+ return new TargetLazyInput(component.getType(), targetBranchComponentUuid);
+ }
+
+ private class TargetLazyInput extends LazyInput<DefaultIssue> {
+ private final Component.Type type;
+ private final String targetBranchComponentUuid;
+
+ private TargetLazyInput(Component.Type type, @Nullable String targetBranchComponentUuid) {
+ this.type = type;
+ this.targetBranchComponentUuid = targetBranchComponentUuid;
+ }
+
+ @Override
+ protected LineHashSequence loadLineHashSequence() {
+ if (targetBranchComponentUuid == null || type != Component.Type.FILE) {
+ return EMPTY_LINE_HASH_SEQUENCE;
+ }
+
+ try (DbSession session = dbClient.openSession(false)) {
+ List<String> hashes = dbClient.fileSourceDao().selectLineHashes(session, targetBranchComponentUuid);
+ if (hashes == null || hashes.isEmpty()) {
+ return EMPTY_LINE_HASH_SEQUENCE;
+ }
+ return new LineHashSequence(hashes);
+ }
+ }
+
+ @Override
+ protected List<DefaultIssue> loadIssues() {
+ if (targetBranchComponentUuid == null) {
+ return Collections.emptyList();
+ }
+ return componentIssuesLoader.loadOpenIssuesWithChanges(targetBranchComponentUuid);
+ }
+ }
+
+}
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IntegrateIssuesVisitorTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IntegrateIssuesVisitorTest.java
index 618e7ab7a0f..b68348ecc41 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IntegrateIssuesVisitorTest.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IntegrateIssuesVisitorTest.java
@@ -127,7 +127,7 @@ public class IntegrateIssuesVisitorTest {
private final ReferenceBranchComponentUuids referenceBranchComponentUuids = mock(ReferenceBranchComponentUuids.class);
private final SourceLinesHashRepository sourceLinesHash = mock(SourceLinesHashRepository.class);
private final NewLinesRepository newLinesRepository = mock(NewLinesRepository.class);
-
+ private TargetBranchComponentUuids targetBranchComponentUuids = mock(TargetBranchComponentUuids.class);
private ArgumentCaptor<DefaultIssue> defaultIssueCaptor;
private final ComponentIssuesLoader issuesLoader = new ComponentIssuesLoader(dbTester.getDbClient(), ruleRepositoryRule, activeRulesHolderRule, new MapSettings().asConfig(),
@@ -153,11 +153,12 @@ public class IntegrateIssuesVisitorTest {
issueFilter, ruleRepositoryRule, activeRulesHolder);
TrackerBaseInputFactory baseInputFactory = new TrackerBaseInputFactory(issuesLoader, dbClient, movedFilesRepository, mock(ReportModulesPath.class), analysisMetadataHolder,
new IssueFieldsSetter(), mock(ComponentsWithUnprocessedIssues.class));
+ TrackerTargetBranchInputFactory targetInputFactory = new TrackerTargetBranchInputFactory(issuesLoader, targetBranchComponentUuids, dbClient);
TrackerReferenceBranchInputFactory mergeInputFactory = new TrackerReferenceBranchInputFactory(issuesLoader, mergeBranchComponentsUuids, dbClient);
ClosedIssuesInputFactory closedIssuesInputFactory = new ClosedIssuesInputFactory(issuesLoader, dbClient, movedFilesRepository);
tracker = new TrackerExecution(baseInputFactory, closedIssuesInputFactory, new Tracker<>(), issuesLoader, analysisMetadataHolder);
- prBranchTracker = new PullRequestTrackerExecution(baseInputFactory, new Tracker<>(), newLinesRepository);
mergeBranchTracker = new ReferenceBranchTrackerExecution(mergeInputFactory, new Tracker<>());
+ prBranchTracker = new PullRequestTrackerExecution(baseInputFactory, targetInputFactory, new Tracker<>(), newLinesRepository);
trackingDelegator = new IssueTrackingDelegator(prBranchTracker, mergeBranchTracker, tracker, analysisMetadataHolder);
treeRootHolder.setRoot(PROJECT);
protoIssueCache = new ProtoIssueCache(temp.newFile(), System2.INSTANCE);
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/PullRequestTrackerExecutionTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/PullRequestTrackerExecutionTest.java
index 0f0f13015b8..07591fe172b 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/PullRequestTrackerExecutionTest.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/PullRequestTrackerExecutionTest.java
@@ -28,6 +28,7 @@ import java.util.List;
import java.util.Optional;
import org.junit.Before;
import org.junit.Test;
+import org.sonar.api.issue.Issue;
import org.sonar.api.rule.RuleKey;
import org.sonar.ce.task.projectanalysis.component.Component;
import org.sonar.ce.task.projectanalysis.source.NewLinesRepository;
@@ -60,26 +61,26 @@ public class PullRequestTrackerExecutionTest {
private final NewLinesRepository newLinesRepository = mock(NewLinesRepository.class);
private final List<DefaultIssue> rawIssues = new ArrayList<>();
private final List<DefaultIssue> baseIssues = new ArrayList<>();
+ private final List<DefaultIssue> targetIssues = new ArrayList<>();
private PullRequestTrackerExecution underTest;
+ private TrackerTargetBranchInputFactory targetFactory = mock(TrackerTargetBranchInputFactory.class);
+
@Before
public void setUp() {
when(baseFactory.create(FILE)).thenReturn(createInput(baseIssues));
+ when(targetFactory.createForTargetBranch(FILE)).thenReturn(createInput(targetIssues));
Tracker<DefaultIssue, DefaultIssue> tracker = new Tracker<>();
- underTest = new PullRequestTrackerExecution(baseFactory, tracker, newLinesRepository);
+ underTest = new PullRequestTrackerExecution(baseFactory, targetFactory, tracker, newLinesRepository);
}
@Test
public void simple_tracking_keep_only_issues_having_location_on_changed_lines() {
- final DefaultIssue issue1 = createIssue(2, RuleTesting.XOO_X1);
- issue1.setLocations(DbIssues.Locations.newBuilder()
- .setTextRange(DbCommons.TextRange.newBuilder().setStartLine(2).setEndLine(3)).build());
+ final DefaultIssue issue1 = createIssue(2, 3, RuleTesting.XOO_X1);
rawIssues.add(issue1);
- final DefaultIssue issue2 = createIssue(2, RuleTesting.XOO_X1);
- issue2.setLocations(DbIssues.Locations.newBuilder().setTextRange(DbCommons.TextRange.newBuilder().setStartLine(2).setEndLine(2)).build());
- rawIssues.add(issue2);
+ rawIssues.add(createIssue(2, RuleTesting.XOO_X1));
when(newLinesRepository.getNewLines(FILE)).thenReturn(Optional.of(new HashSet<>(Arrays.asList(1, 3))));
@@ -127,17 +128,37 @@ public class PullRequestTrackerExecutionTest {
}
@Test
- public void track_and_ignore_issues_from_previous_analysis() {
+ public void track_and_ignore_resolved_issues_from_target_branch() {
when(newLinesRepository.getNewLines(FILE)).thenReturn(Optional.of(new HashSet<>(Arrays.asList(1, 2, 3))));
- rawIssues.add(createIssue(1, RuleTesting.XOO_X1)
- .setLocations(DbIssues.Locations.newBuilder().setTextRange(DbCommons.TextRange.newBuilder().setStartLine(1).setEndLine(1)).build()));
- rawIssues.add(createIssue(2, RuleTesting.XOO_X2)
- .setLocations(DbIssues.Locations.newBuilder().setTextRange(DbCommons.TextRange.newBuilder().setStartLine(2).setEndLine(2)).build()));
- rawIssues.add(createIssue(3, RuleTesting.XOO_X3)
- .setLocations(DbIssues.Locations.newBuilder().setTextRange(DbCommons.TextRange.newBuilder().setStartLine(3).setEndLine(3)).build()));
+ rawIssues.add(createIssue(1, RuleTesting.XOO_X1));
+ rawIssues.add(createIssue(2, RuleTesting.XOO_X2));
+ rawIssues.add(createIssue(3, RuleTesting.XOO_X3));
+
+ when(targetFactory.hasTargetBranchAnalysis()).thenReturn(true);
+ DefaultIssue resolvedIssue = createIssue(1, RuleTesting.XOO_X1).setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_FALSE_POSITIVE);
+ // will cause rawIssue0 to be ignored
+ targetIssues.add(resolvedIssue);
+ // not ignored since it's not resolved
+ targetIssues.add(rawIssues.get(1));
baseIssues.add(rawIssues.get(0));
+ // should be matched
+ baseIssues.add(rawIssues.get(1));
+
+ Tracking<DefaultIssue, DefaultIssue> tracking = underTest.track(FILE, createInput(rawIssues));
+ assertThat(tracking.getMatchedRaws()).isEqualTo(Collections.singletonMap(rawIssues.get(1), rawIssues.get(1)));
+ assertThat(tracking.getUnmatchedRaws()).containsOnly(rawIssues.get(2));
+ }
+
+ @Test
+ public void track_and_ignore_issues_from_previous_analysis() {
+ when(newLinesRepository.getNewLines(FILE)).thenReturn(Optional.of(new HashSet<>(Arrays.asList(1, 2, 3))));
+
+ rawIssues.add(createIssue(1, RuleTesting.XOO_X1));
+ rawIssues.add(createIssue(2, RuleTesting.XOO_X2));
+ rawIssues.add(createIssue(3, RuleTesting.XOO_X3));
+ baseIssues.add(rawIssues.get(0));
Tracking<DefaultIssue, DefaultIssue> tracking = underTest.track(FILE, createInput(rawIssues));
assertThat(tracking.getMatchedRaws()).isEqualTo(Collections.singletonMap(rawIssues.get(0), rawIssues.get(0)));
@@ -145,13 +166,18 @@ public class PullRequestTrackerExecutionTest {
}
private DefaultIssue createIssue(int line, RuleKey ruleKey) {
+ return createIssue(line, line, ruleKey);
+ }
+
+ private DefaultIssue createIssue(int startLine, int endLine, RuleKey ruleKey) {
return new DefaultIssue()
.setRuleKey(ruleKey)
- .setLine(line)
- .setMessage("msg" + line);
+ .setLine(startLine)
+ .setLocations(DbIssues.Locations.newBuilder().setTextRange(DbCommons.TextRange.newBuilder().setStartLine(startLine).setEndLine(endLine)).build())
+ .setMessage("msg" + startLine);
}
- private Input<DefaultIssue> createInput(Collection<DefaultIssue> issues) {
+ private static Input<DefaultIssue> createInput(Collection<DefaultIssue> issues) {
return new Input<DefaultIssue>() {
@Override
public LineHashSequence getLineHashSequence() {
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/TargetBranchComponentUuidsTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/TargetBranchComponentUuidsTest.java
new file mode 100644
index 00000000000..23bcc027f1e
--- /dev/null
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/TargetBranchComponentUuidsTest.java
@@ -0,0 +1,126 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.issue;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
+import org.sonar.ce.task.projectanalysis.analysis.Branch;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.BranchType;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.ComponentTesting;
+import org.sonar.db.protobuf.DbProjectBranches;
+import org.sonar.server.project.Project;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.db.component.SnapshotTesting.newAnalysis;
+
+public class TargetBranchComponentUuidsTest {
+ private static final String BRANCH_KEY = "branch1";
+ private static final String PR_KEY = "pr1";
+
+ @org.junit.Rule
+ public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule();
+
+ @Rule
+ public DbTester db = DbTester.create();
+
+ private TargetBranchComponentUuids underTest;
+ private final Branch branch = mock(Branch.class);
+ private ComponentDto branch1;
+ private ComponentDto branch1File;
+ private ComponentDto pr1File;
+
+ @Before
+ public void setup() {
+ underTest = new TargetBranchComponentUuids(analysisMetadataHolder, db.getDbClient());
+ Project project = mock(Project.class);
+ analysisMetadataHolder.setProject(project);
+ analysisMetadataHolder.setBranch(branch);
+
+ ComponentDto projectDto = db.components().insertPublicProject();
+ when(project.getUuid()).thenReturn(projectDto.uuid());
+ branch1 = db.components().insertProjectBranch(projectDto, b -> b.setKey(BRANCH_KEY));
+ ComponentDto pr1branch = db.components().insertProjectBranch(projectDto, b -> b.setKey(PR_KEY)
+ .setBranchType(BranchType.PULL_REQUEST)
+ .setPullRequestData(DbProjectBranches.PullRequestData.newBuilder().setTarget(BRANCH_KEY).build())
+ .setMergeBranchUuid(projectDto.getMainBranchProjectUuid()));
+ branch1File = ComponentTesting.newFileDto(branch1, null, "file").setUuid("branch1File");
+ pr1File = ComponentTesting.newFileDto(pr1branch, null, "file").setUuid("file1");
+ db.components().insertComponents(branch1File, pr1File);
+ }
+
+ @Test
+ public void should_support_db_key_when_looking_for_target_branch_component() {
+ when(branch.getType()).thenReturn(BranchType.PULL_REQUEST);
+ when(branch.getName()).thenReturn("prBranch");
+ when(branch.getTargetBranchName()).thenReturn(BRANCH_KEY);
+
+ when(branch.getPullRequestKey()).thenReturn(PR_KEY);
+ db.components().insertSnapshot(newAnalysis(branch1));
+
+ assertThat(underTest.getTargetBranchComponentUuid(pr1File.getDbKey())).isEqualTo(branch1File.uuid());
+ assertThat(underTest.hasTargetBranchAnalysis()).isTrue();
+ }
+
+ @Test
+ public void should_support_key_when_looking_for_target_branch_component() {
+ when(branch.getType()).thenReturn(BranchType.PULL_REQUEST);
+ when(branch.getName()).thenReturn("prBranch");
+ when(branch.getTargetBranchName()).thenReturn(BRANCH_KEY);
+ when(branch.getPullRequestKey()).thenReturn(PR_KEY);
+ db.components().insertSnapshot(newAnalysis(branch1));
+
+ assertThat(underTest.getTargetBranchComponentUuid(pr1File.getKey())).isEqualTo(branch1File.uuid());
+ }
+
+ @Test
+ public void return_null_if_file_doesnt_exist() {
+ when(branch.getType()).thenReturn(BranchType.PULL_REQUEST);
+ when(branch.getName()).thenReturn("prBranch");
+ when(branch.getTargetBranchName()).thenReturn(BRANCH_KEY);
+ when(branch.getPullRequestKey()).thenReturn(PR_KEY);
+ db.components().insertSnapshot(newAnalysis(branch1));
+
+ assertThat(underTest.getTargetBranchComponentUuid("doesnt exist")).isNull();
+ }
+
+ @Test
+ public void skip_init_if_not_a_pull_request() {
+ when(branch.getType()).thenReturn(BranchType.BRANCH);
+ when(branch.getName()).thenReturn("prBranch");
+ when(branch.getTargetBranchName()).thenReturn(BRANCH_KEY);
+
+ assertThat(underTest.getTargetBranchComponentUuid(pr1File.getDbKey())).isNull();
+ }
+
+ @Test
+ public void skip_init_if_no_target_branch_analysis() {
+ when(branch.getType()).thenReturn(BranchType.PULL_REQUEST);
+ when(branch.getName()).thenReturn("prBranch");
+ when(branch.getTargetBranchName()).thenReturn(BRANCH_KEY);
+
+ assertThat(underTest.getTargetBranchComponentUuid(pr1File.getDbKey())).isNull();
+ }
+}
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/TrackerTargetBranchInputFactoryTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/TrackerTargetBranchInputFactoryTest.java
new file mode 100644
index 00000000000..3d2367902dc
--- /dev/null
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/TrackerTargetBranchInputFactoryTest.java
@@ -0,0 +1,111 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.ce.task.projectanalysis.issue;
+
+import java.util.Collections;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.ce.task.projectanalysis.component.Component;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.tracking.Input;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.ComponentTesting;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class TrackerTargetBranchInputFactoryTest {
+ private final static String COMPONENT_KEY = "file1";
+ private final static String COMPONENT_UUID = "uuid1";
+
+ @Rule
+ public DbTester db = DbTester.create();
+
+ private final ComponentIssuesLoader componentIssuesLoader = mock(ComponentIssuesLoader.class);
+ private final TargetBranchComponentUuids targetBranchComponentUuids = mock(TargetBranchComponentUuids.class);
+ private TrackerTargetBranchInputFactory underTest;
+
+ @Before
+ public void setUp() {
+ underTest = new TrackerTargetBranchInputFactory(componentIssuesLoader, targetBranchComponentUuids, db.getDbClient());
+ }
+
+ @Test
+ public void gets_issues_and_hashes_in_matching_component() {
+ DefaultIssue issue1 = new DefaultIssue();
+ when(targetBranchComponentUuids.getTargetBranchComponentUuid(COMPONENT_KEY)).thenReturn(COMPONENT_UUID);
+ when(componentIssuesLoader.loadOpenIssuesWithChanges(COMPONENT_UUID)).thenReturn(Collections.singletonList(issue1));
+ ComponentDto fileDto = ComponentTesting.newFileDto(ComponentTesting.newPublicProjectDto()).setUuid(COMPONENT_UUID);
+ db.fileSources().insertFileSource(fileDto, 3);
+
+ Component component = mock(Component.class);
+ when(component.getDbKey()).thenReturn(COMPONENT_KEY);
+ when(component.getType()).thenReturn(Component.Type.FILE);
+ Input<DefaultIssue> input = underTest.createForTargetBranch(component);
+
+ assertThat(input.getIssues()).containsOnly(issue1);
+ assertThat(input.getLineHashSequence().length()).isEqualTo(3);
+ }
+
+ @Test
+ public void get_issues_without_line_hashes() {
+ DefaultIssue issue1 = new DefaultIssue();
+ when(targetBranchComponentUuids.getTargetBranchComponentUuid(COMPONENT_KEY)).thenReturn(COMPONENT_UUID);
+ when(componentIssuesLoader.loadOpenIssuesWithChanges(COMPONENT_UUID)).thenReturn(Collections.singletonList(issue1));
+ ComponentDto fileDto = ComponentTesting.newFileDto(ComponentTesting.newPublicProjectDto()).setUuid(COMPONENT_UUID);
+ db.fileSources().insertFileSource(fileDto, 0);
+
+ Component component = mock(Component.class);
+ when(component.getDbKey()).thenReturn(COMPONENT_KEY);
+ when(component.getType()).thenReturn(Component.Type.FILE);
+ Input<DefaultIssue> input = underTest.createForTargetBranch(component);
+
+ assertThat(input.getIssues()).containsOnly(issue1);
+ assertThat(input.getLineHashSequence().length()).isZero();
+ }
+
+ @Test
+ public void gets_nothing_when_there_is_no_matching_component() {
+ Component component = mock(Component.class);
+ when(component.getDbKey()).thenReturn(COMPONENT_KEY);
+ when(component.getType()).thenReturn(Component.Type.FILE);
+ Input<DefaultIssue> input = underTest.createForTargetBranch(component);
+
+ assertThat(input.getIssues()).isEmpty();
+ assertThat(input.getLineHashSequence().length()).isZero();
+ }
+
+ @Test
+ public void hasTargetBranchAnalysis_returns_true_if_source_branch_of_pr_was_analysed() {
+ when(targetBranchComponentUuids.hasTargetBranchAnalysis()).thenReturn(true);
+
+ assertThat(underTest.hasTargetBranchAnalysis()).isTrue();
+ }
+
+ @Test
+ public void hasTargetBranchAnalysis_returns_false_if_no_target_branch_analysis() {
+ when(targetBranchComponentUuids.hasTargetBranchAnalysis()).thenReturn(false);
+
+ assertThat(underTest.hasTargetBranchAnalysis()).isFalse();
+ }
+}