Browse Source

SONAR-11858 Apply light issue tracking with siblings for all branches

tags/7.8
Julien HENRY 5 years ago
parent
commit
afe312e4cb
21 changed files with 334 additions and 316 deletions
  1. 11
    6
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/SiblingComponentsWithOpenIssues.java
  2. 6
    6
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java
  3. 3
    5
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IntegrateIssuesVisitor.java
  4. 1
    1
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueLifecycle.java
  5. 7
    7
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/SiblingIssue.java
  6. 14
    15
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/SiblingsIssueMerger.java
  7. 14
    14
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/SiblingsIssuesLoader.java
  8. 0
    196
      server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ShortBranchComponentsWithIssuesTest.java
  9. 229
    0
      server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/SiblingComponentsWithOpenIssuesTest.java
  10. 1
    1
      server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IntegrateIssuesVisitorTest.java
  11. 23
    42
      server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/SiblingsIssueMergerTest.java
  12. 2
    2
      server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java
  13. 2
    1
      server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java
  14. 5
    5
      server/sonar-db-dao/src/main/java/org/sonar/db/issue/ShortBranchIssueDto.java
  15. 3
    2
      server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml
  16. 1
    1
      server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml
  17. 1
    1
      server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java
  18. 2
    2
      sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java
  19. 2
    2
      sonar-core/src/main/java/org/sonar/core/issue/tracking/AbstractTracker.java
  20. 2
    2
      sonar-core/src/main/java/org/sonar/core/issue/tracking/Trackable.java
  21. 5
    5
      sonar-core/src/test/java/org/sonar/core/issue/tracking/TrackerTest.java

server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/ShortBranchComponentsWithIssues.java → server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/SiblingComponentsWithOpenIssues.java View File

@@ -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<String, Set<String>> 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<KeyWithUuidDto> components = dbClient.componentDao().selectComponentKeysHavingIssuesToMerge(dbSession, uuid);
List<KeyWithUuidDto> 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());
}

+ 6
- 6
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java View File

@@ -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,

+ 3
- 5
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IntegrateIssuesVisitor.java View File

@@ -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);

+ 1
- 1
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueLifecycle.java View File

@@ -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) {

server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/ShortBranchIssue.java → server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/SiblingIssue.java View File

@@ -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);
}


server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/ShortBranchIssueMerger.java → server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/SiblingsIssueMerger.java View File

@@ -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<DefaultIssue, ShortBranchIssue> tracker;
public class SiblingsIssueMerger {
private final SiblingsIssuesLoader siblingsIssuesLoader;
private final SimpleTracker<DefaultIssue, SiblingIssue> 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<DefaultIssue, ShortBranchIssue> tracker, IssueLifecycle issueLifecycle) {
this.shortBranchIssuesLoader = shortBranchIssuesLoader;
public SiblingsIssueMerger(SiblingsIssuesLoader siblingsIssuesLoader, SimpleTracker<DefaultIssue, SiblingIssue> 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<DefaultIssue> newIssues) {
Collection<ShortBranchIssue> shortBranchIssues = shortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component);
Tracking<DefaultIssue, ShortBranchIssue> tracking = tracker.track(newIssues, shortBranchIssues);
Collection<SiblingIssue> siblingIssues = siblingsIssuesLoader.loadCandidateSiblingIssuesForMerging(component);
Tracking<DefaultIssue, SiblingIssue> tracking = tracker.track(newIssues, siblingIssues);

Map<DefaultIssue, ShortBranchIssue> matchedRaws = tracking.getMatchedRaws();
Map<DefaultIssue, SiblingIssue> matchedRaws = tracking.getMatchedRaws();

Map<ShortBranchIssue, DefaultIssue> defaultIssues = shortBranchIssuesLoader.loadDefaultIssuesWithChanges(matchedRaws.values());
Map<SiblingIssue, DefaultIssue> defaultIssues = siblingsIssuesLoader.loadDefaultIssuesWithChanges(matchedRaws.values());

for (Map.Entry<DefaultIssue, ShortBranchIssue> e : matchedRaws.entrySet()) {
ShortBranchIssue issue = e.getValue();
for (Map.Entry<DefaultIssue, SiblingIssue> e : matchedRaws.entrySet()) {
SiblingIssue issue = e.getValue();
issueLifecycle.mergeConfirmedOrResolvedFromShortLivingBranch(e.getKey(), defaultIssues.get(issue), issue.getBranchName());
}
}

server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/ShortBranchIssuesLoader.java → server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/SiblingsIssuesLoader.java View File

@@ -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<ShortBranchIssue> loadCandidateIssuesForMergingInTargetBranch(Component component) {
public Collection<SiblingIssue> loadCandidateSiblingIssuesForMerging(Component component) {
String componentKey = ComponentDto.removeBranchAndPullRequestFromKey(component.getDbKey());
Set<String> uuids = shortBranchComponentsWithIssues.getUuids(componentKey);
Set<String> 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<ShortBranchIssue, DefaultIssue> loadDefaultIssuesWithChanges(Collection<ShortBranchIssue> lightIssues) {
public Map<SiblingIssue, DefaultIssue> loadDefaultIssuesWithChanges(Collection<SiblingIssue> lightIssues) {
if (lightIssues.isEmpty()) {
return Collections.emptyMap();
}

Map<String, ShortBranchIssue> issuesByKey = lightIssues.stream().collect(Collectors.toMap(ShortBranchIssue::getKey, i -> i));
Map<String, SiblingIssue> issuesByKey = lightIssues.stream().collect(Collectors.toMap(SiblingIssue::getKey, i -> i));
try (DbSession session = dbClient.openSession(false)) {
List<DefaultIssue> issues = dbClient.issueDao().selectByKeys(session, issuesByKey.keySet())
.stream()

+ 0
- 196
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ShortBranchComponentsWithIssuesTest.java View File

@@ -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);
}
}

+ 229
- 0
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/SiblingComponentsWithOpenIssuesTest.java View File

@@ -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);
}
}

+ 1
- 1
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IntegrateIssuesVisitorTest.java View File

@@ -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);

server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/ShortBranchIssueMergerTest.java → server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/SiblingsIssueMergerTest.java View File

@@ -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<DefaultIssue, ShortBranchIssue> tracker = new SimpleTracker<>();
private ShortBranchIssueMerger copier;
private SimpleTracker<DefaultIssue, SiblingIssue> 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<DefaultIssue> 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<DefaultIssue> 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<DefaultIssue> 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

+ 2
- 2
server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java View File

@@ -364,8 +364,8 @@ public class ComponentDao implements Dao {
return mapper(dbSession).selectProjectsByNameQuery(nameQueryForSql, includeModules);
}

public List<KeyWithUuidDto> selectComponentKeysHavingIssuesToMerge(DbSession dbSession, String mergeBranchUuid) {
return mapper(dbSession).selectComponentKeysHavingIssuesToMerge(mergeBranchUuid);
public List<KeyWithUuidDto> selectAllSiblingComponentKeysHavingOpenIssues(DbSession dbSession, String referenceBranchUuid, String currentBranchUuid) {
return mapper(dbSession).selectAllSiblingComponentKeysHavingOpenIssues(referenceBranchUuid, currentBranchUuid);
}

/**

+ 2
- 1
server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java View File

@@ -166,7 +166,8 @@ public interface ComponentMapper {

void updateTags(ComponentDto component);

List<KeyWithUuidDto> selectComponentKeysHavingIssuesToMerge(@Param("mergeBranchUuid") String mergeBranchUuid);
List<KeyWithUuidDto> selectAllSiblingComponentKeysHavingOpenIssues(@Param("referenceBranchUuid") String referenceBranchUuid,
@Param("currentBranchUuid") String currentBranchUuid);

List<ProjectNclocDistributionDto> selectPrivateProjectsWithNcloc(@Param("organizationUuid") String organizationUuid);


+ 5
- 5
server/sonar-db-dao/src/main/java/org/sonar/db/issue/ShortBranchIssueDto.java View File

@@ -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;
}


+ 3
- 2
server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml View File

@@ -816,14 +816,15 @@
DELETE FROM projects WHERE id=#{id,jdbcType=BIGINT}
</delete>

<select id="selectComponentKeysHavingIssuesToMerge" resultType="KeyWithUuid">
<select id="selectAllSiblingComponentKeysHavingOpenIssues" resultType="KeyWithUuid">
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'
</select>


+ 1
- 1
server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml View File

@@ -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

+ 1
- 1
server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java View File

@@ -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

+ 2
- 2
sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java View File

@@ -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;
}
}

+ 2
- 2
sonar-core/src/main/java/org/sonar/core/issue/tracking/AbstractTracker.java View File

@@ -46,8 +46,8 @@ public class AbstractTracker<RAW extends Trackable, BASE extends Trackable> {
SearchKey rawKey = searchKeyFactory.apply(raw);
Collection<BASE> 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);

+ 2
- 2
sonar-core/src/main/java/org/sonar/core/issue/tracking/Trackable.java View File

@@ -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();
}

+ 5
- 5
sonar-core/src/test/java/org/sonar/core/issue/tracking/TrackerTest.java View File

@@ -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;
}
}


Loading…
Cancel
Save