import org.sonar.ce.task.projectanalysis.issue.MovedIssueVisitor;
import org.sonar.ce.task.projectanalysis.issue.NewEffortAggregator;
import org.sonar.ce.task.projectanalysis.issue.ProtoIssueCache;
+import org.sonar.ce.task.projectanalysis.issue.PullRequestSourceBranchMerger;
import org.sonar.ce.task.projectanalysis.issue.PullRequestTrackerExecution;
import org.sonar.ce.task.projectanalysis.issue.ReferenceBranchTrackerExecution;
import org.sonar.ce.task.projectanalysis.issue.RemoveProcessedComponentsVisitor;
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.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.UpdateConflictResolver;
import org.sonar.ce.task.projectanalysis.issue.commonrule.BranchCoverageRule;
import org.sonar.ce.task.projectanalysis.issue.commonrule.CommentDensityRule;
TrackerBaseInputFactory.class,
TrackerRawInputFactory.class,
TrackerReferenceBranchInputFactory.class,
+ TrackerSourceBranchInputFactory.class,
+ SourceBranchComponentUuids.class,
ClosedIssuesInputFactory.class,
Tracker.class,
TrackerExecution.class,
PullRequestTrackerExecution.class,
+ PullRequestSourceBranchMerger.class,
ReferenceBranchTrackerExecution.class,
ComponentIssuesLoader.class,
BaseIssuesLoader.class,
import org.sonar.ce.task.projectanalysis.component.TypeAwareVisitorAdapter;
import org.sonar.ce.task.projectanalysis.util.cache.DiskCache.CacheAppender;
import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.tracking.Input;
import org.sonar.core.util.stream.MoreCollectors;
import static org.sonar.ce.task.projectanalysis.component.ComponentVisitor.Order.POST_ORDER;
public class IntegrateIssuesVisitor extends TypeAwareVisitorAdapter {
private final ProtoIssueCache protoIssueCache;
+ private final TrackerRawInputFactory rawInputFactory;
private final IssueLifecycle issueLifecycle;
private final IssueVisitors issueVisitors;
private final IssueTrackingDelegator issueTracking;
private final SiblingsIssueMerger issueStatusCopier;
private final ReferenceBranchComponentUuids referenceBranchComponentUuids;
+ private final PullRequestSourceBranchMerger pullRequestSourceBranchMerger;
- public IntegrateIssuesVisitor(ProtoIssueCache protoIssueCache, IssueLifecycle issueLifecycle, IssueVisitors issueVisitors, IssueTrackingDelegator issueTracking,
- SiblingsIssueMerger issueStatusCopier, ReferenceBranchComponentUuids referenceBranchComponentUuids) {
+ public IntegrateIssuesVisitor(
+ ProtoIssueCache protoIssueCache,
+ TrackerRawInputFactory rawInputFactory,
+ IssueLifecycle issueLifecycle,
+ IssueVisitors issueVisitors,
+ IssueTrackingDelegator issueTracking,
+ SiblingsIssueMerger issueStatusCopier,
+ ReferenceBranchComponentUuids referenceBranchComponentUuids,
+ PullRequestSourceBranchMerger pullRequestSourceBranchMerger) {
super(CrawlerDepthLimit.FILE, POST_ORDER);
this.protoIssueCache = protoIssueCache;
+ this.rawInputFactory = rawInputFactory;
this.issueLifecycle = issueLifecycle;
this.issueVisitors = issueVisitors;
this.issueTracking = issueTracking;
this.issueStatusCopier = issueStatusCopier;
this.referenceBranchComponentUuids = referenceBranchComponentUuids;
+ this.pullRequestSourceBranchMerger = pullRequestSourceBranchMerger;
}
@Override
public void visitAny(Component component) {
try (CacheAppender<DefaultIssue> cacheAppender = protoIssueCache.newAppender()) {
issueVisitors.beforeComponent(component);
- TrackingResult tracking = issueTracking.track(component);
- fillNewOpenIssues(component, tracking.newIssues(), cacheAppender);
+ Input<DefaultIssue> rawInput = rawInputFactory.create(component);
+ TrackingResult tracking = issueTracking.track(component, rawInput);
+ fillNewOpenIssues(component, tracking.newIssues(), rawInput, cacheAppender);
fillExistingOpenIssues(component, tracking.issuesToMerge(), cacheAppender);
closeIssues(component, tracking.issuesToClose(), cacheAppender);
copyIssues(component, tracking.issuesToCopy(), cacheAppender);
}
}
- private void fillNewOpenIssues(Component component, Stream<DefaultIssue> newIssues, CacheAppender<DefaultIssue> cacheAppender) {
+ private void fillNewOpenIssues(Component component, Stream<DefaultIssue> newIssues, Input<DefaultIssue> rawInput, CacheAppender<DefaultIssue> cacheAppender) {
List<DefaultIssue> newIssuesList = newIssues
.peek(issueLifecycle::initNewOpenIssue)
.collect(MoreCollectors.toList());
return;
}
+ pullRequestSourceBranchMerger.tryMergeIssuesFromSourceBranchOfPullRequest(component, newIssuesList, rawInput);
issueStatusCopier.tryMerge(component, newIssuesList);
for (DefaultIssue issue : newIssuesList) {
raw.setFieldChange(changeContext, IssueFieldsSetter.FROM_BRANCH, from, to);
}
- private void copyAttributesOfIssueFromAnotherBranch(DefaultIssue to, DefaultIssue from) {
+ public void copyExistingIssueFromSourceBranchToPullRequest(DefaultIssue raw, DefaultIssue base) {
+ Preconditions.checkState(analysisMetadataHolder.isPullRequest(), "This operation should be done only on pull request analysis");
+ copyAttributesOfIssueFromAnotherBranch(raw, base);
+ String from = analysisMetadataHolder.getBranch().getName();
+ String to = "#" + analysisMetadataHolder.getPullRequestKey();
+ raw.setFieldChange(changeContext, IssueFieldsSetter.FROM_BRANCH, from, to);
+ }
+
+ public void copyAttributesOfIssueFromAnotherBranch(DefaultIssue to, DefaultIssue from) {
to.setCopied(true);
copyFields(to, from);
if (from.manualSeverity()) {
import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
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.Tracking;
import static java.util.Collections.emptyMap;
this.analysisMetadataHolder = analysisMetadataHolder;
}
- public TrackingResult track(Component component) {
+ public TrackingResult track(Component component, Input<DefaultIssue> rawInput) {
if (analysisMetadataHolder.isPullRequest()) {
- return standardResult(pullRequestTracker.track(component));
+ return standardResult(pullRequestTracker.track(component, rawInput));
} else if (isFirstAnalysisSecondaryBranch()) {
- Tracking<DefaultIssue, DefaultIssue> tracking = referenceBranchTracker.track(component);
+ Tracking<DefaultIssue, DefaultIssue> tracking = referenceBranchTracker.track(component, rawInput);
return new TrackingResult(tracking.getMatchedRaws(), emptyMap(), empty(), tracking.getUnmatchedRaws());
} else {
- return standardResult(tracker.track(component));
+ return standardResult(tracker.track(component, rawInput));
}
}
--- /dev/null
+/*
+ * 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.Collection;
+import java.util.Map;
+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;
+
+public class PullRequestSourceBranchMerger {
+ private final Tracker<DefaultIssue, DefaultIssue> tracker;
+ private final IssueLifecycle issueLifecycle;
+ private final TrackerSourceBranchInputFactory sourceBranchInputFactory;
+
+ public PullRequestSourceBranchMerger(Tracker<DefaultIssue, DefaultIssue> tracker, IssueLifecycle issueLifecycle,
+ TrackerSourceBranchInputFactory sourceBranchInputFactory) {
+ this.tracker = tracker;
+ this.issueLifecycle = issueLifecycle;
+ this.sourceBranchInputFactory = sourceBranchInputFactory;
+ }
+
+ public void tryMergeIssuesFromSourceBranchOfPullRequest(Component component, Collection<DefaultIssue> newIssues, Input<DefaultIssue> rawInput) {
+ if (sourceBranchInputFactory.hasSourceBranchAnalysis()) {
+ Input<DefaultIssue> sourceBranchInput = sourceBranchInputFactory.createForSourceBranch(component);
+ DefaultTrackingInput rawPrTrackingInput = new DefaultTrackingInput(newIssues, rawInput.getLineHashSequence(), rawInput.getBlockHashSequence());
+ Tracking<DefaultIssue, DefaultIssue> prTracking = tracker.trackNonClosed(rawPrTrackingInput, sourceBranchInput);
+ for (Map.Entry<DefaultIssue, DefaultIssue> pair : prTracking.getMatchedRaws().entrySet()) {
+ issueLifecycle.copyExistingIssueFromSourceBranchToPullRequest(pair.getKey(), pair.getValue());
+ }
+ }
+ }
+}
public class PullRequestTrackerExecution {
private final TrackerBaseInputFactory baseInputFactory;
- private final TrackerRawInputFactory rawInputFactory;
private final Tracker<DefaultIssue, DefaultIssue> tracker;
private final NewLinesRepository newLinesRepository;
- public PullRequestTrackerExecution(TrackerBaseInputFactory baseInputFactory, TrackerRawInputFactory rawInputFactory, Tracker<DefaultIssue, DefaultIssue> tracker,
+ public PullRequestTrackerExecution(TrackerBaseInputFactory baseInputFactory, Tracker<DefaultIssue, DefaultIssue> tracker,
NewLinesRepository newLinesRepository) {
this.baseInputFactory = baseInputFactory;
- this.rawInputFactory = rawInputFactory;
this.tracker = tracker;
this.newLinesRepository = newLinesRepository;
}
- public Tracking<DefaultIssue, DefaultIssue> track(Component component) {
- Input<DefaultIssue> rawInput = rawInputFactory.create(component);
+ public Tracking<DefaultIssue, DefaultIssue> track(Component component, Input<DefaultIssue> rawInput) {
Input<DefaultIssue> previousAnalysisInput = baseInputFactory.create(component);
// Step 1: only keep issues on changed lines
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;
public class ReferenceBranchTrackerExecution {
- private final TrackerRawInputFactory rawInputFactory;
private final TrackerReferenceBranchInputFactory referenceBranchInputFactory;
private final Tracker<DefaultIssue, DefaultIssue> tracker;
- public ReferenceBranchTrackerExecution(TrackerRawInputFactory rawInputFactory, TrackerReferenceBranchInputFactory referenceBranchInputFactory,
+ public ReferenceBranchTrackerExecution(TrackerReferenceBranchInputFactory referenceBranchInputFactory,
Tracker<DefaultIssue, DefaultIssue> tracker) {
- this.rawInputFactory = rawInputFactory;
this.referenceBranchInputFactory = referenceBranchInputFactory;
this.tracker = tracker;
}
- public Tracking<DefaultIssue, DefaultIssue> track(Component component) {
- return tracker.trackNonClosed(rawInputFactory.create(component), referenceBranchInputFactory.create(component));
+ public Tracking<DefaultIssue, DefaultIssue> track(Component component, Input<DefaultIssue> rawInput) {
+ return tracker.trackNonClosed(rawInput, referenceBranchInputFactory.create(component));
}
}
--- /dev/null
+/*
+ * 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 source branch of a pull request
+ */
+public class SourceBranchComponentUuids {
+ private final AnalysisMetadataHolder analysisMetadataHolder;
+ private final DbClient dbClient;
+ private Map<String, String> sourceBranchComponentsUuidsByKey;
+ private boolean hasSourceBranchAnalysis;
+
+ public SourceBranchComponentUuids(AnalysisMetadataHolder analysisMetadataHolder, DbClient dbClient) {
+ this.analysisMetadataHolder = analysisMetadataHolder;
+ this.dbClient = dbClient;
+ }
+
+ private void lazyInit() {
+ if (sourceBranchComponentsUuidsByKey == null) {
+ sourceBranchComponentsUuidsByKey = new HashMap<>();
+
+ if (analysisMetadataHolder.isPullRequest()) {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ initForSourceBranch(dbSession);
+ }
+ } else {
+ hasSourceBranchAnalysis = false;
+ }
+ }
+ }
+
+ private void initForSourceBranch(DbSession dbSession) {
+ Optional<BranchDto> branchDtoOpt = dbClient.branchDao().selectByBranchKey(dbSession, analysisMetadataHolder.getProject().getUuid(),
+ analysisMetadataHolder.getBranch().getName());
+ String sourceBranchUuid = branchDtoOpt.map(BranchDto::getUuid).orElse(null);
+ hasSourceBranchAnalysis = sourceBranchUuid != null && dbClient.snapshotDao().selectLastAnalysisByRootComponentUuid(dbSession, sourceBranchUuid).isPresent();
+ if (hasSourceBranchAnalysis) {
+ List<ComponentDto> targetComponents = dbClient.componentDao().selectByProjectUuid(sourceBranchUuid, dbSession);
+ for (ComponentDto dto : targetComponents) {
+ sourceBranchComponentsUuidsByKey.put(dto.getKey(), dto.uuid());
+ }
+ }
+ }
+
+ public boolean hasSourceBranchAnalysis() {
+ lazyInit();
+ return hasSourceBranchAnalysis;
+ }
+
+ @CheckForNull
+ public String getSourceBranchComponentUuid(String dbKey) {
+ lazyInit();
+ String cleanComponentKey = removeBranchAndPullRequestFromKey(dbKey);
+ return sourceBranchComponentsUuidsByKey.get(cleanComponentKey);
+ }
+}
public class TrackerExecution {
private final TrackerBaseInputFactory baseInputFactory;
- private final TrackerRawInputFactory rawInputFactory;
private final ClosedIssuesInputFactory closedIssuesInputFactory;
private final Tracker<DefaultIssue, DefaultIssue> tracker;
private final ComponentIssuesLoader componentIssuesLoader;
private final AnalysisMetadataHolder analysisMetadataHolder;
- public TrackerExecution(TrackerBaseInputFactory baseInputFactory, TrackerRawInputFactory rawInputFactory,
+ public TrackerExecution(TrackerBaseInputFactory baseInputFactory,
ClosedIssuesInputFactory closedIssuesInputFactory, Tracker<DefaultIssue, DefaultIssue> tracker,
ComponentIssuesLoader componentIssuesLoader, AnalysisMetadataHolder analysisMetadataHolder) {
this.baseInputFactory = baseInputFactory;
- this.rawInputFactory = rawInputFactory;
this.closedIssuesInputFactory = closedIssuesInputFactory;
this.tracker = tracker;
this.componentIssuesLoader = componentIssuesLoader;
this.analysisMetadataHolder = analysisMetadataHolder;
}
- public Tracking<DefaultIssue, DefaultIssue> track(Component component) {
- Input<DefaultIssue> rawInput = rawInputFactory.create(component);
+ public Tracking<DefaultIssue, DefaultIssue> track(Component component, Input<DefaultIssue> rawInput) {
Input<DefaultIssue> openBaseIssuesInput = baseInputFactory.create(component);
NonClosedTracking<DefaultIssue, DefaultIssue> openIssueTracking = tracker.trackNonClosed(rawInput, openBaseIssuesInput);
if (openIssueTracking.isComplete() || analysisMetadataHolder.isFirstAnalysis()) {
import static org.sonar.api.issue.Issue.STATUS_TO_REVIEW;
public class TrackerRawInputFactory {
- private static final long DEFAULT_EXTERNAL_ISSUE_EFFORT = 0l;
+ private static final long DEFAULT_EXTERNAL_ISSUE_EFFORT = 0L;
private final TreeRootHolder treeRootHolder;
private final BatchReportReader reportReader;
private final CommonRuleEngine commonRuleEngine;
--- /dev/null
+/*
+ * 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 TrackerSourceBranchInputFactory {
+ private static final LineHashSequence EMPTY_LINE_HASH_SEQUENCE = new LineHashSequence(Collections.emptyList());
+
+ private final ComponentIssuesLoader componentIssuesLoader;
+ private final DbClient dbClient;
+ private final SourceBranchComponentUuids sourceBranchComponentUuids;
+
+ public TrackerSourceBranchInputFactory(ComponentIssuesLoader componentIssuesLoader, SourceBranchComponentUuids sourceBranchComponentUuids,
+ DbClient dbClient) {
+ this.componentIssuesLoader = componentIssuesLoader;
+ this.sourceBranchComponentUuids = sourceBranchComponentUuids;
+ this.dbClient = dbClient;
+ }
+
+ public boolean hasSourceBranchAnalysis() {
+ return sourceBranchComponentUuids.hasSourceBranchAnalysis();
+ }
+
+ public Input<DefaultIssue> createForSourceBranch(Component component) {
+ String sourceBranchComponentUuid = sourceBranchComponentUuids.getSourceBranchComponentUuid(component.getDbKey());
+ return new SourceBranchLazyInput(component.getType(), sourceBranchComponentUuid);
+ }
+
+ private class SourceBranchLazyInput extends LazyInput<DefaultIssue> {
+ private final Component.Type type;
+ private final String sourceBranchComponentUuid;
+
+ private SourceBranchLazyInput(Component.Type type, @Nullable String sourceBranchComponentUuid) {
+ this.type = type;
+ this.sourceBranchComponentUuid = sourceBranchComponentUuid;
+ }
+
+ @Override
+ protected LineHashSequence loadLineHashSequence() {
+ if (sourceBranchComponentUuid == null || type != Component.Type.FILE) {
+ return EMPTY_LINE_HASH_SEQUENCE;
+ }
+
+ try (DbSession session = dbClient.openSession(false)) {
+ List<String> hashes = dbClient.fileSourceDao().selectLineHashes(session, sourceBranchComponentUuid);
+ if (hashes == null || hashes.isEmpty()) {
+ return EMPTY_LINE_HASH_SEQUENCE;
+ }
+ return new LineHashSequence(hashes);
+ }
+ }
+
+ @Override
+ protected List<DefaultIssue> loadIssues() {
+ if (sourceBranchComponentUuid == null) {
+ return Collections.emptyList();
+ }
+ return componentIssuesLoader.loadOpenIssuesWithChanges(sourceBranchComponentUuid);
+ }
+ }
+
+}
import org.sonar.server.issue.workflow.IssueWorkflow;
import static com.google.common.collect.Lists.newArrayList;
-import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
@Rule
public SourceLinesRepositoryRule fileSourceRepository = new SourceLinesRepositoryRule();
- private AnalysisMetadataHolder analysisMetadataHolder = mock(AnalysisMetadataHolder.class);
- private IssueFilter issueFilter = mock(IssueFilter.class);
- private MovedFilesRepository movedFilesRepository = mock(MovedFilesRepository.class);
- private IssueChangeContext issueChangeContext = mock(IssueChangeContext.class);
- private IssueLifecycle issueLifecycle = new IssueLifecycle(analysisMetadataHolder, issueChangeContext, mock(IssueWorkflow.class), new IssueFieldsSetter(),
+ private final AnalysisMetadataHolder analysisMetadataHolder = mock(AnalysisMetadataHolder.class);
+ private final IssueFilter issueFilter = mock(IssueFilter.class);
+ private final MovedFilesRepository movedFilesRepository = mock(MovedFilesRepository.class);
+ private final IssueChangeContext issueChangeContext = mock(IssueChangeContext.class);
+ private final IssueLifecycle issueLifecycle = new IssueLifecycle(analysisMetadataHolder, issueChangeContext, mock(IssueWorkflow.class), new IssueFieldsSetter(),
mock(DebtCalculator.class), ruleRepositoryRule);
- private IssueVisitor issueVisitor = mock(IssueVisitor.class);
- private ReferenceBranchComponentUuids mergeBranchComponentsUuids = mock(ReferenceBranchComponentUuids.class);
- private SiblingsIssueMerger issueStatusCopier = mock(SiblingsIssueMerger.class);
- private ReferenceBranchComponentUuids referenceBranchComponentUuids = mock(ReferenceBranchComponentUuids.class);
- private SourceLinesHashRepository sourceLinesHash = mock(SourceLinesHashRepository.class);
- private NewLinesRepository newLinesRepository = mock(NewLinesRepository.class);
+ private final IssueVisitor issueVisitor = mock(IssueVisitor.class);
+ private final ReferenceBranchComponentUuids mergeBranchComponentsUuids = mock(ReferenceBranchComponentUuids.class);
+ private final SiblingsIssueMerger issueStatusCopier = mock(SiblingsIssueMerger.class);
+ private final ReferenceBranchComponentUuids referenceBranchComponentUuids = mock(ReferenceBranchComponentUuids.class);
+ private final SourceLinesHashRepository sourceLinesHash = mock(SourceLinesHashRepository.class);
+ private final NewLinesRepository newLinesRepository = mock(NewLinesRepository.class);
private ArgumentCaptor<DefaultIssue> defaultIssueCaptor;
- private ComponentIssuesLoader issuesLoader = new ComponentIssuesLoader(dbTester.getDbClient(), ruleRepositoryRule, activeRulesHolderRule, new MapSettings().asConfig(),
+ private final ComponentIssuesLoader issuesLoader = new ComponentIssuesLoader(dbTester.getDbClient(), ruleRepositoryRule, activeRulesHolderRule, new MapSettings().asConfig(),
System2.INSTANCE);
private IssueTrackingDelegator trackingDelegator;
private TrackerExecution tracker;
private PullRequestTrackerExecution prBranchTracker;
private ReferenceBranchTrackerExecution mergeBranchTracker;
- private ActiveRulesHolder activeRulesHolder = new AlwaysActiveRulesHolderImpl();
+ private final ActiveRulesHolder activeRulesHolder = new AlwaysActiveRulesHolderImpl();
private ProtoIssueCache protoIssueCache;
private TypeAwareVisitor underTest;
new IssueFieldsSetter(), mock(ComponentsWithUnprocessedIssues.class));
TrackerReferenceBranchInputFactory mergeInputFactory = new TrackerReferenceBranchInputFactory(issuesLoader, mergeBranchComponentsUuids, dbClient);
ClosedIssuesInputFactory closedIssuesInputFactory = new ClosedIssuesInputFactory(issuesLoader, dbClient, movedFilesRepository);
- tracker = new TrackerExecution(baseInputFactory, rawInputFactory, closedIssuesInputFactory, new Tracker<>(), issuesLoader, analysisMetadataHolder);
- prBranchTracker = new PullRequestTrackerExecution(baseInputFactory, rawInputFactory, new Tracker<>(), newLinesRepository);
- mergeBranchTracker = new ReferenceBranchTrackerExecution(rawInputFactory, mergeInputFactory, new Tracker<>());
+ tracker = new TrackerExecution(baseInputFactory, closedIssuesInputFactory, new Tracker<>(), issuesLoader, analysisMetadataHolder);
+ prBranchTracker = new PullRequestTrackerExecution(baseInputFactory, new Tracker<>(), newLinesRepository);
+ mergeBranchTracker = new ReferenceBranchTrackerExecution(mergeInputFactory, new Tracker<>());
trackingDelegator = new IssueTrackingDelegator(prBranchTracker, mergeBranchTracker, tracker, analysisMetadataHolder);
treeRootHolder.setRoot(PROJECT);
protoIssueCache = new ProtoIssueCache(temp.newFile(), System2.INSTANCE);
when(issueFilter.accept(any(DefaultIssue.class), eq(FILE))).thenReturn(true);
when(issueChangeContext.date()).thenReturn(new Date());
- underTest = new IntegrateIssuesVisitor(protoIssueCache, issueLifecycle, issueVisitors, trackingDelegator, issueStatusCopier, referenceBranchComponentUuids);
+ underTest = new IntegrateIssuesVisitor(protoIssueCache, rawInputFactory, issueLifecycle, issueVisitors, trackingDelegator, issueStatusCopier, referenceBranchComponentUuids,
+ mock(PullRequestSourceBranchMerger.class));
}
@Test
.setRuleKey("S001")
.setSeverity(Constants.Severity.BLOCKER)
.build();
- reportReader.putIssues(FILE_REF, asList(reportIssue));
+ reportReader.putIssues(FILE_REF, singletonList(reportIssue));
fileSourceRepository.addLine(FILE_REF, "line1");
underTest.visitAny(FILE);
.setRuleKey(ruleKey.rule())
.setSeverity(Constants.Severity.BLOCKER)
.build();
- reportReader.putIssues(FILE_REF, asList(reportIssue));
+ reportReader.putIssues(FILE_REF, singletonList(reportIssue));
fileSourceRepository.addLine(FILE_REF, "line1");
underTest.visitAny(FILE);
.setRuleKey(ruleKey.rule())
.setSeverity(Constants.Severity.BLOCKER)
.build();
- reportReader.putIssues(FILE_REF, asList(reportIssue));
+ reportReader.putIssues(FILE_REF, singletonList(reportIssue));
fileSourceRepository.addLine(FILE_REF, "line1");
underTest.visitAny(FILE);
.setRuleKey("S001")
.setSeverity(Constants.Severity.BLOCKER)
.build();
- reportReader.putIssues(FILE_REF, asList(reportIssue));
+ reportReader.putIssues(FILE_REF, singletonList(reportIssue));
fileSourceRepository.addLine(FILE_REF, "line1");
underTest.visitAny(FILE);
assertThat(issues.get(0).isNew()).isFalse();
assertThat(issues.get(0).isCopied()).isTrue();
assertThat(issues.get(0).changes()).hasSize(1);
- assertThat(issues.get(0).changes().get(0).diffs()).contains(entry(IssueFieldsSetter.FROM_BRANCH, new FieldDiffs.Diff("master", null)));
+ assertThat(issues.get(0).changes().get(0).diffs()).contains(entry(IssueFieldsSetter.FROM_BRANCH, new FieldDiffs.Diff<>("master", null)));
}
private void addBaseIssue(RuleKey ruleKey) {
import org.sonar.core.issue.DefaultIssueComment;
import org.sonar.core.issue.FieldDiffs;
import org.sonar.core.issue.IssueChangeContext;
+import org.sonar.db.component.BranchType;
import org.sonar.db.protobuf.DbCommons;
import org.sonar.db.protobuf.DbIssues;
import org.sonar.server.issue.IssueFieldsSetter;
import static com.google.common.collect.Lists.newArrayList;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.entry;
import static org.assertj.core.groups.Tuple.tuple;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import static org.sonar.api.issue.Issue.RESOLUTION_FALSE_POSITIVE;
import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
private static final Date DEFAULT_DATE = new Date();
private static final Duration DEFAULT_DURATION = Duration.create(10);
- private DumbRule rule = new DumbRule(XOO_X1);
+ private final DumbRule rule = new DumbRule(XOO_X1);
@Rule
public RuleRepositoryRule ruleRepository = new RuleRepositoryRule().add(rule);
@Rule
public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule();
- private IssueChangeContext issueChangeContext = IssueChangeContext.createUser(DEFAULT_DATE, "default_user_uuid");
- private IssueWorkflow workflow = mock(IssueWorkflow.class);
- private IssueFieldsSetter updater = mock(IssueFieldsSetter.class);
- private DebtCalculator debtCalculator = mock(DebtCalculator.class);
- private IssueLifecycle underTest = new IssueLifecycle(analysisMetadataHolder, issueChangeContext, workflow, updater, debtCalculator, ruleRepository);
+ private final IssueChangeContext issueChangeContext = IssueChangeContext.createUser(DEFAULT_DATE, "default_user_uuid");
+ private final IssueWorkflow workflow = mock(IssueWorkflow.class);
+ private final IssueFieldsSetter updater = mock(IssueFieldsSetter.class);
+ private final DebtCalculator debtCalculator = mock(DebtCalculator.class);
+ private final IssueLifecycle underTest = new IssueLifecycle(analysisMetadataHolder, issueChangeContext, workflow, updater, debtCalculator, ruleRepository);
@Test
public void initNewOpenIssue() {
assertThat(raw.changes().get(1).get(IssueFieldsSetter.FROM_BRANCH).newValue()).isEqualTo("master");
}
+ @Test
+ public void copyExistingIssuesFromSourceBranchOfPullRequest() {
+ String pullRequestKey = "1";
+ Branch branch = mock(Branch.class);
+ when(branch.getType()).thenReturn(BranchType.PULL_REQUEST);
+ when(branch.getName()).thenReturn("sourceBranch-1");
+ when(branch.getPullRequestKey()).thenReturn(pullRequestKey);
+ analysisMetadataHolder.setBranch(branch);
+ analysisMetadataHolder.setPullRequestKey(pullRequestKey);
+ DefaultIssue raw = new DefaultIssue()
+ .setKey("raw");
+ DefaultIssue fromShort = new DefaultIssue()
+ .setKey("short");
+ fromShort.setResolution("resolution");
+ fromShort.setStatus("status");
+
+ Date commentDate = new Date();
+ fromShort.addComment(new DefaultIssueComment()
+ .setIssueKey("short")
+ .setCreatedAt(commentDate)
+ .setUserUuid("user_uuid")
+ .setMarkdownText("A comment"));
+
+ Date diffDate = new Date();
+ // file diff alone
+ fromShort.addChange(new FieldDiffs()
+ .setCreationDate(diffDate)
+ .setIssueKey("short")
+ .setUserUuid("user_uuid")
+ .setDiff("file", "uuidA1", "uuidB1"));
+ // file diff with another field
+ fromShort.addChange(new FieldDiffs()
+ .setCreationDate(diffDate)
+ .setIssueKey("short")
+ .setUserUuid("user_uuid")
+ .setDiff("severity", "MINOR", "MAJOR")
+ .setDiff("file", "uuidA2", "uuidB2"));
+
+ underTest.copyExistingIssueFromSourceBranchToPullRequest(raw, fromShort);
+
+ assertThat(raw.resolution()).isEqualTo("resolution");
+ assertThat(raw.status()).isEqualTo("status");
+ assertThat(raw.defaultIssueComments())
+ .extracting(DefaultIssueComment::issueKey, DefaultIssueComment::createdAt, DefaultIssueComment::userUuid, DefaultIssueComment::markdownText)
+ .containsOnly(tuple("raw", commentDate, "user_uuid", "A comment"));
+ assertThat(raw.changes()).hasSize(2);
+ assertThat(raw.changes().get(0).creationDate()).isEqualTo(diffDate);
+ assertThat(raw.changes().get(0).userUuid()).isEqualTo("user_uuid");
+ assertThat(raw.changes().get(0).issueKey()).isEqualTo("raw");
+ assertThat(raw.changes().get(0).diffs()).containsOnlyKeys("severity");
+ assertThat(raw.changes().get(1).userUuid()).isEqualTo("default_user_uuid");
+ assertThat(raw.changes().get(1).diffs()).containsOnlyKeys(IssueFieldsSetter.FROM_BRANCH);
+ assertThat(raw.changes().get(1).get(IssueFieldsSetter.FROM_BRANCH).oldValue()).isEqualTo("sourceBranch-1");
+ assertThat(raw.changes().get(1).get(IssueFieldsSetter.FROM_BRANCH).newValue()).isEqualTo("#1");
+ }
+
+ @Test
+ public void copyExistingIssuesFromSourceBranchOfPullRequest_only_works_for_pull_requests() {
+ DefaultIssue raw = new DefaultIssue()
+ .setKey("raw");
+ DefaultIssue from = new DefaultIssue()
+ .setKey("short");
+ from.setResolution("resolution");
+ from.setStatus("status");
+ Branch branch = mock(Branch.class);
+ when(branch.getType()).thenReturn(BranchType.BRANCH);
+ analysisMetadataHolder.setBranch(branch);
+
+ assertThatThrownBy(() -> underTest.copyExistingIssueFromSourceBranchToPullRequest(raw, from))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage("This operation should be done only on pull request analysis");
+ }
+
@Test
public void copiedIssue() {
DefaultIssue raw = new DefaultIssue()
assertThat(raw.changes().get(0).get(IssueFieldsSetter.FROM_BRANCH).oldValue()).isEqualTo("master");
assertThat(raw.changes().get(0).get(IssueFieldsSetter.FROM_BRANCH).newValue()).isEqualTo("release-2.x");
- verifyZeroInteractions(updater);
+ verifyNoInteractions(updater);
}
@Test
assertThat(raw.isChanged()).isFalse();
assertThat(raw.changes()).hasSize(2);
assertThat(raw.changes().get(0).diffs())
- .containsOnly(entry("foo", new FieldDiffs.Diff("bar", "donut")));
+ .containsOnly(entry("foo", new FieldDiffs.Diff<>("bar", "donut")));
assertThat(raw.changes().get(1).diffs())
- .containsOnly(entry("file", new FieldDiffs.Diff("A", "B")));
+ .containsOnly(entry("file", new FieldDiffs.Diff<>("A", "B")));
verify(updater).setPastSeverity(raw, BLOCKER, issueChangeContext);
verify(updater).setPastLine(raw, 10);
import org.sonar.ce.task.projectanalysis.analysis.Branch;
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.Tracking;
import org.sonar.db.component.BranchType;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
public class IssueTrackingDelegatorTest {
private Component component;
@Mock
private Tracking<DefaultIssue, DefaultIssue> trackingResult;
+ @Mock
+ private Input<DefaultIssue> rawInput;
private IssueTrackingDelegator underTest;
public void setUp() {
MockitoAnnotations.initMocks(this);
underTest = new IssueTrackingDelegator(prBranchTracker, mergeBranchTracker, tracker, analysisMetadataHolder);
- when(tracker.track(component)).thenReturn(trackingResult);
- when(mergeBranchTracker.track(component)).thenReturn(trackingResult);
- when(prBranchTracker.track(component)).thenReturn(trackingResult);
+ when(tracker.track(component, rawInput)).thenReturn(trackingResult);
+ when(mergeBranchTracker.track(component, rawInput)).thenReturn(trackingResult);
+ when(prBranchTracker.track(component, rawInput)).thenReturn(trackingResult);
}
@Test
public void delegate_regular_tracker() {
when(analysisMetadataHolder.getBranch()).thenReturn(mock(Branch.class));
- underTest.track(component);
+ underTest.track(component, rawInput);
- verify(tracker).track(component);
- verifyZeroInteractions(prBranchTracker);
- verifyZeroInteractions(mergeBranchTracker);
+ verify(tracker).track(component, rawInput);
+ verifyNoInteractions(prBranchTracker);
+ verifyNoInteractions(mergeBranchTracker);
}
@Test
when(analysisMetadataHolder.getBranch()).thenReturn(branch);
when(analysisMetadataHolder.isFirstAnalysis()).thenReturn(true);
- underTest.track(component);
+ underTest.track(component, rawInput);
- verify(mergeBranchTracker).track(component);
- verifyZeroInteractions(tracker);
- verifyZeroInteractions(prBranchTracker);
+ verify(mergeBranchTracker).track(component, rawInput);
+ verifyNoInteractions(tracker);
+ verifyNoInteractions(prBranchTracker);
}
when(analysisMetadataHolder.getBranch()).thenReturn(mock(Branch.class));
when(analysisMetadataHolder.isPullRequest()).thenReturn(true);
- underTest.track(component);
+ underTest.track(component, rawInput);
- verify(prBranchTracker).track(component);
- verifyZeroInteractions(tracker);
- verifyZeroInteractions(mergeBranchTracker);
+ verify(prBranchTracker).track(component, rawInput);
+ verifyNoInteractions(tracker);
+ verifyNoInteractions(mergeBranchTracker);
}
}
--- /dev/null
+/*
+ * 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.Date;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.rule.RuleKey;
+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.NonClosedTracking;
+import org.sonar.core.issue.tracking.Tracker;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.BranchType;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.rule.RuleDefinitionDto;
+
+import static java.util.Collections.singletonList;
+import static java.util.Collections.singletonMap;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.when;
+import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
+import static org.sonar.db.component.ComponentTesting.newFileDto;
+
+public class PullRequestSourceBranchMergerTest {
+ private static final String PROJECT_KEY = "project";
+ private static final String PROJECT_UUID = "projectUuid";
+ private static final int FILE_1_REF = 12341;
+ private static final String FILE_1_KEY = "fileKey";
+ private static final String FILE_1_UUID = "fileUuid";
+ private static final org.sonar.ce.task.projectanalysis.component.Component FILE_1 = builder(
+ org.sonar.ce.task.projectanalysis.component.Component.Type.FILE, FILE_1_REF)
+ .setKey(FILE_1_KEY)
+ .setUuid(FILE_1_UUID)
+ .build();
+
+ @Mock
+ private Tracker<DefaultIssue, DefaultIssue> tracker;
+
+ @Mock
+ private IssueLifecycle issueLifecycle;
+
+ @Mock
+ private TrackerSourceBranchInputFactory sourceBranchInputFactory;
+
+ @Mock
+ private NonClosedTracking<DefaultIssue, DefaultIssue> prTracking;
+
+ @Rule
+ public DbTester db = DbTester.create();
+
+ private PullRequestSourceBranchMerger underTest;
+ private RuleDefinitionDto rule;
+ private DefaultIssue rawIssue;
+ private Input<DefaultIssue> rawIssuesInput;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ underTest = new PullRequestSourceBranchMerger(
+ tracker,
+ issueLifecycle,
+ sourceBranchInputFactory);
+
+ ComponentDto projectDto = db.components().insertPublicProject(p -> p.setDbKey(PROJECT_KEY).setUuid(PROJECT_UUID));
+ ComponentDto branch1Dto = db.components().insertProjectBranch(projectDto, b -> b.setKey("myBranch1")
+ .setBranchType(BranchType.PULL_REQUEST)
+ .setMergeBranchUuid(projectDto.uuid()));
+ ComponentDto branch2Dto = db.components().insertProjectBranch(projectDto, b -> b.setKey("myBranch2")
+ .setBranchType(BranchType.PULL_REQUEST)
+ .setMergeBranchUuid(projectDto.uuid()));
+ ComponentDto branch3Dto = db.components().insertProjectBranch(projectDto, b -> b.setKey("myBranch3")
+ .setBranchType(BranchType.PULL_REQUEST)
+ .setMergeBranchUuid(projectDto.uuid()));
+ db.components().insertComponent(newFileDto(branch1Dto).setDbKey(FILE_1_KEY + ":PULL_REQUEST:myBranch1"));
+ db.components().insertComponent(newFileDto(branch2Dto).setDbKey(FILE_1_KEY + ":PULL_REQUEST:myBranch2"));
+ db.components().insertComponent(newFileDto(branch3Dto).setDbKey(FILE_1_KEY + ":PULL_REQUEST:myBranch3"));
+ rule = db.rules().insert();
+ rawIssue = createIssue("issue1", rule.getKey(), Issue.STATUS_OPEN, new Date());
+ rawIssuesInput = new DefaultTrackingInput(singletonList(rawIssue), mock(LineHashSequence.class), mock(BlockHashSequence.class));
+ }
+
+ @Test
+ public void tryMergeIssuesFromSourceBranchOfPullRequest_does_nothing_if_source_branch_was_not_analyzed() {
+ when(sourceBranchInputFactory.hasSourceBranchAnalysis()).thenReturn(false);
+
+ underTest.tryMergeIssuesFromSourceBranchOfPullRequest(FILE_1, rawIssuesInput.getIssues(), rawIssuesInput);
+
+ verifyNoInteractions(issueLifecycle);
+ }
+
+ @Test
+ public void tryMergeIssuesFromSourceBranchOfPullRequest_merges_issue_state_from_source_branch_into_pull_request() {
+ DefaultIssue sourceBranchIssue = createIssue("issue2", rule.getKey(), Issue.STATUS_CONFIRMED, new Date());
+ Input<DefaultIssue> sourceBranchInput = new DefaultTrackingInput(singletonList(sourceBranchIssue), mock(LineHashSequence.class), mock(BlockHashSequence.class));
+ when(sourceBranchInputFactory.hasSourceBranchAnalysis()).thenReturn(true);
+ when(sourceBranchInputFactory.createForSourceBranch(any())).thenReturn(sourceBranchInput);
+ when(tracker.trackNonClosed(any(), any())).thenReturn(prTracking);
+ when(prTracking.getMatchedRaws()).thenReturn(singletonMap(rawIssue, sourceBranchIssue));
+
+ underTest.tryMergeIssuesFromSourceBranchOfPullRequest(FILE_1, rawIssuesInput.getIssues(), rawIssuesInput);
+
+ verify(issueLifecycle).copyExistingIssueFromSourceBranchToPullRequest(rawIssue, sourceBranchIssue);
+ }
+
+ private static DefaultIssue createIssue(String key, RuleKey ruleKey, String status, Date creationDate) {
+ DefaultIssue issue = new DefaultIssue();
+ issue.setKey(key);
+ issue.setRuleKey(ruleKey);
+ issue.setMessage("msg");
+ issue.setLine(1);
+ issue.setStatus(status);
+ issue.setResolution(null);
+ issue.setCreationDate(creationDate);
+ issue.setChecksum("checksum");
+ return issue;
+ }
+}
.setUuid(FILE_UUID)
.build();
- private TrackerRawInputFactory rawFactory = mock(TrackerRawInputFactory.class);
- private TrackerBaseInputFactory baseFactory = mock(TrackerBaseInputFactory.class);
- private NewLinesRepository newLinesRepository = mock(NewLinesRepository.class);
+ private final TrackerBaseInputFactory baseFactory = mock(TrackerBaseInputFactory.class);
+ private final NewLinesRepository newLinesRepository = mock(NewLinesRepository.class);
+ private final List<DefaultIssue> rawIssues = new ArrayList<>();
+ private final List<DefaultIssue> baseIssues = new ArrayList<>();
private PullRequestTrackerExecution underTest;
- private List<DefaultIssue> rawIssues = new ArrayList<>();
- private List<DefaultIssue> baseIssues = new ArrayList<>();
-
@Before
public void setUp() {
- when(rawFactory.create(FILE)).thenReturn(createInput(rawIssues));
when(baseFactory.create(FILE)).thenReturn(createInput(baseIssues));
Tracker<DefaultIssue, DefaultIssue> tracker = new Tracker<>();
- underTest = new PullRequestTrackerExecution(baseFactory, rawFactory, tracker, newLinesRepository);
+ underTest = new PullRequestTrackerExecution(baseFactory, tracker, newLinesRepository);
}
@Test
when(newLinesRepository.getNewLines(FILE)).thenReturn(Optional.of(new HashSet<>(Arrays.asList(1, 3))));
- Tracking<DefaultIssue, DefaultIssue> tracking = underTest.track(FILE);
+ Tracking<DefaultIssue, DefaultIssue> tracking = underTest.track(FILE, createInput(rawIssues));
assertThat(tracking.getUnmatchedBases()).isEmpty();
assertThat(tracking.getMatchedRaws()).isEmpty();
when(newLinesRepository.getNewLines(FILE)).thenReturn(Optional.of(new HashSet<>(Arrays.asList(7, 10))));
- Tracking<DefaultIssue, DefaultIssue> tracking = underTest.track(FILE);
+ Tracking<DefaultIssue, DefaultIssue> tracking = underTest.track(FILE, createInput(rawIssues));
assertThat(tracking.getUnmatchedBases()).isEmpty();
assertThat(tracking.getMatchedRaws()).isEmpty();
baseIssues.add(rawIssues.get(0));
- Tracking<DefaultIssue, DefaultIssue> tracking = underTest.track(FILE);
+ Tracking<DefaultIssue, DefaultIssue> tracking = underTest.track(FILE, createInput(rawIssues));
assertThat(tracking.getMatchedRaws()).isEqualTo(Collections.singletonMap(rawIssues.get(0), rawIssues.get(0)));
assertThat(tracking.getUnmatchedRaws()).containsOnly(rawIssues.get(2));
}
import static org.mockito.Mockito.when;
public class ReferenceBranchTrackerExecutionTest {
- @Mock
- private TrackerRawInputFactory rawInputFactory;
@Mock
private TrackerReferenceBranchInputFactory mergeInputFactory;
@Mock
@Before
public void before() {
MockitoAnnotations.initMocks(this);
- underTest = new ReferenceBranchTrackerExecution(rawInputFactory, mergeInputFactory, tracker);
+ underTest = new ReferenceBranchTrackerExecution(mergeInputFactory, tracker);
}
@Test
Input<DefaultIssue> rawInput = mock(Input.class);
Input<DefaultIssue> mergeInput = mock(Input.class);
NonClosedTracking<DefaultIssue, DefaultIssue> result = mock(NonClosedTracking.class);
- when(rawInputFactory.create(component)).thenReturn(rawInput);
when(mergeInputFactory.create(component)).thenReturn(mergeInput);
when(tracker.trackNonClosed(rawInput, mergeInput)).thenReturn(result);
- assertThat(underTest.track(component)).isEqualTo(result);
+ assertThat(underTest.track(component, rawInput)).isEqualTo(result);
}
}
import java.time.temporal.ChronoUnit;
import java.util.Collections;
import java.util.Date;
-import javax.annotation.Nullable;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
import static org.sonar.db.component.ComponentTesting.newFileDto;
private static final org.sonar.ce.task.projectanalysis.component.Component FILE_1 = builder(
org.sonar.ce.task.projectanalysis.component.Component.Type.FILE, FILE_1_REF)
- .setKey(FILE_1_KEY)
- .setUuid(FILE_1_UUID)
- .build();
+ .setKey(FILE_1_KEY)
+ .setUuid(FILE_1_UUID)
+ .build();
- private SimpleTracker<DefaultIssue, SiblingIssue> tracker = new SimpleTracker<>();
+ private final SimpleTracker<DefaultIssue, SiblingIssue> tracker = new SimpleTracker<>();
private SiblingsIssueMerger copier;
private ComponentDto fileOnBranch1Dto;
private ComponentDto fileOnBranch2Dto;
@Test
public void do_nothing_if_no_match() {
- DefaultIssue i = createIssue("issue1", rule.getKey(), Issue.STATUS_CONFIRMED, null, new Date());
+ DefaultIssue i = createIssue("issue1", rule.getKey(), Issue.STATUS_CONFIRMED, new Date());
copier.tryMerge(FILE_1, Collections.singleton(i));
- verifyZeroInteractions(issueLifecycle);
+ verifyNoInteractions(issueLifecycle);
}
@Test
db.issues().insert(IssueTesting.newIssue(rule, branch1Dto, fileOnBranch1Dto).setKee("issue1").setStatus(Issue.STATUS_CONFIRMED).setLine(1).setChecksum("checksum"));
copier.tryMerge(FILE_1, Collections.emptyList());
- verifyZeroInteractions(issueLifecycle);
+ verifyNoInteractions(issueLifecycle);
}
@Test
public void merge_confirmed_issues() {
db.issues().insert(IssueTesting.newIssue(rule, branch1Dto, fileOnBranch1Dto).setKee("issue1").setStatus(Issue.STATUS_CONFIRMED).setLine(1).setChecksum("checksum"));
- DefaultIssue newIssue = createIssue("issue2", rule.getKey(), Issue.STATUS_OPEN, null, new Date());
+ DefaultIssue newIssue = createIssue("issue2", rule.getKey(), Issue.STATUS_OPEN, new Date());
copier.tryMerge(FILE_1, Collections.singleton(newIssue));
.setIssueUpdateDate(Date.from(now.plus(1, ChronoUnit.SECONDS))));
db.issues().insert(IssueTesting.newIssue(rule, branch3Dto, fileOnBranch3Dto).setKee("issue3").setStatus(Issue.STATUS_OPEN).setLine(1).setChecksum("checksum")
.setIssueUpdateDate(Date.from(now)));
- DefaultIssue newIssue = createIssue("newIssue", rule.getKey(), Issue.STATUS_OPEN, null, new Date());
+ DefaultIssue newIssue = createIssue("newIssue", rule.getKey(), Issue.STATUS_OPEN, new Date());
copier.tryMerge(FILE_1, Collections.singleton(newIssue));
.insert(IssueTesting.newIssue(rule, branch2Dto, fileOnBranch2Dto).setKee("issue").setStatus(Issue.STATUS_CONFIRMED).setLine(1).setChecksum("checksum"));
db.issues().insertComment(issue, user, "A comment 2");
db.issues().insertFieldDiffs(issue, FieldDiffs.parse("severity=BLOCKER|MINOR,assignee=foo|bar").setCreationDate(new Date()));
- DefaultIssue newIssue = createIssue("newIssue", rule.getKey(), Issue.STATUS_OPEN, null, new Date());
+ DefaultIssue newIssue = createIssue("newIssue", rule.getKey(), Issue.STATUS_OPEN, new Date());
copier.tryMerge(FILE_1, Collections.singleton(newIssue));
assertThat(issueToMerge.getValue().changes()).isNotEmpty();
}
- private static DefaultIssue createIssue(String key, RuleKey ruleKey, String status, @Nullable String resolution, Date creationDate) {
+ private static DefaultIssue createIssue(String key, RuleKey ruleKey, String status, Date creationDate) {
DefaultIssue issue = new DefaultIssue();
issue.setKey(key);
issue.setRuleKey(ruleKey);
issue.setMessage("msg");
issue.setLine(1);
issue.setStatus(status);
- issue.setResolution(resolution);
+ issue.setResolution(null);
issue.setCreationDate(creationDate);
issue.setChecksum("checksum");
return issue;
--- /dev/null
+/*
+ * 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 SourceBranchComponentUuidsTest {
+
+ private static final String BRANCH_KEY = "branch1";
+ private static final String PR_KEY = "pr1";
+
+ @Rule
+ public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule();
+
+ @Rule
+ public DbTester db = DbTester.create();
+
+ private SourceBranchComponentUuids underTest;
+ private final Branch branch = mock(Branch.class);
+ private ComponentDto branch1;
+ private ComponentDto branch1File;
+ private ComponentDto pr1File;
+
+ @Before
+ public void setup() {
+ underTest = new SourceBranchComponentUuids(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().setBranch(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_source_branch_component() {
+ when(branch.getType()).thenReturn(BranchType.PULL_REQUEST);
+ when(branch.getName()).thenReturn(BRANCH_KEY);
+ when(branch.getPullRequestKey()).thenReturn(PR_KEY);
+ db.components().insertSnapshot(newAnalysis(branch1));
+
+ assertThat(underTest.getSourceBranchComponentUuid(pr1File.getDbKey())).isEqualTo(branch1File.uuid());
+ assertThat(underTest.hasSourceBranchAnalysis()).isTrue();
+ }
+
+ @Test
+ public void should_support_key_when_looking_for_source_branch_component() {
+ when(branch.getType()).thenReturn(BranchType.PULL_REQUEST);
+ when(branch.getName()).thenReturn(BRANCH_KEY);
+ when(branch.getPullRequestKey()).thenReturn(PR_KEY);
+ db.components().insertSnapshot(newAnalysis(branch1));
+
+ assertThat(underTest.getSourceBranchComponentUuid(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(BRANCH_KEY);
+ when(branch.getPullRequestKey()).thenReturn(PR_KEY);
+ db.components().insertSnapshot(newAnalysis(branch1));
+
+ assertThat(underTest.getSourceBranchComponentUuid("doesnt exist")).isNull();
+ }
+
+ @Test
+ public void skip_init_if_not_a_pull_request() {
+ when(branch.getType()).thenReturn(BranchType.BRANCH);
+ when(branch.getName()).thenReturn(BRANCH_KEY);
+
+ assertThat(underTest.getSourceBranchComponentUuid(pr1File.getDbKey())).isNull();
+ }
+
+ @Test
+ public void skip_init_if_no_source_branch_analysis() {
+ when(branch.getType()).thenReturn(BranchType.PULL_REQUEST);
+ when(branch.getName()).thenReturn(BRANCH_KEY);
+
+ assertThat(underTest.getSourceBranchComponentUuid(pr1File.getDbKey())).isNull();
+ }
+}
import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
public class TrackerExecutionTest {
- private final TrackerRawInputFactory rawInputFactory = mock(TrackerRawInputFactory.class);
private final TrackerBaseInputFactory baseInputFactory = mock(TrackerBaseInputFactory.class);
private final ClosedIssuesInputFactory closedIssuesInputFactory = mock(ClosedIssuesInputFactory.class);
private final Tracker<DefaultIssue, DefaultIssue> tracker = mock(Tracker.class);
private final ComponentIssuesLoader componentIssuesLoader = mock(ComponentIssuesLoader.class);
private final AnalysisMetadataHolder analysisMetadataHolder = mock(AnalysisMetadataHolder.class);
- private TrackerExecution underTest = new TrackerExecution(baseInputFactory, rawInputFactory, closedIssuesInputFactory, tracker, componentIssuesLoader, analysisMetadataHolder);
+ private final TrackerExecution underTest = new TrackerExecution(baseInputFactory, closedIssuesInputFactory, tracker, componentIssuesLoader,
+ analysisMetadataHolder);
- private Input<DefaultIssue> rawInput = mock(Input.class);
- private Input<DefaultIssue> openIssuesInput = mock(Input.class);
- private Input<DefaultIssue> closedIssuesInput = mock(Input.class);
- private NonClosedTracking<DefaultIssue, DefaultIssue> nonClosedTracking = mock(NonClosedTracking.class);
- private Tracking<DefaultIssue, DefaultIssue> closedTracking = mock(Tracking.class);
+ private final Input<DefaultIssue> rawInput = mock(Input.class);
+ private final Input<DefaultIssue> openIssuesInput = mock(Input.class);
+ private final Input<DefaultIssue> closedIssuesInput = mock(Input.class);
+ private final NonClosedTracking<DefaultIssue, DefaultIssue> nonClosedTracking = mock(NonClosedTracking.class);
+ private final Tracking<DefaultIssue, DefaultIssue> closedTracking = mock(Tracking.class);
@Test
public void track_tracks_only_nonClosed_issues_if_tracking_returns_complete_from_Tracker() {
ReportComponent component = ReportComponent.builder(Component.Type.FILE, 1).build();
- when(rawInputFactory.create(component)).thenReturn(rawInput);
when(baseInputFactory.create(component)).thenReturn(openIssuesInput);
when(closedIssuesInputFactory.create(any())).thenThrow(new IllegalStateException("closedIssuesInputFactory should not be called"));
when(nonClosedTracking.isComplete()).thenReturn(true);
when(tracker.trackNonClosed(rawInput, openIssuesInput)).thenReturn(nonClosedTracking);
when(tracker.trackClosed(any(), any())).thenThrow(new IllegalStateException("trackClosed should not be called"));
- Tracking<DefaultIssue, DefaultIssue> tracking = underTest.track(component);
+ Tracking<DefaultIssue, DefaultIssue> tracking = underTest.track(component, rawInput);
assertThat(tracking).isSameAs(nonClosedTracking);
verify(tracker).trackNonClosed(rawInput, openIssuesInput);
@Test
public void track_does_not_track_nonClosed_issues_if_tracking_returns_incomplete_but_this_is_first_analysis() {
ReportComponent component = ReportComponent.builder(Component.Type.FILE, 1).build();
- when(rawInputFactory.create(component)).thenReturn(rawInput);
when(baseInputFactory.create(component)).thenReturn(openIssuesInput);
when(closedIssuesInputFactory.create(any())).thenThrow(new IllegalStateException("closedIssuesInputFactory should not be called"));
when(nonClosedTracking.isComplete()).thenReturn(false);
when(tracker.trackNonClosed(rawInput, openIssuesInput)).thenReturn(nonClosedTracking);
when(tracker.trackClosed(any(), any())).thenThrow(new IllegalStateException("trackClosed should not be called"));
- Tracking<DefaultIssue, DefaultIssue> tracking = underTest.track(component);
+ Tracking<DefaultIssue, DefaultIssue> tracking = underTest.track(component, rawInput);
assertThat(tracking).isSameAs(nonClosedTracking);
verify(tracker).trackNonClosed(rawInput, openIssuesInput);
@Test
public void track_tracks_nonClosed_issues_and_then_closedOnes_if_tracking_returns_incomplete() {
ReportComponent component = ReportComponent.builder(Component.Type.FILE, 1).build();
- when(rawInputFactory.create(component)).thenReturn(rawInput);
when(baseInputFactory.create(component)).thenReturn(openIssuesInput);
when(closedIssuesInputFactory.create(component)).thenReturn(closedIssuesInput);
when(nonClosedTracking.isComplete()).thenReturn(false);
when(tracker.trackNonClosed(rawInput, openIssuesInput)).thenReturn(nonClosedTracking);
when(tracker.trackClosed(nonClosedTracking, closedIssuesInput)).thenReturn(closedTracking);
- Tracking<DefaultIssue, DefaultIssue> tracking = underTest.track(component);
+ Tracking<DefaultIssue, DefaultIssue> tracking = underTest.track(component, rawInput);
assertThat(tracking).isSameAs(closedTracking);
verify(tracker).trackNonClosed(rawInput, openIssuesInput);
@Test
public void track_loadChanges_on_matched_closed_issues() {
ReportComponent component = ReportComponent.builder(Component.Type.FILE, 1).build();
- when(rawInputFactory.create(component)).thenReturn(rawInput);
when(baseInputFactory.create(component)).thenReturn(openIssuesInput);
when(closedIssuesInputFactory.create(component)).thenReturn(closedIssuesInput);
when(nonClosedTracking.isComplete()).thenReturn(false);
Collections.shuffle(mappedBaseIssues);
when(closedTracking.getMatchedRaws()).thenReturn(mappedBaseIssues.stream().collect(uniqueIndex(i -> new DefaultIssue().setKey("raw_for_" + i.key()), i -> i)));
- Tracking<DefaultIssue, DefaultIssue> tracking = underTest.track(component);
+ Tracking<DefaultIssue, DefaultIssue> tracking = underTest.track(component, rawInput);
assertThat(tracking).isSameAs(closedTracking);
verify(tracker).trackNonClosed(rawInput, openIssuesInput);
--- /dev/null
+/*
+ * 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 TrackerSourceBranchInputFactoryTest {
+ 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 SourceBranchComponentUuids sourceBranchComponentUuids = mock(SourceBranchComponentUuids.class);
+ private TrackerSourceBranchInputFactory underTest;
+
+ @Before
+ public void setUp() {
+ underTest = new TrackerSourceBranchInputFactory(componentIssuesLoader, sourceBranchComponentUuids, db.getDbClient());
+ }
+
+ @Test
+ public void gets_issues_and_hashes_in_matching_component() {
+ DefaultIssue issue1 = new DefaultIssue();
+ when(sourceBranchComponentUuids.getSourceBranchComponentUuid(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.createForSourceBranch(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(sourceBranchComponentUuids.getSourceBranchComponentUuid(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.createForSourceBranch(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.createForSourceBranch(component);
+
+ assertThat(input.getIssues()).isEmpty();
+ assertThat(input.getLineHashSequence().length()).isZero();
+ }
+
+ @Test
+ public void hasSourceBranchAnalysis_returns_true_if_source_branch_of_pr_was_analysed() {
+ when(sourceBranchComponentUuids.hasSourceBranchAnalysis()).thenReturn(true);
+
+ assertThat(underTest.hasSourceBranchAnalysis()).isTrue();
+ }
+
+ @Test
+ public void hasSourceBranchAnalysis_returns_false_if_no_source_branch_analysis() {
+ when(sourceBranchComponentUuids.hasSourceBranchAnalysis()).thenReturn(false);
+
+ assertThat(underTest.hasSourceBranchAnalysis()).isFalse();
+ }
+}