From afe312e4cbf9ac88676d526ff6da0b641aef4ae8 Mon Sep 17 00:00:00 2001 From: Julien HENRY Date: Tue, 26 Mar 2019 16:36:26 +0100 Subject: [PATCH] SONAR-11858 Apply light issue tracking with siblings for all branches --- ...a => SiblingComponentsWithOpenIssues.java} | 17 +- ...ProjectAnalysisTaskContainerPopulator.java | 12 +- .../issue/IntegrateIssuesVisitor.java | 8 +- .../projectanalysis/issue/IssueLifecycle.java | 2 +- ...hortBranchIssue.java => SiblingIssue.java} | 14 +- ...ueMerger.java => SiblingsIssueMerger.java} | 29 ++- ...sLoader.java => SiblingsIssuesLoader.java} | 28 +-- .../ShortBranchComponentsWithIssuesTest.java | 196 --------------- .../SiblingComponentsWithOpenIssuesTest.java | 229 ++++++++++++++++++ .../issue/IntegrateIssuesVisitorTest.java | 2 +- ...Test.java => SiblingsIssueMergerTest.java} | 65 ++--- .../org/sonar/db/component/ComponentDao.java | 4 +- .../sonar/db/component/ComponentMapper.java | 3 +- .../sonar/db/issue/ShortBranchIssueDto.java | 10 +- .../sonar/db/component/ComponentMapper.xml | 5 +- .../org/sonar/db/issue/IssueMapper.xml | 2 +- .../java/org/sonar/db/issue/IssueDaoTest.java | 2 +- .../org/sonar/core/issue/DefaultIssue.java | 4 +- .../core/issue/tracking/AbstractTracker.java | 4 +- .../sonar/core/issue/tracking/Trackable.java | 4 +- .../core/issue/tracking/TrackerTest.java | 10 +- 21 files changed, 334 insertions(+), 316 deletions(-) rename server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/{ShortBranchComponentsWithIssues.java => SiblingComponentsWithOpenIssues.java} (71%) rename server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/{ShortBranchIssue.java => SiblingIssue.java} (85%) rename server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/{ShortBranchIssueMerger.java => SiblingsIssueMerger.java} (54%) rename server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/{ShortBranchIssuesLoader.java => SiblingsIssuesLoader.java} (68%) delete mode 100644 server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ShortBranchComponentsWithIssuesTest.java create mode 100644 server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/SiblingComponentsWithOpenIssuesTest.java rename server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/{ShortBranchIssueMergerTest.java => SiblingsIssueMergerTest.java} (75%) diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/ShortBranchComponentsWithIssues.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/SiblingComponentsWithOpenIssues.java similarity index 71% rename from server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/ShortBranchComponentsWithIssues.java rename to server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/SiblingComponentsWithOpenIssues.java index 13942470210..cce1c2d09dc 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/ShortBranchComponentsWithIssues.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/SiblingComponentsWithOpenIssues.java @@ -25,6 +25,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.component.KeyWithUuidDto; @@ -32,24 +33,28 @@ import org.sonar.db.component.KeyWithUuidDto; import static org.sonar.db.component.ComponentDto.removeBranchAndPullRequestFromKey; /** - * Cache a map of component key -> uuid in short branches that have issues with status either RESOLVED or CONFIRMED. + * Cache a map of component key -> set<uuid> in sibling branches/PR that have open issues * */ -public class ShortBranchComponentsWithIssues { - private final String uuid; +public class SiblingComponentsWithOpenIssues { private final DbClient dbClient; + private final AnalysisMetadataHolder metadataHolder; + private final TreeRootHolder treeRootHolder; private Map> uuidsByKey; - public ShortBranchComponentsWithIssues(TreeRootHolder treeRootHolder, DbClient dbClient) { - this.uuid = treeRootHolder.getRoot().getUuid(); + public SiblingComponentsWithOpenIssues(TreeRootHolder treeRootHolder, AnalysisMetadataHolder metadataHolder, DbClient dbClient) { + this.treeRootHolder = treeRootHolder; + this.metadataHolder = metadataHolder; this.dbClient = dbClient; } private void loadUuidsByKey() { uuidsByKey = new HashMap<>(); + String currentBranchUuid = treeRootHolder.getRoot().getUuid(); try (DbSession dbSession = dbClient.openSession(false)) { - List components = dbClient.componentDao().selectComponentKeysHavingIssuesToMerge(dbSession, uuid); + List components = dbClient.componentDao().selectAllSiblingComponentKeysHavingOpenIssues(dbSession, + metadataHolder.getBranch().getMergeBranchUuid().orElse(currentBranchUuid), currentBranchUuid); for (KeyWithUuidDto dto : components) { uuidsByKey.computeIfAbsent(removeBranchAndPullRequestFromKey(dto.key()), s -> new HashSet<>()).add(dto.uuid()); } diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java index 3298ce5abc0..776e9d63b9a 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java @@ -36,7 +36,7 @@ import org.sonar.ce.task.projectanalysis.component.DbIdsRepositoryImpl; import org.sonar.ce.task.projectanalysis.component.DisabledComponentsHolderImpl; import org.sonar.ce.task.projectanalysis.component.MergeBranchComponentUuids; import org.sonar.ce.task.projectanalysis.component.ReportModulesPath; -import org.sonar.ce.task.projectanalysis.component.ShortBranchComponentsWithIssues; +import org.sonar.ce.task.projectanalysis.component.SiblingComponentsWithOpenIssues; import org.sonar.ce.task.projectanalysis.component.TreeRootHolderImpl; import org.sonar.ce.task.projectanalysis.dbmigration.DbMigrationModule; import org.sonar.ce.task.projectanalysis.duplication.CrossProjectDuplicationStatusHolderImpl; @@ -77,8 +77,8 @@ import org.sonar.ce.task.projectanalysis.issue.RuleRepositoryImpl; import org.sonar.ce.task.projectanalysis.issue.RuleTagsCopier; import org.sonar.ce.task.projectanalysis.issue.ScmAccountToUser; 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.SiblingsIssueMerger; +import org.sonar.ce.task.projectanalysis.issue.SiblingsIssuesLoader; import org.sonar.ce.task.projectanalysis.issue.ShortBranchOrPullRequestTrackerExecution; import org.sonar.ce.task.projectanalysis.issue.TrackerBaseInputFactory; import org.sonar.ce.task.projectanalysis.issue.TrackerExecution; @@ -197,7 +197,7 @@ public final class ProjectAnalysisTaskContainerPopulator implements ContainerPop MutableTaskResultHolderImpl.class, BatchReportReaderImpl.class, MergeBranchComponentUuids.class, - ShortBranchComponentsWithIssues.class, + SiblingComponentsWithOpenIssues.class, // repositories LanguageRepositoryImpl.class, @@ -281,8 +281,8 @@ public final class ProjectAnalysisTaskContainerPopulator implements ContainerPop BaseIssuesLoader.class, IssueTrackingDelegator.class, BranchPersisterImpl.class, - ShortBranchIssuesLoader.class, - ShortBranchIssueMerger.class, + SiblingsIssuesLoader.class, + SiblingsIssueMerger.class, // filemove ScoreMatrixDumperImpl.class, diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IntegrateIssuesVisitor.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IntegrateIssuesVisitor.java index bf04ad5d4a0..b048a138b9f 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IntegrateIssuesVisitor.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IntegrateIssuesVisitor.java @@ -40,12 +40,12 @@ public class IntegrateIssuesVisitor extends TypeAwareVisitorAdapter { private final IssueLifecycle issueLifecycle; private final IssueVisitors issueVisitors; private final IssueTrackingDelegator issueTracking; - private final ShortBranchIssueMerger issueStatusCopier; + private final SiblingsIssueMerger issueStatusCopier; private final AnalysisMetadataHolder analysisMetadataHolder; private final MergeBranchComponentUuids mergeBranchComponentUuids; public IntegrateIssuesVisitor(IssueCache issueCache, IssueLifecycle issueLifecycle, IssueVisitors issueVisitors, - AnalysisMetadataHolder analysisMetadataHolder, IssueTrackingDelegator issueTracking, ShortBranchIssueMerger issueStatusCopier, + AnalysisMetadataHolder analysisMetadataHolder, IssueTrackingDelegator issueTracking, SiblingsIssueMerger issueStatusCopier, MergeBranchComponentUuids mergeBranchComponentUuids) { super(CrawlerDepthLimit.FILE, POST_ORDER); this.issueCache = issueCache; @@ -86,9 +86,7 @@ public class IntegrateIssuesVisitor extends TypeAwareVisitorAdapter { return; } - if (analysisMetadataHolder.isLongLivingBranch()) { - issueStatusCopier.tryMerge(component, list); - } + issueStatusCopier.tryMerge(component, list); for (DefaultIssue issue : list) { process(component, issue, cacheAppender); diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueLifecycle.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueLifecycle.java index e8f046e8f06..55c4305918f 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueLifecycle.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueLifecycle.java @@ -94,7 +94,7 @@ public class IssueLifecycle { public void mergeConfirmedOrResolvedFromShortLivingBranch(DefaultIssue raw, DefaultIssue base, String fromShortBranchName) { copyAttributesOfIssueFromOtherBranch(raw, base); - raw.setFieldChange(changeContext, IssueFieldsSetter.FROM_SHORT_BRANCH, fromShortBranchName, analysisMetadataHolder.getBranch().getName()); + raw.setFieldChange(changeContext, IssueFieldsSetter.FROM_SHORT_BRANCH, fromShortBranchName, analysisMetadataHolder.isPullRequest() ? analysisMetadataHolder.getPullRequestKey() : analysisMetadataHolder.getBranch().getName()); } private void copyAttributesOfIssueFromOtherBranch(DefaultIssue to, DefaultIssue from) { diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/ShortBranchIssue.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/SiblingIssue.java similarity index 85% rename from server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/ShortBranchIssue.java rename to server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/SiblingIssue.java index 3f4b4d66b94..438fd46380f 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/ShortBranchIssue.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/SiblingIssue.java @@ -27,7 +27,7 @@ import org.sonar.api.rule.RuleKey; import org.sonar.core.issue.tracking.Trackable; @Immutable -public class ShortBranchIssue implements Trackable { +public class SiblingIssue implements Trackable { private final String key; private final Integer line; private final String message; @@ -35,9 +35,9 @@ public class ShortBranchIssue implements Trackable { private final RuleKey ruleKey; private final String status; private final String branchName; - private final Date creationDate; + private final Date updateDate; - public ShortBranchIssue(String key, @Nullable Integer line, String message, @Nullable String lineHash, RuleKey ruleKey, String status, String branchName, Date creationDate) { + public SiblingIssue(String key, @Nullable Integer line, String message, @Nullable String lineHash, RuleKey ruleKey, String status, String branchName, Date updateDate) { this.key = key; this.line = line; this.message = message; @@ -45,7 +45,7 @@ public class ShortBranchIssue implements Trackable { this.ruleKey = ruleKey; this.status = status; this.branchName = branchName; - this.creationDate = creationDate; + this.updateDate = updateDate; } public String getKey() { @@ -84,8 +84,8 @@ public class ShortBranchIssue implements Trackable { } @Override - public Date getCreationDate() { - return creationDate; + public Date getUpdateDate() { + return updateDate; } @Override @@ -104,7 +104,7 @@ public class ShortBranchIssue implements Trackable { if (getClass() != obj.getClass()) { return false; } - ShortBranchIssue other = (ShortBranchIssue) obj; + SiblingIssue other = (SiblingIssue) obj; return key.equals(other.key); } diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/ShortBranchIssueMerger.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/SiblingsIssueMerger.java similarity index 54% rename from server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/ShortBranchIssueMerger.java rename to server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/SiblingsIssueMerger.java index 2088df0a760..aa60198c0a1 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/ShortBranchIssueMerger.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/SiblingsIssueMerger.java @@ -25,37 +25,36 @@ import org.sonar.ce.task.projectanalysis.component.Component; import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.tracking.SimpleTracker; import org.sonar.core.issue.tracking.Tracking; -import org.sonar.ce.task.projectanalysis.component.Component; -public class ShortBranchIssueMerger { - private final ShortBranchIssuesLoader shortBranchIssuesLoader; - private final SimpleTracker tracker; +public class SiblingsIssueMerger { + private final SiblingsIssuesLoader siblingsIssuesLoader; + private final SimpleTracker tracker; private final IssueLifecycle issueLifecycle; - public ShortBranchIssueMerger(ShortBranchIssuesLoader resolvedShortBranchIssuesLoader, IssueLifecycle issueLifecycle) { - this(resolvedShortBranchIssuesLoader, new SimpleTracker<>(), issueLifecycle); + public SiblingsIssueMerger(SiblingsIssuesLoader resolvedSiblingsIssuesLoader, IssueLifecycle issueLifecycle) { + this(resolvedSiblingsIssuesLoader, new SimpleTracker<>(), issueLifecycle); } - public ShortBranchIssueMerger(ShortBranchIssuesLoader shortBranchIssuesLoader, SimpleTracker tracker, IssueLifecycle issueLifecycle) { - this.shortBranchIssuesLoader = shortBranchIssuesLoader; + public SiblingsIssueMerger(SiblingsIssuesLoader siblingsIssuesLoader, SimpleTracker tracker, IssueLifecycle issueLifecycle) { + this.siblingsIssuesLoader = siblingsIssuesLoader; this.tracker = tracker; this.issueLifecycle = issueLifecycle; } /** - * Look for all resolved/confirmed issues in short living branches targeting the current long living branch, and run + * Look for all unclosed issues in branches/PR targeting the same long living branch, and run * a light issue tracking to find matches. Then merge issue attributes in the new issues. */ public void tryMerge(Component component, Collection newIssues) { - Collection shortBranchIssues = shortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component); - Tracking tracking = tracker.track(newIssues, shortBranchIssues); + Collection siblingIssues = siblingsIssuesLoader.loadCandidateSiblingIssuesForMerging(component); + Tracking tracking = tracker.track(newIssues, siblingIssues); - Map matchedRaws = tracking.getMatchedRaws(); + Map matchedRaws = tracking.getMatchedRaws(); - Map defaultIssues = shortBranchIssuesLoader.loadDefaultIssuesWithChanges(matchedRaws.values()); + Map defaultIssues = siblingsIssuesLoader.loadDefaultIssuesWithChanges(matchedRaws.values()); - for (Map.Entry e : matchedRaws.entrySet()) { - ShortBranchIssue issue = e.getValue(); + for (Map.Entry e : matchedRaws.entrySet()) { + SiblingIssue issue = e.getValue(); issueLifecycle.mergeConfirmedOrResolvedFromShortLivingBranch(e.getKey(), defaultIssues.get(issue), issue.getBranchName()); } } diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/ShortBranchIssuesLoader.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/SiblingsIssuesLoader.java similarity index 68% rename from server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/ShortBranchIssuesLoader.java rename to server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/SiblingsIssuesLoader.java index 39323268284..1b8f2b2b241 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/ShortBranchIssuesLoader.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/SiblingsIssuesLoader.java @@ -26,7 +26,7 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import org.sonar.ce.task.projectanalysis.component.Component; -import org.sonar.ce.task.projectanalysis.component.ShortBranchComponentsWithIssues; +import org.sonar.ce.task.projectanalysis.component.SiblingComponentsWithOpenIssues; import org.sonar.core.issue.DefaultIssue; import org.sonar.db.DbClient; import org.sonar.db.DbSession; @@ -38,22 +38,22 @@ import static org.sonar.api.utils.DateUtils.longToDate; import static org.sonar.core.util.stream.MoreCollectors.toList; import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex; -public class ShortBranchIssuesLoader { +public class SiblingsIssuesLoader { - private final ShortBranchComponentsWithIssues shortBranchComponentsWithIssues; + private final SiblingComponentsWithOpenIssues siblingComponentsWithOpenIssues; private final DbClient dbClient; private final ComponentIssuesLoader componentIssuesLoader; - public ShortBranchIssuesLoader(ShortBranchComponentsWithIssues shortBranchComponentsWithIssues, DbClient dbClient, - ComponentIssuesLoader componentIssuesLoader) { - this.shortBranchComponentsWithIssues = shortBranchComponentsWithIssues; + public SiblingsIssuesLoader(SiblingComponentsWithOpenIssues siblingComponentsWithOpenIssues, DbClient dbClient, + ComponentIssuesLoader componentIssuesLoader) { + this.siblingComponentsWithOpenIssues = siblingComponentsWithOpenIssues; this.dbClient = dbClient; this.componentIssuesLoader = componentIssuesLoader; } - public Collection loadCandidateIssuesForMergingInTargetBranch(Component component) { + public Collection loadCandidateSiblingIssuesForMerging(Component component) { String componentKey = ComponentDto.removeBranchAndPullRequestFromKey(component.getDbKey()); - Set uuids = shortBranchComponentsWithIssues.getUuids(componentKey); + Set uuids = siblingComponentsWithOpenIssues.getUuids(componentKey); if (uuids.isEmpty()) { return Collections.emptyList(); } @@ -61,22 +61,22 @@ public class ShortBranchIssuesLoader { try (DbSession session = dbClient.openSession(false)) { return dbClient.issueDao().selectOpenByComponentUuids(session, uuids) .stream() - .map(ShortBranchIssuesLoader::toShortBranchIssue) + .map(SiblingsIssuesLoader::toSiblingIssue) .collect(Collectors.toList()); } } - private static ShortBranchIssue toShortBranchIssue(ShortBranchIssueDto dto) { - return new ShortBranchIssue(dto.getKey(), dto.getLine(), dto.getMessage(), dto.getChecksum(), dto.getRuleKey(), dto.getStatus(), dto.getBranchName(), - longToDate(dto.getIssueCreationDate())); + private static SiblingIssue toSiblingIssue(ShortBranchIssueDto dto) { + return new SiblingIssue(dto.getKey(), dto.getLine(), dto.getMessage(), dto.getChecksum(), dto.getRuleKey(), dto.getStatus(), dto.getBranchName(), + longToDate(dto.getIssueUpdateDate())); } - public Map loadDefaultIssuesWithChanges(Collection lightIssues) { + public Map loadDefaultIssuesWithChanges(Collection lightIssues) { if (lightIssues.isEmpty()) { return Collections.emptyMap(); } - Map issuesByKey = lightIssues.stream().collect(Collectors.toMap(ShortBranchIssue::getKey, i -> i)); + Map issuesByKey = lightIssues.stream().collect(Collectors.toMap(SiblingIssue::getKey, i -> i)); try (DbSession session = dbClient.openSession(false)) { List issues = dbClient.issueDao().selectByKeys(session, issuesByKey.keySet()) .stream() diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ShortBranchComponentsWithIssuesTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ShortBranchComponentsWithIssuesTest.java deleted file mode 100644 index c2d5d63a2de..00000000000 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ShortBranchComponentsWithIssuesTest.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * 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.component; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -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.issue.IssueTesting; -import org.sonar.db.rule.RuleDefinitionDto; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class ShortBranchComponentsWithIssuesTest { - @Rule - public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); - - @Rule - public DbTester db = DbTester.create(); - - private ShortBranchComponentsWithIssues underTest; - - private ComponentDto long1; - private ComponentDto fileWithNoIssues; - private ComponentDto fileWithOneOpenIssue; - private ComponentDto fileWithOneResolvedIssue; - private ComponentDto fileWithOneOpenTwoResolvedIssues; - private ComponentDto fileWithOneResolvedIssueInLong1Short1; - private ComponentDto fileWithOneResolvedIssueInLong1Short2; - - private ComponentDto long2; - private ComponentDto fileWithOneOpenIssueOnLong2; - private ComponentDto fileWithOneResolvedIssueOnLong2; - - @Before - public void setUp() { - ComponentDto project = db.components().insertMainBranch(); - - long1 = db.components().insertProjectBranch(project, b -> b.setKey("long1"), b -> b.setBranchType(BranchType.LONG)); - ComponentDto long1short1 = db.components().insertProjectBranch(project, - b -> b.setKey("long1short1"), - b -> b.setBranchType(BranchType.SHORT), - b -> b.setMergeBranchUuid(long1.uuid())); - ComponentDto long1short2 = db.components().insertProjectBranch(project, - b -> b.setKey("long1short2"), - b -> b.setBranchType(BranchType.SHORT), - b -> b.setMergeBranchUuid(long1.uuid())); - - fileWithNoIssues = db.components().insertComponent(ComponentTesting.newFileDto(long1, null)); - - RuleDefinitionDto rule = db.rules().insert(); - - fileWithOneOpenIssue = db.components().insertComponent(ComponentTesting.newFileDto(long1short1, null)); - db.issues().insertIssue(IssueTesting.newIssue(rule, long1short1, fileWithOneOpenIssue)); - - fileWithOneResolvedIssue = db.components().insertComponent(ComponentTesting.newFileDto(long1short1, null)); - db.issues().insertIssue(IssueTesting.newIssue(rule, long1short1, fileWithOneResolvedIssue).setStatus("RESOLVED")); - - fileWithOneOpenTwoResolvedIssues = db.components().insertComponent(ComponentTesting.newFileDto(long1short1, null)); - db.issues().insertIssue(IssueTesting.newIssue(rule, long1short1, fileWithOneOpenTwoResolvedIssues)); - db.issues().insertIssue(IssueTesting.newIssue(rule, long1short1, fileWithOneOpenTwoResolvedIssues).setStatus("RESOLVED")); - - String fileKey = "file-x"; - fileWithOneResolvedIssueInLong1Short1 = db.components().insertComponent(ComponentTesting.newFileDto(long1short1, null) - .setDbKey(fileKey + ":BRANCH:long1short1")); - db.issues().insertIssue(IssueTesting.newIssue(rule, long1short1, fileWithOneResolvedIssueInLong1Short1).setStatus("RESOLVED")); - fileWithOneResolvedIssueInLong1Short2 = db.components().insertComponent(ComponentTesting.newFileDto(long1short2, null) - .setDbKey(fileKey + ":BRANCH:long1short2")); - db.issues().insertIssue(IssueTesting.newIssue(rule, long1short2, fileWithOneResolvedIssueInLong1Short2).setStatus("RESOLVED")); - - long2 = db.components().insertProjectBranch(project, b -> b.setKey("long2"), b -> b.setBranchType(BranchType.LONG)); - ComponentDto long2short1 = db.components().insertProjectBranch(project, - b -> b.setKey("long2short1"), - b -> b.setBranchType(BranchType.SHORT), - b -> b.setMergeBranchUuid(long2.uuid())); - - fileWithOneOpenIssueOnLong2 = db.components().insertComponent(ComponentTesting.newFileDto(long2short1, null)); - db.issues().insertIssue(IssueTesting.newIssue(rule, long2short1, fileWithOneOpenIssueOnLong2)); - - fileWithOneResolvedIssueOnLong2 = db.components().insertComponent(ComponentTesting.newFileDto(long2short1, null)); - db.issues().insertIssue(IssueTesting.newIssue(rule, long2short1, fileWithOneResolvedIssueOnLong2).setStatus("RESOLVED")); - - setRootUuid(long1.uuid()); - underTest = new ShortBranchComponentsWithIssues(treeRootHolder, db.getDbClient()); - } - - @Test - public void should_find_components_with_issues_to_merge_on_long1() { - setRootUuid(long1.uuid()); - - assertThat(underTest.getUuids(fileWithNoIssues.getKey())).isEmpty(); - assertThat(underTest.getUuids(fileWithOneOpenIssue.getKey())).containsOnly(fileWithOneOpenIssue.uuid()); - assertThat(underTest.getUuids(fileWithOneResolvedIssue.getKey())).containsOnly(fileWithOneResolvedIssue.uuid()); - assertThat(underTest.getUuids(fileWithOneOpenTwoResolvedIssues.getKey())).containsOnly(fileWithOneOpenTwoResolvedIssues.uuid()); - - assertThat(fileWithOneResolvedIssueInLong1Short1.getKey()).isEqualTo(fileWithOneResolvedIssueInLong1Short2.getKey()); - assertThat(underTest.getUuids(fileWithOneResolvedIssueInLong1Short1.getKey())).containsOnly( - fileWithOneResolvedIssueInLong1Short1.uuid(), - fileWithOneResolvedIssueInLong1Short2.uuid()); - } - - @Test - public void should_find_components_with_issues_to_merge_on_long2() { - setRootUuid(long2.uuid()); - underTest = new ShortBranchComponentsWithIssues(treeRootHolder, db.getDbClient()); - - assertThat(underTest.getUuids(fileWithOneResolvedIssue.getKey())).isEmpty(); - assertThat(underTest.getUuids(fileWithOneResolvedIssueOnLong2.getKey())).containsOnly(fileWithOneResolvedIssueOnLong2.uuid()); - assertThat(underTest.getUuids(fileWithOneOpenIssueOnLong2.getKey())).containsOnly(fileWithOneOpenIssueOnLong2.uuid()); - } - - @Test - public void should_find_components_with_issues_to_merge_on_derived_short() { - ComponentDto project = db.components().insertMainBranch(); - setRootUuid(project.uuid()); - - ComponentDto branch = db.components().insertProjectBranch(project, - b -> b.setBranchType(BranchType.SHORT), - b -> b.setMergeBranchUuid(project.uuid())); - - RuleDefinitionDto rule = db.rules().insert(); - - ComponentDto fileWithResolvedIssue = db.components().insertComponent(ComponentTesting.newFileDto(branch, null)); - db.issues().insertIssue(IssueTesting.newIssue(rule, branch, fileWithResolvedIssue).setStatus("RESOLVED")); - - underTest = new ShortBranchComponentsWithIssues(treeRootHolder, db.getDbClient()); - - assertThat(underTest.getUuids(fileWithResolvedIssue.getKey())).hasSize(1); - } - - @Test - public void should_find_components_with_issues_to_merge_on_derived_pull_request() { - ComponentDto project = db.components().insertMainBranch(); - setRootUuid(project.uuid()); - - ComponentDto pullRequest = db.components().insertProjectBranch(project, - b -> b.setBranchType(BranchType.PULL_REQUEST), - b -> b.setMergeBranchUuid(project.uuid())); - - RuleDefinitionDto rule = db.rules().insert(); - - ComponentDto fileWithResolvedIssue = db.components().insertComponent(ComponentTesting.newFileDto(pullRequest, null)); - db.issues().insertIssue(IssueTesting.newIssue(rule, pullRequest, fileWithResolvedIssue).setStatus("RESOLVED")); - - underTest = new ShortBranchComponentsWithIssues(treeRootHolder, db.getDbClient()); - - assertThat(underTest.getUuids(fileWithResolvedIssue.getKey())).hasSize(1); - } - - @Test - public void should_not_find_components_with_issues_to_merge_on_derived_long() { - ComponentDto project = db.components().insertMainBranch(); - setRootUuid(project.uuid()); - - ComponentDto branch = db.components().insertProjectBranch(project, - b -> b.setBranchType(BranchType.LONG), - b -> b.setMergeBranchUuid(project.uuid())); - - RuleDefinitionDto rule = db.rules().insert(); - - ComponentDto fileWithResolvedIssue = db.components().insertComponent(ComponentTesting.newFileDto(branch, null)); - db.issues().insertIssue(IssueTesting.newIssue(rule, branch, fileWithResolvedIssue).setStatus("RESOLVED")); - - underTest = new ShortBranchComponentsWithIssues(treeRootHolder, db.getDbClient()); - - assertThat(underTest.getUuids(fileWithResolvedIssue.getKey())).isEmpty(); - } - - private void setRootUuid(String uuid) { - Component root = mock(Component.class); - when(root.getUuid()).thenReturn(uuid); - treeRootHolder.setRoot(root); - } -} diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/SiblingComponentsWithOpenIssuesTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/SiblingComponentsWithOpenIssuesTest.java new file mode 100644 index 00000000000..dc73d0cb075 --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/SiblingComponentsWithOpenIssuesTest.java @@ -0,0 +1,229 @@ +/* + * 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.component; + +import java.util.Optional; +import javax.annotation.Nullable; +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.issue.IssueTesting; +import org.sonar.db.rule.RuleDefinitionDto; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class SiblingComponentsWithOpenIssuesTest { + @Rule + public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); + + @Rule + public AnalysisMetadataHolderRule metadataHolder = new AnalysisMetadataHolderRule(); + + @Rule + public DbTester db = DbTester.create(); + + private SiblingComponentsWithOpenIssues underTest; + + private ComponentDto long1; + private ComponentDto fileWithNoIssuesOnLong1; + private ComponentDto fileWithOneOpenIssueOnLong1Short1; + private ComponentDto fileWithOneResolvedIssueOnLong1Short1; + private ComponentDto fileWithOneOpenTwoResolvedIssuesOnLong1Short1; + private ComponentDto fileXWithOneResolvedIssueOnLong1Short1; + private ComponentDto fileXWithOneResolvedIssueOnLong1Short2; + + private ComponentDto long2; + private ComponentDto fileWithOneOpenIssueOnLong2Short1; + private ComponentDto fileWithOneResolvedIssueOnLong2Short1; + private ComponentDto long1short1; + + @Before + public void setUp() { + ComponentDto project = db.components().insertMainBranch(); + + long1 = db.components().insertProjectBranch(project, b -> b.setKey("long1"), b -> b.setBranchType(BranchType.LONG)); + long1short1 = db.components().insertProjectBranch(project, + b -> b.setKey("long1short1"), + b -> b.setBranchType(BranchType.SHORT), + b -> b.setMergeBranchUuid(long1.uuid())); + ComponentDto long1short2 = db.components().insertProjectBranch(project, + b -> b.setKey("long1short2"), + b -> b.setBranchType(BranchType.SHORT), + b -> b.setMergeBranchUuid(long1.uuid())); + + fileWithNoIssuesOnLong1 = db.components().insertComponent(ComponentTesting.newFileDto(long1, null)); + + RuleDefinitionDto rule = db.rules().insert(); + + fileWithOneOpenIssueOnLong1Short1 = db.components().insertComponent(ComponentTesting.newFileDto(long1short1, null)); + db.issues().insertIssue(IssueTesting.newIssue(rule, long1short1, fileWithOneOpenIssueOnLong1Short1)); + + fileWithOneResolvedIssueOnLong1Short1 = db.components().insertComponent(ComponentTesting.newFileDto(long1short1, null)); + db.issues().insertIssue(IssueTesting.newIssue(rule, long1short1, fileWithOneResolvedIssueOnLong1Short1).setStatus("RESOLVED")); + + fileWithOneOpenTwoResolvedIssuesOnLong1Short1 = db.components().insertComponent(ComponentTesting.newFileDto(long1short1, null)); + db.issues().insertIssue(IssueTesting.newIssue(rule, long1short1, fileWithOneOpenTwoResolvedIssuesOnLong1Short1)); + db.issues().insertIssue(IssueTesting.newIssue(rule, long1short1, fileWithOneOpenTwoResolvedIssuesOnLong1Short1).setStatus("RESOLVED")); + + String fileKey = "file-x"; + fileXWithOneResolvedIssueOnLong1Short1 = db.components().insertComponent(ComponentTesting.newFileDto(long1short1, null) + .setDbKey(fileKey + ":BRANCH:long1short1")); + db.issues().insertIssue(IssueTesting.newIssue(rule, long1short1, fileXWithOneResolvedIssueOnLong1Short1).setStatus("RESOLVED")); + fileXWithOneResolvedIssueOnLong1Short2 = db.components().insertComponent(ComponentTesting.newFileDto(long1short2, null) + .setDbKey(fileKey + ":BRANCH:long1short2")); + db.issues().insertIssue(IssueTesting.newIssue(rule, long1short2, fileXWithOneResolvedIssueOnLong1Short2).setStatus("RESOLVED")); + + long2 = db.components().insertProjectBranch(project, b -> b.setKey("long2"), b -> b.setBranchType(BranchType.LONG)); + ComponentDto long2short1 = db.components().insertProjectBranch(project, + b -> b.setKey("long2short1"), + b -> b.setBranchType(BranchType.SHORT), + b -> b.setMergeBranchUuid(long2.uuid())); + + fileWithOneOpenIssueOnLong2Short1 = db.components().insertComponent(ComponentTesting.newFileDto(long2short1, null)); + db.issues().insertIssue(IssueTesting.newIssue(rule, long2short1, fileWithOneOpenIssueOnLong2Short1)); + + fileWithOneResolvedIssueOnLong2Short1 = db.components().insertComponent(ComponentTesting.newFileDto(long2short1, null)); + db.issues().insertIssue(IssueTesting.newIssue(rule, long2short1, fileWithOneResolvedIssueOnLong2Short1).setStatus("RESOLVED")); + + setCurrentBranchUuid(long1.uuid()); + underTest = new SiblingComponentsWithOpenIssues(treeRootHolder, metadataHolder, db.getDbClient()); + } + + @Test + public void should_find_sibling_components_with_open_issues_for_long1() { + setCurrentBranchUuid(long1.uuid()); + setReferenceBranchUuid(null); + + assertThat(underTest.getUuids(fileWithNoIssuesOnLong1.getKey())).isEmpty(); + assertThat(underTest.getUuids(fileWithOneOpenIssueOnLong1Short1.getKey())).containsOnly(fileWithOneOpenIssueOnLong1Short1.uuid()); + assertThat(underTest.getUuids(fileWithOneResolvedIssueOnLong1Short1.getKey())).containsOnly(fileWithOneResolvedIssueOnLong1Short1.uuid()); + assertThat(underTest.getUuids(fileWithOneOpenTwoResolvedIssuesOnLong1Short1.getKey())).containsOnly(fileWithOneOpenTwoResolvedIssuesOnLong1Short1.uuid()); + + assertThat(fileXWithOneResolvedIssueOnLong1Short1.getKey()).isEqualTo(fileXWithOneResolvedIssueOnLong1Short2.getKey()); + assertThat(underTest.getUuids(fileXWithOneResolvedIssueOnLong1Short1.getKey())).containsOnly( + fileXWithOneResolvedIssueOnLong1Short1.uuid(), + fileXWithOneResolvedIssueOnLong1Short2.uuid()); + } + + @Test + public void should_find_sibling_components_with_open_issues_for_short1() { + setCurrentBranchUuid(long1short1.uuid()); + setReferenceBranchUuid(long1.uuid()); + + assertThat(underTest.getUuids(fileWithNoIssuesOnLong1.getKey())).isEmpty(); + assertThat(underTest.getUuids(fileWithOneOpenIssueOnLong1Short1.getKey())).isEmpty(); + assertThat(underTest.getUuids(fileWithOneResolvedIssueOnLong1Short1.getKey())).isEmpty(); + assertThat(underTest.getUuids(fileWithOneOpenTwoResolvedIssuesOnLong1Short1.getKey())).isEmpty(); + + assertThat(underTest.getUuids(fileXWithOneResolvedIssueOnLong1Short1.getKey())).containsOnly( + fileXWithOneResolvedIssueOnLong1Short2.uuid()); + } + + @Test + public void should_find_sibling_components_with_open_issues_for_long2() { + setCurrentBranchUuid(long2.uuid()); + setReferenceBranchUuid(null); + underTest = new SiblingComponentsWithOpenIssues(treeRootHolder, metadataHolder, db.getDbClient()); + + assertThat(underTest.getUuids(fileWithOneResolvedIssueOnLong1Short1.getKey())).isEmpty(); + assertThat(underTest.getUuids(fileWithOneResolvedIssueOnLong2Short1.getKey())).containsOnly(fileWithOneResolvedIssueOnLong2Short1.uuid()); + assertThat(underTest.getUuids(fileWithOneOpenIssueOnLong2Short1.getKey())).containsOnly(fileWithOneOpenIssueOnLong2Short1.uuid()); + } + + @Test + public void should_find_sibling_components_with_open_issues_from_short() { + ComponentDto project = db.components().insertMainBranch(); + setCurrentBranchUuid(project.uuid()); + setReferenceBranchUuid(null); + + ComponentDto branch = db.components().insertProjectBranch(project, + b -> b.setBranchType(BranchType.SHORT), + b -> b.setMergeBranchUuid(project.uuid())); + + RuleDefinitionDto rule = db.rules().insert(); + + ComponentDto fileWithResolvedIssueOnShort = db.components().insertComponent(ComponentTesting.newFileDto(branch, null)); + db.issues().insertIssue(IssueTesting.newIssue(rule, branch, fileWithResolvedIssueOnShort).setStatus("RESOLVED")); + + underTest = new SiblingComponentsWithOpenIssues(treeRootHolder, metadataHolder, db.getDbClient()); + + assertThat(underTest.getUuids(fileWithResolvedIssueOnShort.getKey())).hasSize(1); + } + + @Test + public void should_find_sibling_components_with_open_issues_from_pullrequest() { + ComponentDto project = db.components().insertMainBranch(); + setCurrentBranchUuid(project.uuid()); + setReferenceBranchUuid(null); + + ComponentDto pullRequest = db.components().insertProjectBranch(project, + b -> b.setBranchType(BranchType.PULL_REQUEST), + b -> b.setMergeBranchUuid(project.uuid())); + + RuleDefinitionDto rule = db.rules().insert(); + + ComponentDto fileWithResolvedIssueOnPullrequest = db.components().insertComponent(ComponentTesting.newFileDto(pullRequest, null)); + db.issues().insertIssue(IssueTesting.newIssue(rule, pullRequest, fileWithResolvedIssueOnPullrequest).setStatus("RESOLVED")); + + underTest = new SiblingComponentsWithOpenIssues(treeRootHolder, metadataHolder, db.getDbClient()); + + assertThat(underTest.getUuids(fileWithResolvedIssueOnPullrequest.getKey())).hasSize(1); + } + + @Test + public void should_not_find_sibling_components_on_derived_long() { + ComponentDto project = db.components().insertMainBranch(); + setCurrentBranchUuid(project.uuid()); + setReferenceBranchUuid(null); + + ComponentDto derivedLongBranch = db.components().insertProjectBranch(project, + b -> b.setBranchType(BranchType.LONG), + b -> b.setMergeBranchUuid(project.uuid())); + + RuleDefinitionDto rule = db.rules().insert(); + + ComponentDto fileWithResolvedIssueOnDerivedLongBranch = db.components().insertComponent(ComponentTesting.newFileDto(derivedLongBranch, null)); + db.issues().insertIssue(IssueTesting.newIssue(rule, derivedLongBranch, fileWithResolvedIssueOnDerivedLongBranch).setStatus("RESOLVED")); + + underTest = new SiblingComponentsWithOpenIssues(treeRootHolder, metadataHolder, db.getDbClient()); + + assertThat(underTest.getUuids(fileWithResolvedIssueOnDerivedLongBranch.getKey())).isEmpty(); + } + + private void setCurrentBranchUuid(String uuid) { + Component root = mock(Component.class); + when(root.getUuid()).thenReturn(uuid); + treeRootHolder.setRoot(root); + } + + private void setReferenceBranchUuid(@Nullable String uuid) { + Branch branch = mock(Branch.class); + when(branch.getMergeBranchUuid()).thenReturn(Optional.ofNullable(uuid)); + metadataHolder.setBranch(branch); + } +} diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IntegrateIssuesVisitorTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IntegrateIssuesVisitorTest.java index 5bd063b8d16..55e56244c62 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IntegrateIssuesVisitorTest.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IntegrateIssuesVisitorTest.java @@ -117,7 +117,7 @@ public class IntegrateIssuesVisitorTest { private IssueLifecycle issueLifecycle = mock(IssueLifecycle.class); private IssueVisitor issueVisitor = mock(IssueVisitor.class); private MergeBranchComponentUuids mergeBranchComponentsUuids = mock(MergeBranchComponentUuids.class); - private ShortBranchIssueMerger issueStatusCopier = mock(ShortBranchIssueMerger.class); + private SiblingsIssueMerger issueStatusCopier = mock(SiblingsIssueMerger.class); private MergeBranchComponentUuids mergeBranchComponentUuids = mock(MergeBranchComponentUuids.class); private SourceLinesHashRepository sourceLinesHash = mock(SourceLinesHashRepository.class); private NewLinesRepository newLinesRepository = mock(NewLinesRepository.class); diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/ShortBranchIssueMergerTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/SiblingsIssueMergerTest.java similarity index 75% rename from server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/ShortBranchIssueMergerTest.java rename to server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/SiblingsIssueMergerTest.java index 6cc8c11bf2e..e595aa04b69 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/ShortBranchIssueMergerTest.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/SiblingsIssueMergerTest.java @@ -23,6 +23,7 @@ import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Collections; import java.util.Date; +import java.util.Optional; import javax.annotation.Nullable; import org.junit.Before; import org.junit.Rule; @@ -34,7 +35,9 @@ import org.sonar.api.config.internal.MapSettings; import org.sonar.api.issue.Issue; import org.sonar.api.rule.RuleKey; import org.sonar.api.utils.System2; -import org.sonar.ce.task.projectanalysis.component.ShortBranchComponentsWithIssues; +import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule; +import org.sonar.ce.task.projectanalysis.analysis.Branch; +import org.sonar.ce.task.projectanalysis.component.SiblingComponentsWithOpenIssues; import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule; import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.FieldDiffs; @@ -52,13 +55,17 @@ 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.when; import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder; import static org.sonar.db.component.ComponentTesting.newFileDto; -public class ShortBranchIssueMergerTest { +public class SiblingsIssueMergerTest { @Mock private IssueLifecycle issueLifecycle; + @Mock + private Branch branch; + @Rule public DbTester db = DbTester.create(); @@ -68,6 +75,9 @@ public class ShortBranchIssueMergerTest { .addChildren(FILE_1) .build()); + @Rule + public AnalysisMetadataHolderRule metadataHolder = new AnalysisMetadataHolderRule(); + private static final String PROJECT_KEY = "project"; private static final int PROJECT_REF = 1; private static final String PROJECT_UUID = "projectUuid"; @@ -81,8 +91,8 @@ public class ShortBranchIssueMergerTest { .setUuid(FILE_1_UUID) .build(); - private SimpleTracker tracker = new SimpleTracker<>(); - private ShortBranchIssueMerger copier; + private SimpleTracker tracker = new SimpleTracker<>(); + private SiblingsIssueMerger copier; private ComponentDto fileOnBranch1Dto; private ComponentDto fileOnBranch2Dto; private ComponentDto fileOnBranch3Dto; @@ -95,9 +105,11 @@ public class ShortBranchIssueMergerTest { @Before public void setUp() { MockitoAnnotations.initMocks(this); + when(branch.getMergeBranchUuid()).thenReturn(Optional.empty()); + metadataHolder.setBranch(branch); DbClient dbClient = db.getDbClient(); ComponentIssuesLoader componentIssuesLoader = new ComponentIssuesLoader(dbClient, null, null, new MapSettings().asConfig(), System2.INSTANCE); - copier = new ShortBranchIssueMerger(new ShortBranchIssuesLoader(new ShortBranchComponentsWithIssues(treeRootHolder, dbClient), dbClient, componentIssuesLoader), tracker, + copier = new SiblingsIssueMerger(new SiblingsIssuesLoader(new SiblingComponentsWithOpenIssues(treeRootHolder, metadataHolder, dbClient), dbClient, componentIssuesLoader), tracker, issueLifecycle); projectDto = db.components().insertMainBranch(p -> p.setDbKey(PROJECT_KEY).setUuid(PROJECT_UUID)); branch1Dto = db.components().insertProjectBranch(projectDto, b -> b.setKey("myBranch1") @@ -145,53 +157,22 @@ public class ShortBranchIssueMergerTest { } @Test - public void prefer_resolved_issues() { - db.issues().insertIssue(IssueTesting.newIssue(rule, branch1Dto, fileOnBranch1Dto).setKee("issue1").setStatus(Issue.STATUS_REOPENED).setLine(1).setChecksum("checksum")); - db.issues().insertIssue(IssueTesting.newIssue(rule, branch2Dto, fileOnBranch2Dto).setKee("issue2").setStatus(Issue.STATUS_CONFIRMED).setLine(1).setChecksum("checksum")); - db.issues().insertIssue(IssueTesting.newIssue(rule, branch3Dto, fileOnBranch3Dto).setKee("issue3").setStatus(Issue.STATUS_RESOLVED) - .setResolution(Issue.RESOLUTION_FALSE_POSITIVE).setLine(1).setChecksum("checksum")); - DefaultIssue newIssue = createIssue("newIssue", rule.getKey(), Issue.STATUS_OPEN, null, new Date()); - - copier.tryMerge(FILE_1, Collections.singleton(newIssue)); - - ArgumentCaptor issueToMerge = ArgumentCaptor.forClass(DefaultIssue.class); - verify(issueLifecycle).mergeConfirmedOrResolvedFromShortLivingBranch(eq(newIssue), issueToMerge.capture(), eq("myBranch3")); - - assertThat(issueToMerge.getValue().key()).isEqualTo("issue3"); - } - - @Test - public void prefer_confirmed_issues_if_no_resolved() { - db.issues().insertIssue(IssueTesting.newIssue(rule, branch1Dto, fileOnBranch1Dto).setKee("issue1").setStatus(Issue.STATUS_REOPENED).setLine(1).setChecksum("checksum")); - db.issues().insertIssue(IssueTesting.newIssue(rule, branch2Dto, fileOnBranch2Dto).setKee("issue2").setStatus(Issue.STATUS_OPEN).setLine(1).setChecksum("checksum")); - db.issues().insertIssue(IssueTesting.newIssue(rule, branch3Dto, fileOnBranch3Dto).setKee("issue3").setStatus(Issue.STATUS_CONFIRMED).setLine(1).setChecksum("checksum")); - DefaultIssue newIssue = createIssue("newIssue", rule.getKey(), Issue.STATUS_OPEN, null, new Date()); - - copier.tryMerge(FILE_1, Collections.singleton(newIssue)); - - ArgumentCaptor issueToMerge = ArgumentCaptor.forClass(DefaultIssue.class); - verify(issueLifecycle).mergeConfirmedOrResolvedFromShortLivingBranch(eq(newIssue), issueToMerge.capture(), eq("myBranch3")); - - assertThat(issueToMerge.getValue().key()).isEqualTo("issue3"); - } - - @Test - public void prefer_older_issues() { + public void prefer_more_recently_updated_issues() { Instant now = Instant.now(); db.issues().insertIssue(IssueTesting.newIssue(rule, branch1Dto, fileOnBranch1Dto).setKee("issue1").setStatus(Issue.STATUS_REOPENED).setLine(1).setChecksum("checksum") - .setIssueCreationDate(Date.from(now.plus(2, ChronoUnit.SECONDS)))); + .setIssueUpdateDate(Date.from(now.plus(2, ChronoUnit.SECONDS)))); db.issues().insertIssue(IssueTesting.newIssue(rule, branch2Dto, fileOnBranch2Dto).setKee("issue2").setStatus(Issue.STATUS_OPEN).setLine(1).setChecksum("checksum") - .setIssueCreationDate(Date.from(now.plus(1, ChronoUnit.SECONDS)))); + .setIssueUpdateDate(Date.from(now.plus(1, ChronoUnit.SECONDS)))); db.issues().insertIssue(IssueTesting.newIssue(rule, branch3Dto, fileOnBranch3Dto).setKee("issue3").setStatus(Issue.STATUS_OPEN).setLine(1).setChecksum("checksum") - .setIssueCreationDate(Date.from(now))); + .setIssueUpdateDate(Date.from(now))); DefaultIssue newIssue = createIssue("newIssue", rule.getKey(), Issue.STATUS_OPEN, null, new Date()); copier.tryMerge(FILE_1, Collections.singleton(newIssue)); ArgumentCaptor issueToMerge = ArgumentCaptor.forClass(DefaultIssue.class); - verify(issueLifecycle).mergeConfirmedOrResolvedFromShortLivingBranch(eq(newIssue), issueToMerge.capture(), eq("myBranch3")); + verify(issueLifecycle).mergeConfirmedOrResolvedFromShortLivingBranch(eq(newIssue), issueToMerge.capture(), eq("myBranch1")); - assertThat(issueToMerge.getValue().key()).isEqualTo("issue3"); + assertThat(issueToMerge.getValue().key()).isEqualTo("issue1"); } @Test diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java index eca30f4dcd1..d065136abab 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java @@ -364,8 +364,8 @@ public class ComponentDao implements Dao { return mapper(dbSession).selectProjectsByNameQuery(nameQueryForSql, includeModules); } - public List selectComponentKeysHavingIssuesToMerge(DbSession dbSession, String mergeBranchUuid) { - return mapper(dbSession).selectComponentKeysHavingIssuesToMerge(mergeBranchUuid); + public List selectAllSiblingComponentKeysHavingOpenIssues(DbSession dbSession, String referenceBranchUuid, String currentBranchUuid) { + return mapper(dbSession).selectAllSiblingComponentKeysHavingOpenIssues(referenceBranchUuid, currentBranchUuid); } /** diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java index fac6f2c06da..b8045d385c4 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java @@ -166,7 +166,8 @@ public interface ComponentMapper { void updateTags(ComponentDto component); - List selectComponentKeysHavingIssuesToMerge(@Param("mergeBranchUuid") String mergeBranchUuid); + List selectAllSiblingComponentKeysHavingOpenIssues(@Param("referenceBranchUuid") String referenceBranchUuid, + @Param("currentBranchUuid") String currentBranchUuid); List selectPrivateProjectsWithNcloc(@Param("organizationUuid") String organizationUuid); diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/ShortBranchIssueDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/ShortBranchIssueDto.java index 623c790b10f..7876c944ab3 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/ShortBranchIssueDto.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/ShortBranchIssueDto.java @@ -33,7 +33,7 @@ public final class ShortBranchIssueDto implements Serializable { private Integer line; private String checksum; private String status; - private Long issueCreationDate; + private Long issueUpdateDate; // joins private String ruleKey; @@ -109,12 +109,12 @@ public final class ShortBranchIssueDto implements Serializable { return RuleKey.of(ruleRepo, ruleKey); } - public Long getIssueCreationDate() { - return issueCreationDate; + public Long getIssueUpdateDate() { + return issueUpdateDate; } - public ShortBranchIssueDto setIssueCreationDate(Long issueCreationDate) { - this.issueCreationDate = issueCreationDate; + public ShortBranchIssueDto setIssueUpdateDate(Long issueUpdateDate) { + this.issueUpdateDate = issueUpdateDate; return this; } diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml index 90ec7a99a7e..f0c7c129f09 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml @@ -816,14 +816,15 @@ DELETE FROM projects WHERE id=#{id,jdbcType=BIGINT} - SELECT DISTINCT p.kee as kee, p.uuid as uuid FROM projects p JOIN issues i ON p.uuid = i.component_uuid JOIN project_branches b ON i.project_uuid = b.uuid AND (b.branch_type = 'SHORT' OR b.branch_type = 'PULL_REQUEST') - AND b.merge_branch_uuid = #{mergeBranchUuid,jdbcType=VARCHAR} + AND b.merge_branch_uuid = #{referenceBranchUuid,jdbcType=VARCHAR} + AND b.uuid != #{currentBranchUuid,jdbcType=VARCHAR} AND i.status != 'CLOSED' diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml index cf9958a1e50..b636849ce36 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml @@ -290,7 +290,7 @@ i.line as line, i.status as status, i.checksum as checksum, - i.issue_creation_date as issueCreationDate, + i.issue_update_date as issueUpdateDate, r.plugin_rule_key as ruleKey, r.plugin_name as ruleRepo, b.kee as branchName diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java index 28263acb141..fbc50aea6bf 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java @@ -235,7 +235,7 @@ public class IssueDaoTest { assertThat(fp.getRuleKey()).isNotNull(); assertThat(fp.getStatus()).isNotNull(); assertThat(fp.getBranchName()).isEqualTo("feature/foo"); - assertThat(fp.getIssueCreationDate()).isNotNull(); + assertThat(fp.getIssueUpdateDate()).isNotNull(); } @Test diff --git a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java b/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java index 4bfa55b9137..af8649794d8 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java @@ -647,7 +647,7 @@ public class DefaultIssue implements Issue, Trackable, org.sonar.api.ce.measure. } @Override - public Date getCreationDate() { - return creationDate; + public Date getUpdateDate() { + return updateDate; } } diff --git a/sonar-core/src/main/java/org/sonar/core/issue/tracking/AbstractTracker.java b/sonar-core/src/main/java/org/sonar/core/issue/tracking/AbstractTracker.java index 5d1c048a797..53473607f24 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/tracking/AbstractTracker.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/tracking/AbstractTracker.java @@ -46,8 +46,8 @@ public class AbstractTracker { SearchKey rawKey = searchKeyFactory.apply(raw); Collection bases = baseSearch.get(rawKey); bases.stream() - .sorted(comparing(this::statusRank).reversed() - .thenComparing(comparing(Trackable::getCreationDate))) + // Choose the more recently updated issue first to get the latest changes in siblings + .sorted(comparing(Trackable::getUpdateDate).reversed()) .findFirst() .ifPresent(match -> { tracking.match(raw, match); diff --git a/sonar-core/src/main/java/org/sonar/core/issue/tracking/Trackable.java b/sonar-core/src/main/java/org/sonar/core/issue/tracking/Trackable.java index da1891668d5..bda8f9c6061 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/tracking/Trackable.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/tracking/Trackable.java @@ -46,7 +46,7 @@ public interface Trackable { String getStatus(); /** - * Functional creation date for the issue. See {@link DefaultIssue#creationDate()} + * Functional update date for the issue. See {@link DefaultIssue#updateDate()} */ - Date getCreationDate(); + Date getUpdateDate(); } diff --git a/sonar-core/src/test/java/org/sonar/core/issue/tracking/TrackerTest.java b/sonar-core/src/test/java/org/sonar/core/issue/tracking/TrackerTest.java index ac612407280..95a42d72db7 100644 --- a/sonar-core/src/test/java/org/sonar/core/issue/tracking/TrackerTest.java +++ b/sonar-core/src/test/java/org/sonar/core/issue/tracking/TrackerTest.java @@ -454,14 +454,14 @@ public class TrackerTest { private final Integer line; private final String message, lineHash; private final String status; - private final Date creationDate; + private final Date updateDate; - Issue(@Nullable Integer line, String lineHash, RuleKey ruleKey, String message, String status, Date creationDate) { + Issue(@Nullable Integer line, String lineHash, RuleKey ruleKey, String message, String status, Date updateDate) { this.line = line; this.lineHash = lineHash; this.ruleKey = ruleKey; this.status = status; - this.creationDate = creationDate; + this.updateDate = updateDate; this.message = trim(message); } @@ -491,8 +491,8 @@ public class TrackerTest { } @Override - public Date getCreationDate() { - return creationDate; + public Date getUpdateDate() { + return updateDate; } } -- 2.39.5