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 org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
import static com.google.common.base.Preconditions.checkState;
import static org.sonar.db.component.ComponentDto.removeBranchAndPullRequestFromKey;
private final DbClient dbClient;
private Map<String, String> uuidsByKey;
private String mergeBranchName;
+ private boolean hasMergeBranchAnalysis;
public MergeBranchComponentUuids(AnalysisMetadataHolder analysisMetadataHolder, DbClient dbClient) {
this.analysisMetadataHolder = analysisMetadataHolder;
Optional<BranchDto> opt = dbClient.branchDao().selectByUuid(dbSession, mergeBranchUuid);
checkState(opt.isPresent(), "Merge branch '%s' does not exist", mergeBranchUuid);
mergeBranchName = opt.get().getKey();
+
+ hasMergeBranchAnalysis = dbClient.snapshotDao().selectLastAnalysisByRootComponentUuid(dbSession, mergeBranchUuid).isPresent();
}
}
}
+ public boolean hasMergeBranchAnalysis() {
+ lazyInit();
+ return hasMergeBranchAnalysis;
+ }
+
public String getMergeBranchName() {
lazyInit();
return mergeBranchName;
import org.sonar.ce.task.projectanalysis.issue.ScmAccountToUserLoader;
import org.sonar.ce.task.projectanalysis.issue.ShortBranchIssueMerger;
import org.sonar.ce.task.projectanalysis.issue.ShortBranchIssuesLoader;
-import org.sonar.ce.task.projectanalysis.issue.ShortBranchTrackerExecution;
+import org.sonar.ce.task.projectanalysis.issue.ShortBranchOrPullRequestTrackerExecution;
import org.sonar.ce.task.projectanalysis.issue.TrackerBaseInputFactory;
import org.sonar.ce.task.projectanalysis.issue.TrackerExecution;
import org.sonar.ce.task.projectanalysis.issue.TrackerMergeBranchInputFactory;
ClosedIssuesInputFactory.class,
Tracker.class,
TrackerExecution.class,
- ShortBranchTrackerExecution.class,
+ ShortBranchOrPullRequestTrackerExecution.class,
MergeBranchTrackerExecution.class,
ComponentIssuesLoader.class,
BaseIssuesLoader.class,
}
}
- private void processLeaf(Component file, Path<FormulaExecutorComponentVisitor.Counters> path) {
- CounterInitializationContext counterContext = new CounterInitializationContextImpl(file);
+ private void processLeaf(Component component, Path<FormulaExecutorComponentVisitor.Counters> path) {
+ CounterInitializationContext counterContext = new CounterInitializationContextImpl(component);
for (Formula formula : formulas) {
Counter counter = formula.createNewCounter();
counter.initialize(counterContext);
for (String metricKey : formula.getOutputMetricKeys()) {
- addNewMeasure(file, metricKey, formula, counter);
+ addNewMeasure(component, metricKey, formula, counter);
}
aggregateToParent(path, formula, counter);
}
}
private class CounterInitializationContextImpl implements CounterInitializationContext {
- private final Component file;
+ private final Component component;
- public CounterInitializationContextImpl(Component file) {
- this.file = file;
+ public CounterInitializationContextImpl(Component component) {
+ this.component = component;
}
@Override
public Component getLeaf() {
- return file;
+ return component;
}
@Override
public Optional<Measure> getMeasure(String metricKey) {
- return measureRepository.getRawMeasure(file, metricRepository.getByKey(metricKey));
+ return measureRepository.getRawMeasure(component, metricRepository.getByKey(metricKey));
}
}
import static java.util.Collections.emptyMap;
public class IssueTrackingDelegator {
- private final ShortBranchTrackerExecution shortBranchTracker;
+ private final ShortBranchOrPullRequestTrackerExecution shortBranchOrPullRequestTracker;
private final TrackerExecution tracker;
private final AnalysisMetadataHolder analysisMetadataHolder;
private final MergeBranchTrackerExecution mergeBranchTracker;
- public IssueTrackingDelegator(ShortBranchTrackerExecution shortBranchTracker, MergeBranchTrackerExecution longBranchTracker,
- TrackerExecution tracker, AnalysisMetadataHolder analysisMetadataHolder) {
- this.shortBranchTracker = shortBranchTracker;
+ public IssueTrackingDelegator(ShortBranchOrPullRequestTrackerExecution shortBranchOrPullRequestTracker, MergeBranchTrackerExecution longBranchTracker,
+ TrackerExecution tracker, AnalysisMetadataHolder analysisMetadataHolder) {
+ this.shortBranchOrPullRequestTracker = shortBranchOrPullRequestTracker;
this.mergeBranchTracker = longBranchTracker;
this.tracker = tracker;
this.analysisMetadataHolder = analysisMetadataHolder;
public TrackingResult track(Component component) {
if (analysisMetadataHolder.isShortLivingBranch() || analysisMetadataHolder.isPullRequest()) {
- return standardResult(shortBranchTracker.track(component));
+ return standardResult(shortBranchOrPullRequestTracker.track(component));
} else if (isFirstAnalysisSecondaryLongLivingBranch()) {
Tracking<DefaultIssue, DefaultIssue> tracking = mergeBranchTracker.track(component);
return new TrackingResult(tracking.getMatchedRaws(), emptyMap(), Stream.empty(), tracking.getUnmatchedRaws());
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.sonar.ce.task.projectanalysis.component.Component;
+import org.sonar.ce.task.projectanalysis.source.NewLinesRepository;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.tracking.Input;
+import org.sonar.core.issue.tracking.Tracker;
+import org.sonar.core.issue.tracking.Tracking;
+import org.sonar.core.util.stream.MoreCollectors;
+
+public class ShortBranchOrPullRequestTrackerExecution {
+ private final TrackerBaseInputFactory baseInputFactory;
+ private final TrackerRawInputFactory rawInputFactory;
+ private final TrackerMergeBranchInputFactory mergeInputFactory;
+ private final Tracker<DefaultIssue, DefaultIssue> tracker;
+ private final NewLinesRepository newLinesRepository;
+
+ public ShortBranchOrPullRequestTrackerExecution(TrackerBaseInputFactory baseInputFactory, TrackerRawInputFactory rawInputFactory,
+ TrackerMergeBranchInputFactory mergeInputFactory,
+ Tracker<DefaultIssue, DefaultIssue> tracker, NewLinesRepository newLinesRepository) {
+ this.baseInputFactory = baseInputFactory;
+ this.rawInputFactory = rawInputFactory;
+ this.mergeInputFactory = mergeInputFactory;
+ this.tracker = tracker;
+ this.newLinesRepository = newLinesRepository;
+ }
+
+ public Tracking<DefaultIssue, DefaultIssue> track(Component component) {
+ Input<DefaultIssue> rawInput = rawInputFactory.create(component);
+ Input<DefaultIssue> baseInput = baseInputFactory.create(component);
+
+ Input<DefaultIssue> unmatchedRawInput;
+ if (mergeInputFactory.hasMergeBranchAnalysis()) {
+ Input<DefaultIssue> mergeInput = mergeInputFactory.create(component);
+ Tracking<DefaultIssue, DefaultIssue> mergeTracking = tracker.trackNonClosed(rawInput, mergeInput);
+ List<DefaultIssue> unmatchedRaws = mergeTracking.getUnmatchedRaws().collect(MoreCollectors.toList());
+ unmatchedRawInput = new DefaultTrackingInput(unmatchedRaws, rawInput.getLineHashSequence(), rawInput.getBlockHashSequence());
+ } else {
+ List<DefaultIssue> filteredRaws = keepIssuesHavingAtLeastOneLocationOnChangedLines(component, rawInput.getIssues());
+ unmatchedRawInput = new DefaultTrackingInput(filteredRaws, rawInput.getLineHashSequence(), rawInput.getBlockHashSequence());
+ }
+
+ // do second tracking with base branch using raws issues that are still unmatched
+ return tracker.trackNonClosed(unmatchedRawInput, baseInput);
+ }
+
+ private List<DefaultIssue> keepIssuesHavingAtLeastOneLocationOnChangedLines(Component component, Collection<DefaultIssue> issues) {
+ if (component.getType() != Component.Type.FILE) {
+ return Collections.emptyList();
+ }
+ final Optional<Set<Integer>> newLinesOpt = newLinesRepository.getNewLines(component);
+ if (!newLinesOpt.isPresent()) {
+ return Collections.emptyList();
+ }
+ final Set<Integer> newLines = newLinesOpt.get();
+ return issues
+ .stream()
+ .filter(i -> IssueLocations.allLinesFor(i, component.getUuid()).anyMatch(newLines::contains))
+ .collect(Collectors.toList());
+ }
+
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.List;
-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.Tracker;
-import org.sonar.core.issue.tracking.Tracking;
-import org.sonar.core.util.stream.MoreCollectors;
-
-public class ShortBranchTrackerExecution {
- private final TrackerBaseInputFactory baseInputFactory;
- private final TrackerRawInputFactory rawInputFactory;
- private final TrackerMergeBranchInputFactory mergeInputFactory;
- private final Tracker<DefaultIssue, DefaultIssue> tracker;
-
- public ShortBranchTrackerExecution(TrackerBaseInputFactory baseInputFactory, TrackerRawInputFactory rawInputFactory, TrackerMergeBranchInputFactory mergeInputFactory,
- Tracker<DefaultIssue, DefaultIssue> tracker) {
- this.baseInputFactory = baseInputFactory;
- this.rawInputFactory = rawInputFactory;
- this.mergeInputFactory = mergeInputFactory;
- this.tracker = tracker;
- }
-
- public Tracking<DefaultIssue, DefaultIssue> track(Component component) {
- Input<DefaultIssue> rawInput = rawInputFactory.create(component);
- Input<DefaultIssue> baseInput = baseInputFactory.create(component);
- Input<DefaultIssue> mergeInput = mergeInputFactory.create(component);
-
- Tracking<DefaultIssue, DefaultIssue> mergeTracking = tracker.trackNonClosed(rawInput, mergeInput);
- List<DefaultIssue> unmatchedRaws = mergeTracking.getUnmatchedRaws().collect(MoreCollectors.toList());
- Input<DefaultIssue> unmatchedRawInput = new DefaultTrackingInput(unmatchedRaws, rawInput.getLineHashSequence(), rawInput.getBlockHashSequence());
-
- // do second tracking with base branch using raws issues that are still unmatched
- return tracker.trackNonClosed(unmatchedRawInput, baseInput);
- }
-
-}
// TODO detect file moves?
}
+ public boolean hasMergeBranchAnalysis() {
+ return mergeBranchComponentUuids.hasMergeBranchAnalysis();
+ }
+
public Input<DefaultIssue> create(Component component) {
String mergeBranchComponentUuid = mergeBranchComponentUuids.getUuid(component.getDbKey());
return new MergeLazyInput(component.getType(), mergeBranchComponentUuid);
*/
package org.sonar.ce.task.projectanalysis.source;
+import com.google.common.base.Preconditions;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
return isPullRequestOrShortLivedBranch() || periodHolder.hasPeriod();
}
- public Optional<Set<Integer>> getNewLines(Component component) {
- Optional<Set<Integer>> reportChangedLines = getChangedLinesFromReport(component);
+ public Optional<Set<Integer>> getNewLines(Component file) {
+ Preconditions.checkArgument(file.getType() == Component.Type.FILE, "Changed lines are only available on files, but was: " + file.getType().name());
+ Optional<Set<Integer>> reportChangedLines = getChangedLinesFromReport(file);
if (reportChangedLines.isPresent()) {
return reportChangedLines;
}
- return computeNewLinesFromScm(component);
+ return computeNewLinesFromScm(file);
}
/**
return lineDate > period.getSnapshotDate();
}
- private Optional<Set<Integer>> getChangedLinesFromReport(Component component) {
+ private Optional<Set<Integer>> getChangedLinesFromReport(Component file) {
if (isPullRequestOrShortLivedBranch()) {
- return reportChangedLinesCache.computeIfAbsent(component, this::readFromReport);
+ return reportChangedLinesCache.computeIfAbsent(file, this::readFromReport);
}
return Optional.empty();
return analysisMetadataHolder.isPullRequest() || analysisMetadataHolder.isShortLivingBranch();
}
- private Optional<Set<Integer>> readFromReport(Component component) {
- return reportReader.readComponentChangedLines(component.getReportAttributes().getRef())
+ private Optional<Set<Integer>> readFromReport(Component file) {
+ return reportReader.readComponentChangedLines(file.getReportAttributes().getRef())
.map(c -> new HashSet<>(c.getLineList()));
}
}
*/
package org.sonar.ce.task.projectanalysis.step;
+import java.util.Optional;
import java.util.function.Function;
-import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonar.ce.task.projectanalysis.analysis.Analysis;
import org.sonar.ce.task.projectanalysis.analysis.Branch;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.SnapshotDto;
-import org.sonar.db.component.SnapshotQuery;
import org.sonar.scanner.protocol.output.ScannerReport;
import static com.google.common.base.MoreObjects.firstNonNull;
ComponentUuidFactoryWithMigration componentUuidFactoryWithMigration = new ComponentUuidFactoryWithMigration(dbClient, dbSession, rootKey, pathToKey, reportModulesPath.get());
String rootUuid = componentUuidFactoryWithMigration.getOrCreateForKey(rootKey);
- SnapshotDto baseAnalysis = loadBaseAnalysis(dbSession, rootUuid);
+ Optional<SnapshotDto> baseAnalysis = dbClient.snapshotDao().selectLastAnalysisByRootComponentUuid(dbSession, rootUuid);
ComponentTreeBuilder builder = new ComponentTreeBuilder(keyGenerator, publicKeyGenerator,
componentUuidFactoryWithMigration::getOrCreateForKey,
reportReader::readComponent,
analysisMetadataHolder.getProject(),
analysisMetadataHolder.getBranch(),
- createProjectAttributes(metadata, baseAnalysis),
+ createProjectAttributes(metadata, baseAnalysis.orElse(null)),
issueRelocationToRoot);
String relativePathFromScmRoot = metadata.getRelativePathFromScmRoot();
treeRootHolder.setRoots(reportTreeRoot, reportTreeRoot);
}
- analysisMetadataHolder.setBaseAnalysis(toAnalysis(baseAnalysis));
+ analysisMetadataHolder.setBaseAnalysis(baseAnalysis.map(BuildComponentTreeStep::toAnalysis).orElse(null));
context.getStatistics().add("components", treeRootHolder.getSize());
}
return branch;
}
- @CheckForNull
- private SnapshotDto loadBaseAnalysis(DbSession dbSession, String rootUuid) {
- return dbClient.snapshotDao().selectAnalysisByQuery(
- dbSession,
- new SnapshotQuery()
- .setComponentUuid(rootUuid)
- .setIsLast(true));
- }
-
- @CheckForNull
- private static Analysis toAnalysis(@Nullable SnapshotDto dto) {
- if (dto != null) {
- return new Analysis.Builder()
- .setId(dto.getId())
- .setUuid(dto.getUuid())
- .setCreatedAt(dto.getCreatedAt())
- .build();
- }
- return null;
+ private static Analysis toAnalysis(SnapshotDto dto) {
+ return new Analysis.Builder()
+ .setId(dto.getId())
+ .setUuid(dto.getUuid())
+ .setCreatedAt(dto.getCreatedAt())
+ .build();
}
}
@Override
public void initialize(CounterInitializationContext context) {
- Component fileComponent = context.getLeaf();
- Optional<Set<Integer>> newLinesSet = newLinesRepository.getNewLines(fileComponent);
+ Component component = context.getLeaf();
+ if (component.getType() != Component.Type.FILE) {
+ return;
+ }
+ Optional<Set<Integer>> newLinesSet = newLinesRepository.getNewLines(component);
if (!newLinesSet.isPresent()) {
return;
}
@Override
public void initialize(CounterInitializationContext context) {
Component leaf = context.getLeaf();
+ if (leaf.getType() != Component.Type.FILE) {
+ return;
+ }
Optional<Set<Integer>> changedLines = newLinesRepository.getNewLines(leaf);
if (!changedLines.isPresent()) {
return;
import org.sonar.ce.task.projectanalysis.qualityprofile.ActiveRulesHolder;
import org.sonar.ce.task.projectanalysis.qualityprofile.ActiveRulesHolderRule;
import org.sonar.ce.task.projectanalysis.qualityprofile.AlwaysActiveRulesHolderImpl;
+import org.sonar.ce.task.projectanalysis.source.NewLinesRepository;
import org.sonar.ce.task.projectanalysis.source.SourceLinesHashRepository;
import org.sonar.ce.task.projectanalysis.source.SourceLinesRepositoryRule;
import org.sonar.core.issue.DefaultIssue;
private MergeBranchComponentUuids mergeBranchComponentUuids = mock(MergeBranchComponentUuids.class);
private SourceLinesHashRepository sourceLinesHash = mock(SourceLinesHashRepository.class);
private IssueRelocationToRoot issueRelocationToRoot = mock(IssueRelocationToRoot.class);
+ private NewLinesRepository newLinesRepository = mock(NewLinesRepository.class);
private ArgumentCaptor<DefaultIssue> defaultIssueCaptor;
private ComponentIssuesLoader issuesLoader = new ComponentIssuesLoader(dbTester.getDbClient(), ruleRepositoryRule, activeRulesHolderRule, new MapSettings().asConfig(), System2.INSTANCE);
private IssueTrackingDelegator trackingDelegator;
private TrackerExecution tracker;
- private ShortBranchTrackerExecution shortBranchTracker;
+ private ShortBranchOrPullRequestTrackerExecution shortBranchTracker;
private MergeBranchTrackerExecution mergeBranchTracker;
private ActiveRulesHolder activeRulesHolder = new AlwaysActiveRulesHolderImpl();
private IssueCache issueCache;
TrackerMergeBranchInputFactory mergeInputFactory = new TrackerMergeBranchInputFactory(issuesLoader, mergeBranchComponentsUuids, dbClient);
ClosedIssuesInputFactory closedIssuesInputFactory = new ClosedIssuesInputFactory(issuesLoader, dbClient, movedFilesRepository);
tracker = new TrackerExecution(baseInputFactory, rawInputFactory, closedIssuesInputFactory, new Tracker<>(), issuesLoader, analysisMetadataHolder);
- shortBranchTracker = new ShortBranchTrackerExecution(baseInputFactory, rawInputFactory, mergeInputFactory, new Tracker<>());
+ shortBranchTracker = new ShortBranchOrPullRequestTrackerExecution(baseInputFactory, rawInputFactory, mergeInputFactory, new Tracker<>(), newLinesRepository);
mergeBranchTracker = new MergeBranchTrackerExecution(rawInputFactory, mergeInputFactory, new Tracker<>());
trackingDelegator = new IssueTrackingDelegator(shortBranchTracker, mergeBranchTracker, tracker, analysisMetadataHolder);
public class IssueTrackingDelegatorTest {
@Mock
- private ShortBranchTrackerExecution shortBranchTracker;
+ private ShortBranchOrPullRequestTrackerExecution shortBranchTracker;
@Mock
private MergeBranchTrackerExecution mergeBranchTracker;
@Mock
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.ce.task.projectanalysis.component.Component;
+import org.sonar.ce.task.projectanalysis.source.NewLinesRepository;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.tracking.BlockHashSequence;
+import org.sonar.core.issue.tracking.Input;
+import org.sonar.core.issue.tracking.LineHashSequence;
+import org.sonar.core.issue.tracking.Tracker;
+import org.sonar.core.issue.tracking.Tracking;
+import org.sonar.db.protobuf.DbCommons;
+import org.sonar.db.protobuf.DbIssues;
+import org.sonar.db.rule.RuleTesting;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.when;
+import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
+
+public class ShortBranchOrPullRequestTrackerExecutionTest {
+ static final String FILE_UUID = "FILE_UUID";
+ static final String FILE_KEY = "FILE_KEY";
+ static final int FILE_REF = 2;
+
+ static final Component FILE = builder(Component.Type.FILE, FILE_REF)
+ .setKey(FILE_KEY)
+ .setUuid(FILE_UUID)
+ .build();
+
+ @Mock
+ private TrackerRawInputFactory rawFactory;
+ @Mock
+ private TrackerBaseInputFactory baseFactory;
+ @Mock
+ private TrackerMergeBranchInputFactory mergeFactory;
+ @Mock
+ private NewLinesRepository newLinesRepository;
+
+ private ShortBranchOrPullRequestTrackerExecution underTest;
+
+ private List<DefaultIssue> rawIssues = new ArrayList<>();
+ private List<DefaultIssue> baseIssues = new ArrayList<>();
+ private List<DefaultIssue> mergeBranchIssues = new ArrayList<>();
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ when(rawFactory.create(FILE)).thenReturn(createInput(rawIssues));
+ when(baseFactory.create(FILE)).thenReturn(createInput(baseIssues));
+ when(mergeFactory.create(FILE)).thenReturn(createInput(mergeBranchIssues));
+
+ Tracker<DefaultIssue, DefaultIssue> tracker = new Tracker<>();
+ underTest = new ShortBranchOrPullRequestTrackerExecution(baseFactory, rawFactory, mergeFactory, 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());
+ 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);
+
+ when(mergeFactory.hasMergeBranchAnalysis()).thenReturn(false);
+ when(newLinesRepository.getNewLines(FILE)).thenReturn(Optional.of(new HashSet<>(Arrays.asList(1, 3))));
+
+ Tracking<DefaultIssue, DefaultIssue> tracking = underTest.track(FILE);
+
+ assertThat(tracking.getUnmatchedBases()).isEmpty();
+ assertThat(tracking.getMatchedRaws()).isEmpty();
+ assertThat(tracking.getUnmatchedRaws()).containsOnly(issue1);
+ }
+
+ @Test
+ public void simple_tracking_keep_also_issues_having_secondary_locations_on_changed_lines() {
+ final DefaultIssue issueWithSecondaryLocationOnAChangedLine = createIssue(2, RuleTesting.XOO_X1);
+ issueWithSecondaryLocationOnAChangedLine.setLocations(DbIssues.Locations.newBuilder()
+ .setTextRange(DbCommons.TextRange.newBuilder().setStartLine(2).setEndLine(3))
+ .addFlow(DbIssues.Flow.newBuilder().addLocation(DbIssues.Location.newBuilder()
+ .setComponentId(FILE.getUuid())
+ .setTextRange(DbCommons.TextRange.newBuilder().setStartLine(6).setEndLine(8)).build()).build())
+ .build());
+ rawIssues.add(issueWithSecondaryLocationOnAChangedLine);
+ final DefaultIssue issueWithNoLocationsOnChangedLines = createIssue(2, RuleTesting.XOO_X1);
+ issueWithNoLocationsOnChangedLines.setLocations(DbIssues.Locations.newBuilder()
+ .setTextRange(DbCommons.TextRange.newBuilder().setStartLine(2).setEndLine(2))
+ .addFlow(DbIssues.Flow.newBuilder().addLocation(DbIssues.Location.newBuilder()
+ .setComponentId(FILE.getUuid())
+ .setTextRange(DbCommons.TextRange.newBuilder().setStartLine(11).setEndLine(12)).build()).build())
+ .build());
+ rawIssues.add(issueWithNoLocationsOnChangedLines);
+ final DefaultIssue issueWithALocationOnADifferentFile = createIssue(2, RuleTesting.XOO_X1);
+ issueWithALocationOnADifferentFile.setLocations(DbIssues.Locations.newBuilder()
+ .setTextRange(DbCommons.TextRange.newBuilder().setStartLine(2).setEndLine(3))
+ .addFlow(DbIssues.Flow.newBuilder().addLocation(DbIssues.Location.newBuilder()
+ .setComponentId("anotherUuid")
+ .setTextRange(DbCommons.TextRange.newBuilder().setStartLine(6).setEndLine(8)).build()).build())
+ .build());
+ rawIssues.add(issueWithALocationOnADifferentFile);
+
+ when(mergeFactory.hasMergeBranchAnalysis()).thenReturn(false);
+ when(newLinesRepository.getNewLines(FILE)).thenReturn(Optional.of(new HashSet<>(Arrays.asList(7, 10))));
+
+ Tracking<DefaultIssue, DefaultIssue> tracking = underTest.track(FILE);
+
+ assertThat(tracking.getUnmatchedBases()).isEmpty();
+ assertThat(tracking.getMatchedRaws()).isEmpty();
+ assertThat(tracking.getUnmatchedRaws()).containsOnly(issueWithSecondaryLocationOnAChangedLine);
+ }
+
+ @Test
+ public void tracking_with_all_results() {
+ rawIssues.add(createIssue(1, RuleTesting.XOO_X1));
+ rawIssues.add(createIssue(2, RuleTesting.XOO_X2));
+ rawIssues.add(createIssue(3, RuleTesting.XOO_X3));
+
+ when(mergeFactory.hasMergeBranchAnalysis()).thenReturn(true);
+ mergeBranchIssues.add(rawIssues.get(0));
+
+ baseIssues.add(rawIssues.get(0));
+ baseIssues.add(rawIssues.get(1));
+
+ Tracking<DefaultIssue, DefaultIssue> tracking = underTest.track(FILE);
+ assertThat(tracking.getMatchedRaws()).isEqualTo(Collections.singletonMap(rawIssues.get(1), rawIssues.get(1)));
+ assertThat(tracking.getUnmatchedRaws()).containsOnly(rawIssues.get(2));
+ }
+
+ private DefaultIssue createIssue(int line, RuleKey ruleKey) {
+ DefaultIssue issue = new DefaultIssue()
+ .setRuleKey(ruleKey)
+ .setLine(line)
+ .setMessage("msg" + line);
+ return issue;
+ }
+
+ private Input<DefaultIssue> createInput(Collection<DefaultIssue> issues) {
+ return new Input<DefaultIssue>() {
+ @Override
+ public LineHashSequence getLineHashSequence() {
+ return LineHashSequence.createForLines(Arrays.asList("line1", "line2", "line3"));
+ }
+
+ @Override
+ public BlockHashSequence getBlockHashSequence() {
+ return BlockHashSequence.create(getLineHashSequence());
+ }
+
+ @Override
+ public Collection<DefaultIssue> getIssues() {
+ return issues;
+ }
+ };
+ }
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.sonar.api.rule.RuleKey;
-import org.sonar.ce.task.projectanalysis.component.Component;
-import org.sonar.core.issue.DefaultIssue;
-import org.sonar.core.issue.tracking.BlockHashSequence;
-import org.sonar.core.issue.tracking.Input;
-import org.sonar.core.issue.tracking.LineHashSequence;
-import org.sonar.core.issue.tracking.Tracker;
-import org.sonar.core.issue.tracking.Tracking;
-import org.sonar.db.rule.RuleTesting;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.when;
-import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
-
-public class ShortBranchTrackerExecutionTest {
- static final String FILE_UUID = "FILE_UUID";
- static final String FILE_KEY = "FILE_KEY";
- static final int FILE_REF = 2;
-
- static final Component FILE = builder(Component.Type.FILE, FILE_REF)
- .setKey(FILE_KEY)
- .setUuid(FILE_UUID)
- .build();
-
- @Mock
- private TrackerRawInputFactory rawFactory;
- @Mock
- private TrackerBaseInputFactory baseFactory;
- @Mock
- private TrackerMergeBranchInputFactory mergeFactory;
-
- private ShortBranchTrackerExecution underTest;
-
- private List<DefaultIssue> rawIssues = new ArrayList<>();
- private List<DefaultIssue> baseIssues = new ArrayList<>();
- private List<DefaultIssue> mergeBranchIssues = new ArrayList<>();
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
-
- when(rawFactory.create(FILE)).thenReturn(createInput(rawIssues));
- when(baseFactory.create(FILE)).thenReturn(createInput(baseIssues));
- when(mergeFactory.create(FILE)).thenReturn(createInput(mergeBranchIssues));
-
- Tracker<DefaultIssue, DefaultIssue> tracker = new Tracker<>();
- underTest = new ShortBranchTrackerExecution(baseFactory, rawFactory, mergeFactory, tracker);
- }
-
- @Test
- public void simple_tracking() {
- rawIssues.add(createIssue(1, RuleTesting.XOO_X1));
- Tracking<DefaultIssue, DefaultIssue> tracking = underTest.track(FILE);
- assertThat(tracking.getUnmatchedBases()).isEmpty();
- assertThat(tracking.getMatchedRaws()).isEmpty();
- assertThat(tracking.getUnmatchedRaws()).containsOnly(rawIssues.get(0));
- }
-
- @Test
- public void tracking_with_all_results() {
- rawIssues.add(createIssue(1, RuleTesting.XOO_X1));
- rawIssues.add(createIssue(2, RuleTesting.XOO_X2));
- rawIssues.add(createIssue(3, RuleTesting.XOO_X3));
-
- mergeBranchIssues.add(rawIssues.get(0));
-
- baseIssues.add(rawIssues.get(0));
- baseIssues.add(rawIssues.get(1));
-
- Tracking<DefaultIssue, DefaultIssue> tracking = underTest.track(FILE);
- assertThat(tracking.getMatchedRaws()).isEqualTo(Collections.singletonMap(rawIssues.get(1), rawIssues.get(1)));
- assertThat(tracking.getUnmatchedRaws()).containsOnly(rawIssues.get(2));
- }
-
- private DefaultIssue createIssue(int line, RuleKey ruleKey) {
- DefaultIssue issue = new DefaultIssue()
- .setRuleKey(ruleKey)
- .setLine(line)
- .setMessage("msg" + line);
- return issue;
- }
-
- private Input<DefaultIssue> createInput(Collection<DefaultIssue> issues) {
- return new Input<DefaultIssue>() {
- @Override
- public LineHashSequence getLineHashSequence() {
- return LineHashSequence.createForLines(Arrays.asList("line1", "line2", "line3"));
- }
-
- @Override
- public BlockHashSequence getBlockHashSequence() {
- return BlockHashSequence.create(getLineHashSequence());
- }
-
- @Override
- public Collection<DefaultIssue> getIssues() {
- return issues;
- }
- };
- }
-}