import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import org.sonar.api.issue.Issue;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.utils.System2;
import org.sonar.db.DbTester;
RuleDefinitionDto rule = db.rules().insert();
ComponentDto project = db.components().insertPrivateProject();
ComponentDto file = db.components().insertComponent(newFileDto(project));
- IssueDto openIssue = db.issues().insert(rule, project, file, i -> i.setStatus("OPEN").setResolution(null));
- IssueDto closedIssue = db.issues().insert(rule, project, file, i -> i.setStatus("CLOSED").setResolution("FIXED"));
- IssueDto reopenedIssue = db.issues().insert(rule, project, file, i -> i.setStatus("REOPENED").setResolution(null));
- IssueDto confirmedIssue = db.issues().insert(rule, project, file, i -> i.setStatus("CONFIRMED").setResolution(null));
- IssueDto wontfixIssue = db.issues().insert(rule, project, file, i -> i.setStatus("RESOLVED").setResolution("WONTFIX"));
- IssueDto fpIssue = db.issues().insert(rule, project, file, i -> i.setStatus("RESOLVED").setResolution("FALSE-POSITIVE"));
+ IssueDto openIssue = db.issues().insert(rule, project, file, i -> i.setStatus(Issue.STATUS_OPEN).setResolution(null));
+ IssueDto closedIssue = db.issues().insert(rule, project, file, i -> i.setStatus(Issue.STATUS_CLOSED).setResolution(Issue.RESOLUTION_FIXED));
+ IssueDto reopenedIssue = db.issues().insert(rule, project, file, i -> i.setStatus(Issue.STATUS_REOPENED).setResolution(null));
+ IssueDto confirmedIssue = db.issues().insert(rule, project, file, i -> i.setStatus(Issue.STATUS_CONFIRMED).setResolution(null));
+ IssueDto wontfixIssue = db.issues().insert(rule, project, file, i -> i.setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_WONT_FIX));
+ IssueDto fpIssue = db.issues().insert(rule, project, file, i -> i.setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_FALSE_POSITIVE));
assertThat(underTest.selectResolvedOrConfirmedByComponentUuids(db.getSession(), Collections.singletonList(file.uuid())))
.extracting("kee")
import org.sonar.server.computation.task.projectanalysis.issue.IssueCounter;
import org.sonar.server.computation.task.projectanalysis.issue.IssueCreationDateCalculator;
import org.sonar.server.computation.task.projectanalysis.issue.IssueLifecycle;
-import org.sonar.server.computation.task.projectanalysis.issue.ShortBranchIssueStatusCopier;
+import org.sonar.server.computation.task.projectanalysis.issue.ShortBranchIssueMerger;
import org.sonar.server.computation.task.projectanalysis.issue.IssueTrackingDelegator;
import org.sonar.server.computation.task.projectanalysis.issue.IssueVisitors;
import org.sonar.server.computation.task.projectanalysis.issue.IssuesRepositoryVisitor;
IssueTrackingDelegator.class,
BranchPersister.class,
ShortBranchIssuesLoader.class,
- ShortBranchIssueStatusCopier.class,
+ ShortBranchIssueMerger.class,
// filemove
SourceSimilarityImpl.class,
import java.util.ArrayList;
import java.util.List;
-
+import java.util.Map;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.RuleStatus;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
+import org.sonar.db.issue.IssueChangeDto;
import org.sonar.db.issue.IssueMapper;
import org.sonar.server.computation.task.projectanalysis.qualityprofile.ActiveRulesHolder;
+import static java.util.Collections.emptyList;
+import static java.util.stream.Collectors.groupingBy;
+import static java.util.stream.Collectors.toList;
+
public class ComponentIssuesLoader {
private final DbClient dbClient;
private final RuleRepository ruleRepository;
public List<DefaultIssue> loadForComponentUuid(String componentUuid) {
try (DbSession dbSession = dbClient.openSession(false)) {
- List<DefaultIssue> result = new ArrayList<>();
- dbSession.getMapper(IssueMapper.class).scrollNonClosedByComponentUuid(componentUuid, resultContext -> {
- DefaultIssue issue = (resultContext.getResultObject()).toDefaultIssue();
+ return loadForComponentUuid(componentUuid, dbSession);
+ }
+ }
+
+ public List<DefaultIssue> loadForComponentUuidWithChanges(String componentUuid) {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ List<DefaultIssue> result = loadForComponentUuid(componentUuid, dbSession);
- // TODO this field should be set outside this class
- if (!isActive(issue.ruleKey()) || ruleRepository.getByKey(issue.ruleKey()).getStatus() == RuleStatus.REMOVED) {
- issue.setOnDisabledRule(true);
- // TODO to be improved, why setOnDisabledRule(true) is not enough ?
- issue.setBeingClosed(true);
- }
- // FIXME
- issue.setSelectedAt(System.currentTimeMillis());
- result.add(issue);
- });
- return result;
+ Map<String, List<IssueChangeDto>> changeDtoByIssueKey = dbClient.issueChangeDao()
+ .selectByIssueKeys(dbSession, result.stream().map(DefaultIssue::key).collect(toList()))
+ .stream()
+ .collect(groupingBy(IssueChangeDto::getIssueKey));
+
+ return result
+ .stream()
+ .peek(i -> setChanges(changeDtoByIssueKey, i))
+ .collect(toList());
}
}
+ private List<DefaultIssue> loadForComponentUuid(String componentUuid, DbSession dbSession) {
+ List<DefaultIssue> result = new ArrayList<>();
+ dbSession.getMapper(IssueMapper.class).scrollNonClosedByComponentUuid(componentUuid, resultContext -> {
+ DefaultIssue issue = (resultContext.getResultObject()).toDefaultIssue();
+
+ // TODO this field should be set outside this class
+ if (!isActive(issue.ruleKey()) || ruleRepository.getByKey(issue.ruleKey()).getStatus() == RuleStatus.REMOVED) {
+ issue.setOnDisabledRule(true);
+ // TODO to be improved, why setOnDisabledRule(true) is not enough ?
+ issue.setBeingClosed(true);
+ }
+ // FIXME
+ issue.setSelectedAt(System.currentTimeMillis());
+ result.add(issue);
+ });
+ return result;
+ }
+
+ public static void setChanges(Map<String, List<IssueChangeDto>> changeDtoByIssueKey, DefaultIssue i) {
+ changeDtoByIssueKey.computeIfAbsent(i.key(), k -> emptyList()).forEach(c -> {
+ switch (c.getChangeType()) {
+ case IssueChangeDto.TYPE_FIELD_CHANGE:
+ i.addChange(c.toFieldDiffs());
+ break;
+ case IssueChangeDto.TYPE_COMMENT:
+ i.addComment(c.toComment());
+ break;
+ default:
+ throw new IllegalStateException("Unknow change type: " + c.getChangeType());
+ }
+ });
+ }
+
private boolean isActive(RuleKey ruleKey) {
return activeRulesHolder.get(ruleKey).isPresent();
}
private final IssueLifecycle issueLifecycle;
private final IssueVisitors issueVisitors;
private final IssueTrackingDelegator issueTracking;
- private final ShortBranchIssueStatusCopier issueStatusCopier;
+ private final ShortBranchIssueMerger issueStatusCopier;
private final AnalysisMetadataHolder analysisMetadataHolder;
public IntegrateIssuesVisitor(IssueCache issueCache, IssueLifecycle issueLifecycle, IssueVisitors issueVisitors,
- AnalysisMetadataHolder analysisMetadataHolder, IssueTrackingDelegator issueTracking, ShortBranchIssueStatusCopier issueStatusCopier) {
+ AnalysisMetadataHolder analysisMetadataHolder, IssueTrackingDelegator issueTracking, ShortBranchIssueMerger issueStatusCopier) {
super(CrawlerDepthLimit.FILE, POST_ORDER);
this.issueCache = issueCache;
this.issueLifecycle = issueLifecycle;
}
if (analysisMetadataHolder.isLongLivingBranch()) {
- issueStatusCopier.updateStatus(component, list);
+ issueStatusCopier.tryMerge(component, list);
}
for (DefaultIssue issue : list) {
public void copyExistingOpenIssueFromLongLivingBranch(DefaultIssue raw, DefaultIssue base) {
raw.setKey(Uuids.create());
raw.setNew(false);
- raw.setCopied(true);
- copyFields(raw, base);
+ copyIssueAttributes(raw, base);
+ }
- if (base.manualSeverity()) {
- raw.setManualSeverity(true);
- raw.setSeverity(base.severity());
+ public void copyIssueAttributes(DefaultIssue to, DefaultIssue from) {
+ to.setCopied(true);
+ copyFields(to, from);
+ if (from.manualSeverity()) {
+ to.setManualSeverity(true);
+ to.setSeverity(from.severity());
}
+ copyChanges(to, from);
}
- public void mergeIssueFromShortLivingBranch(DefaultIssue raw, DefaultIssue fromShortLiving) {
- raw.setCopied(true);
- raw.setType(fromShortLiving.type());
- raw.setResolution(fromShortLiving.resolution());
- raw.setStatus(fromShortLiving.status());
- raw.setAssignee(fromShortLiving.assignee());
- raw.setAuthorLogin(fromShortLiving.authorLogin());
- raw.setTags(fromShortLiving.tags());
- raw.setAttributes(fromShortLiving.attributes());
- if (fromShortLiving.manualSeverity()) {
- raw.setManualSeverity(true);
- raw.setSeverity(fromShortLiving.severity());
- }
- fromShortLiving.comments().forEach(c -> raw.addComment(DefaultIssueComment.copy(raw.key(), c)));
- fromShortLiving.changes().forEach(c -> raw.addChange(FieldDiffs.copy(raw.key(), c)));
+ private static void copyChanges(DefaultIssue raw, DefaultIssue base) {
+ base.comments().forEach(c -> raw.addComment(DefaultIssueComment.copy(raw.key(), c)));
+ base.changes().forEach(c -> raw.addChange(FieldDiffs.copy(raw.key(), c)));
}
public void mergeExistingOpenIssue(DefaultIssue raw, DefaultIssue base) {
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.server.computation.task.projectanalysis.issue;
+
+import java.util.Collection;
+import java.util.Map;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.ShortBranchIssue;
+import org.sonar.core.issue.tracking.SimpleTracker;
+import org.sonar.core.issue.tracking.Tracking;
+import org.sonar.server.computation.task.projectanalysis.component.Component;
+
+public class ShortBranchIssueMerger {
+ private final ShortBranchIssuesLoader shortBranchIssuesLoader;
+ private final SimpleTracker<DefaultIssue, ShortBranchIssue> tracker;
+ private final IssueLifecycle issueLifecycle;
+
+ public ShortBranchIssueMerger(ShortBranchIssuesLoader resolvedShortBranchIssuesLoader, IssueLifecycle issueLifecycle) {
+ this(resolvedShortBranchIssuesLoader, new SimpleTracker<>(), issueLifecycle);
+ }
+
+ public ShortBranchIssueMerger(ShortBranchIssuesLoader shortBranchIssuesLoader, SimpleTracker<DefaultIssue, ShortBranchIssue> tracker, IssueLifecycle issueLifecycle) {
+ this.shortBranchIssuesLoader = shortBranchIssuesLoader;
+ this.tracker = tracker;
+ this.issueLifecycle = issueLifecycle;
+ }
+
+ /**
+ * Look for all resolved/confirmed issues in short living branches targeting the current 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);
+
+ Map<DefaultIssue, ShortBranchIssue> matchedRaws = tracking.getMatchedRaws();
+
+ Map<ShortBranchIssue, DefaultIssue> defaultIssues = shortBranchIssuesLoader.loadDefaultIssuesWithChanges(matchedRaws.values());
+
+ for (Map.Entry<DefaultIssue, ShortBranchIssue> e : matchedRaws.entrySet()) {
+ ShortBranchIssue issue = e.getValue();
+ issueLifecycle.copyIssueAttributes(e.getKey(), defaultIssues.get(issue));
+ }
+ }
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.server.computation.task.projectanalysis.issue;
-
-import java.util.Collection;
-import java.util.Map;
-import org.sonar.core.issue.DefaultIssue;
-import org.sonar.core.issue.ShortBranchIssue;
-import org.sonar.core.issue.tracking.SimpleTracker;
-import org.sonar.core.issue.tracking.Tracking;
-import org.sonar.server.computation.task.projectanalysis.component.Component;
-
-public class ShortBranchIssueStatusCopier {
- private final ShortBranchIssuesLoader shortBranchIssuesLoader;
- private final SimpleTracker<DefaultIssue, ShortBranchIssue> tracker;
- private final IssueLifecycle issueLifecycle;
-
- public ShortBranchIssueStatusCopier(ShortBranchIssuesLoader resolvedShortBranchIssuesLoader, IssueLifecycle issueLifecycle) {
- this(resolvedShortBranchIssuesLoader, new SimpleTracker<>(), issueLifecycle);
- }
-
- public ShortBranchIssueStatusCopier(ShortBranchIssuesLoader shortBranchIssuesLoader, SimpleTracker<DefaultIssue, ShortBranchIssue> tracker, IssueLifecycle issueLifecycle) {
- this.shortBranchIssuesLoader = shortBranchIssuesLoader;
- this.tracker = tracker;
- this.issueLifecycle = issueLifecycle;
- }
-
- public void updateStatus(Component component, Collection<DefaultIssue> newIssues) {
- Collection<ShortBranchIssue> shortBranchIssues = shortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component);
- Tracking<DefaultIssue, ShortBranchIssue> tracking = tracker.track(newIssues, shortBranchIssues);
-
- Map<DefaultIssue, ShortBranchIssue> matchedRaws = tracking.getMatchedRaws();
-
- Map<ShortBranchIssue, DefaultIssue> defaultIssues = shortBranchIssuesLoader.loadDefaultIssuesWithChanges(matchedRaws.values());
-
- for (Map.Entry<DefaultIssue, ShortBranchIssue> e : matchedRaws.entrySet()) {
- ShortBranchIssue issue = e.getValue();
- issueLifecycle.mergeIssueFromShortLivingBranch(e.getKey(), defaultIssues.get(issue));
- }
- }
-}
return dbClient.issueDao().selectByKeys(session, issuesByKey.keySet())
.stream()
.map(IssueDto::toDefaultIssue)
- .peek(i -> setChanges(changeDtoByIssueKey, i))
+ .peek(i -> ComponentIssuesLoader.setChanges(changeDtoByIssueKey, i))
.collect(toMap(i -> issuesByKey.get(i.key()), i -> i));
}
}
- private static void setChanges(Map<String, List<IssueChangeDto>> changeDtoByIssueKey, DefaultIssue i) {
- changeDtoByIssueKey.get(i.key()).forEach(c -> {
- switch (c.getChangeType()) {
- case IssueChangeDto.TYPE_FIELD_CHANGE:
- i.addChange(c.toFieldDiffs());
- break;
- case IssueChangeDto.TYPE_COMMENT:
- i.addComment(c.toComment());
- break;
- default:
- throw new IllegalStateException("Unknow change type: " + c.getChangeType());
- }
- });
- }
}
if (mergeBranchComponentUuid == null) {
return Collections.emptyList();
}
- return mergeIssuesLoader.loadForComponentUuid(mergeBranchComponentUuid);
+ return mergeIssuesLoader.loadForComponentUuidWithChanges(mergeBranchComponentUuid);
}
}
}
}
FieldDiffs diffs = issue.currentChange();
- if (!issue.isNew() && diffs != null) {
- IssueChangeDto changeDto = IssueChangeDto.of(issue.key(), diffs);
- mapper.insert(changeDto);
- } else if (issue.isCopied()) {
+ if (issue.isCopied()) {
for (FieldDiffs d : issue.changes()) {
IssueChangeDto changeDto = IssueChangeDto.of(issue.key(), d);
mapper.insert(changeDto);
}
+ } else if (!issue.isNew() && diffs != null) {
+ IssueChangeDto changeDto = IssueChangeDto.of(issue.key(), diffs);
+ mapper.insert(changeDto);
}
}
@Before
public void setUp() throws Exception {
issueCache = new IssueCache(temp.newFile(), System2.INSTANCE);
- underTest = new VisitorsCrawler(Arrays.<ComponentVisitor>asList(new CloseIssuesOnRemovedComponentsVisitor(issuesLoader, componentsWithUnprocessedIssues, issueCache, issueLifecycle)));
+ underTest = new VisitorsCrawler(
+ Arrays.<ComponentVisitor>asList(new CloseIssuesOnRemovedComponentsVisitor(issuesLoader, componentsWithUnprocessedIssues, issueCache, issueLifecycle)));
}
@Test
import org.sonar.core.issue.DefaultIssue;
import org.sonar.core.issue.tracking.Tracker;
import org.sonar.db.DbTester;
+import org.sonar.db.component.BranchType;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
import org.sonar.db.issue.IssueDto;
import org.sonar.scanner.protocol.Constants;
import org.sonar.scanner.protocol.output.ScannerReport;
import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolder;
+import org.sonar.server.computation.task.projectanalysis.analysis.Branch;
import org.sonar.server.computation.task.projectanalysis.batch.BatchReportReaderRule;
import org.sonar.server.computation.task.projectanalysis.component.Component;
import org.sonar.server.computation.task.projectanalysis.component.MergeBranchComponentUuids;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.sonar.server.computation.task.projectanalysis.component.ReportComponent.builder;
public class IntegrateIssuesVisitorTest {
static final String FILE_UUID = "FILE_UUID";
+ static final String FILE_UUID_ON_BRANCH = "FILE_UUID_BRANCH";
static final String FILE_KEY = "FILE_KEY";
static final int FILE_REF = 2;
static final String PROJECT_KEY = "PROJECT_KEY";
static final String PROJECT_UUID = "PROJECT_UUID";
+ static final String PROJECT_UUID_ON_BRANCH = "PROJECT_UUID_BRANCH";
static final int PROJECT_REF = 1;
static final Component PROJECT = builder(Component.Type.PROJECT, PROJECT_REF)
.setKey(PROJECT_KEY)
@Mock
private MergeBranchComponentUuids mergeBranchComponentsUuids;
@Mock
- private ShortBranchIssueStatusCopier issueStatusCopier;
+ private ShortBranchIssueMerger issueStatusCopier;
ArgumentCaptor<DefaultIssue> defaultIssueCaptor;
DefaultIssue capturedIssue = defaultIssueCaptor.getValue();
assertThat(capturedIssue.ruleKey().rule()).isEqualTo("S001");
- verify(issueStatusCopier).updateStatus(FILE, Collections.singletonList(capturedIssue));
+ verify(issueStatusCopier).tryMerge(FILE, Collections.singletonList(capturedIssue));
verify(issueLifecycle).doAutomaticTransition(capturedIssue);
underTest.visitAny(FILE);
}
+ @Test
+ public void copy_issues_when_creating_new_long_living_branch() throws Exception {
+
+ when(mergeBranchComponentsUuids.getUuid(FILE_KEY)).thenReturn(FILE_UUID_ON_BRANCH);
+
+ when(analysisMetadataHolder.isLongLivingBranch()).thenReturn(true);
+ when(analysisMetadataHolder.isFirstAnalysis()).thenReturn(true);
+ Branch branch = mock(Branch.class);
+ when(branch.isMain()).thenReturn(false);
+ when(branch.getType()).thenReturn(BranchType.LONG);
+ when(analysisMetadataHolder.getBranch()).thenReturn(java.util.Optional.of(branch));
+
+ RuleKey ruleKey = RuleTesting.XOO_X1;
+ // Issue from main branch has severity major
+ addBaseIssueOnBranch(ruleKey);
+
+ // Issue from report has severity blocker
+ ScannerReport.Issue reportIssue = ScannerReport.Issue.newBuilder()
+ .setMsg("the message")
+ .setRuleRepository(ruleKey.repository())
+ .setRuleKey(ruleKey.rule())
+ .setSeverity(Constants.Severity.BLOCKER)
+ .build();
+ reportReader.putIssues(FILE_REF, asList(reportIssue));
+ fileSourceRepository.addLine(FILE_REF, "line1");
+
+ underTest.visitAny(FILE);
+
+ ArgumentCaptor<DefaultIssue> rawIssueCaptor = ArgumentCaptor.forClass(DefaultIssue.class);
+ ArgumentCaptor<DefaultIssue> baseIssueCaptor = ArgumentCaptor.forClass(DefaultIssue.class);
+ verify(issueLifecycle).copyExistingOpenIssueFromLongLivingBranch(rawIssueCaptor.capture(), baseIssueCaptor.capture());
+ assertThat(rawIssueCaptor.getValue().severity()).isEqualTo(Severity.BLOCKER);
+ assertThat(baseIssueCaptor.getValue().severity()).isEqualTo(Severity.MAJOR);
+
+ verify(issueLifecycle).doAutomaticTransition(defaultIssueCaptor.capture());
+ assertThat(defaultIssueCaptor.getValue().ruleKey()).isEqualTo(ruleKey);
+ List<DefaultIssue> issues = newArrayList(issueCache.traverse());
+ assertThat(issues).hasSize(1);
+ assertThat(issues.get(0).severity()).isEqualTo(Severity.BLOCKER);
+ }
+
private void addBaseIssue(RuleKey ruleKey) {
ComponentDto project = ComponentTesting.newPrivateProjectDto(dbTester.organizations().insert(), PROJECT_UUID).setDbKey(PROJECT_KEY);
ComponentDto file = ComponentTesting.newFileDto(project, null, FILE_UUID).setDbKey(FILE_KEY);
dbTester.getDbClient().issueDao().insert(dbTester.getSession(), issue);
dbTester.getSession().commit();
}
+
+ private void addBaseIssueOnBranch(RuleKey ruleKey) {
+ ComponentDto project = ComponentTesting.newPrivateProjectDto(dbTester.organizations().insert(), PROJECT_UUID_ON_BRANCH).setDbKey(PROJECT_KEY);
+ ComponentDto file = ComponentTesting.newFileDto(project, null, FILE_UUID_ON_BRANCH).setDbKey(FILE_KEY);
+ dbTester.getDbClient().componentDao().insert(dbTester.getSession(), project, file);
+
+ RuleDto ruleDto = RuleTesting.newDto(ruleKey);
+ dbTester.rules().insertRule(ruleDto);
+ ruleRepositoryRule.add(ruleKey);
+
+ IssueDto issue = IssueTesting.newDto(ruleDto, file, project)
+ .setKee("ISSUE")
+ .setStatus(Issue.STATUS_OPEN)
+ .setSeverity(Severity.MAJOR);
+ dbTester.getDbClient().issueDao().insert(dbTester.getSession(), issue);
+ dbTester.getSession().commit();
+ }
}
DefaultIssue fromShort = new DefaultIssue();
fromShort.setResolution("resolution");
fromShort.setStatus("status");
- underTest.mergeIssueFromShortLivingBranch(raw, fromShort);
+ underTest.copyIssueAttributes(raw, fromShort);
assertThat(raw.resolution()).isEqualTo("resolution");
assertThat(raw.status()).isEqualTo("status");
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.server.computation.task.projectanalysis.issue;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import javax.annotation.Nullable;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.ShortBranchIssue;
+import org.sonar.core.issue.tracking.SimpleTracker;
+import org.sonar.server.computation.task.projectanalysis.component.Component;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.anyListOf;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+public class ShortBranchIssueMergerTest {
+ @Mock
+ private ShortBranchIssuesLoader resolvedShortBranchIssuesLoader;
+ @Mock
+ private IssueLifecycle issueLifecycle;
+ @Mock
+ private Component component;
+
+ private SimpleTracker<DefaultIssue, ShortBranchIssue> tracker = new SimpleTracker<>();
+ private ShortBranchIssueMerger copier;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ copier = new ShortBranchIssueMerger(resolvedShortBranchIssuesLoader, tracker, issueLifecycle);
+ }
+
+ @Test
+ public void do_nothing_if_no_match() {
+ when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Collections.emptyList());
+ DefaultIssue i = createIssue("issue1", "rule1", Issue.STATUS_CONFIRMED, null);
+ copier.tryMerge(component, Collections.singleton(i));
+
+ verify(resolvedShortBranchIssuesLoader).loadCandidateIssuesForMergingInTargetBranch(component);
+ verifyZeroInteractions(issueLifecycle);
+ }
+
+ @Test
+ public void do_nothing_if_no_new_issue() {
+ DefaultIssue i = createIssue("issue1", "rule1", Issue.STATUS_CONFIRMED, null);
+ when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Collections.singleton(newShortBranchIssue(i)));
+ copier.tryMerge(component, Collections.emptyList());
+
+ verify(resolvedShortBranchIssuesLoader).loadCandidateIssuesForMergingInTargetBranch(component);
+ verifyZeroInteractions(issueLifecycle);
+ }
+
+ @Test
+ public void update_status_on_matches() {
+ DefaultIssue issue1 = createIssue("issue1", "rule1", Issue.STATUS_CONFIRMED, null);
+ ShortBranchIssue shortBranchIssue = newShortBranchIssue(issue1);
+ DefaultIssue newIssue = createIssue("issue2", "rule1", Issue.STATUS_OPEN, null);
+
+ when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Collections.singleton(shortBranchIssue));
+ when(resolvedShortBranchIssuesLoader.loadDefaultIssuesWithChanges(anyListOf(ShortBranchIssue.class))).thenReturn(ImmutableMap.of(shortBranchIssue, issue1));
+ copier.tryMerge(component, Collections.singleton(newIssue));
+ ArgumentCaptor<Collection> captor = ArgumentCaptor.forClass(Collection.class);
+ verify(resolvedShortBranchIssuesLoader).loadDefaultIssuesWithChanges(captor.capture());
+ assertThat(captor.getValue()).containsOnly(shortBranchIssue);
+ verify(issueLifecycle).copyIssueAttributes(newIssue, issue1);
+ }
+
+ @Test
+ public void prefer_resolved_issues() {
+ ShortBranchIssue shortBranchIssue1 = newShortBranchIssue(createIssue("issue1", "rule1", Issue.STATUS_CONFIRMED, null));
+ ShortBranchIssue shortBranchIssue2 = newShortBranchIssue(createIssue("issue2", "rule1", Issue.STATUS_CONFIRMED, null));
+ DefaultIssue issue3 = createIssue("issue3", "rule1", Issue.STATUS_RESOLVED, Issue.RESOLUTION_FALSE_POSITIVE);
+ ShortBranchIssue shortBranchIssue3 = newShortBranchIssue(issue3);
+ DefaultIssue newIssue = createIssue("newIssue", "rule1", Issue.STATUS_OPEN, null);
+
+ when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Arrays.asList(shortBranchIssue1, shortBranchIssue2, shortBranchIssue3));
+ when(resolvedShortBranchIssuesLoader.loadDefaultIssuesWithChanges(anyListOf(ShortBranchIssue.class))).thenReturn(ImmutableMap.of(shortBranchIssue3, issue3));
+ copier.tryMerge(component, Collections.singleton(newIssue));
+ verify(issueLifecycle).copyIssueAttributes(newIssue, issue3);
+ }
+
+ private static DefaultIssue createIssue(String key, String ruleKey, String status, @Nullable String resolution) {
+ DefaultIssue issue = new DefaultIssue();
+ issue.setKey(key);
+ issue.setRuleKey(RuleKey.of("repo", ruleKey));
+ issue.setMessage("msg");
+ issue.setLine(1);
+ issue.setStatus(status);
+ issue.setResolution(resolution);
+ return issue;
+ }
+
+ private ShortBranchIssue newShortBranchIssue(DefaultIssue i) {
+ return new ShortBranchIssue(i.key(), i.line(), i.message(), i.getLineHash(), i.ruleKey(), i.status());
+ }
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.server.computation.task.projectanalysis.issue;
-
-import com.google.common.collect.ImmutableMap;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import javax.annotation.Nullable;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.sonar.api.issue.Issue;
-import org.sonar.api.rule.RuleKey;
-import org.sonar.core.issue.DefaultIssue;
-import org.sonar.core.issue.ShortBranchIssue;
-import org.sonar.core.issue.tracking.SimpleTracker;
-import org.sonar.server.computation.task.projectanalysis.component.Component;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Matchers.anyListOf;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-
-public class ShortBranchIssueStatusCopierTest {
- @Mock
- private ShortBranchIssuesLoader resolvedShortBranchIssuesLoader;
- @Mock
- private IssueLifecycle issueLifecycle;
- @Mock
- private Component component;
-
- private SimpleTracker<DefaultIssue, ShortBranchIssue> tracker = new SimpleTracker<>();
- private ShortBranchIssueStatusCopier copier;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- copier = new ShortBranchIssueStatusCopier(resolvedShortBranchIssuesLoader, tracker, issueLifecycle);
- }
-
- @Test
- public void do_nothing_if_no_match() {
- when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Collections.emptyList());
- DefaultIssue i = createIssue("issue1", "rule1", Issue.STATUS_CONFIRMED, null);
- copier.updateStatus(component, Collections.singleton(i));
-
- verify(resolvedShortBranchIssuesLoader).loadCandidateIssuesForMergingInTargetBranch(component);
- verifyZeroInteractions(issueLifecycle);
- }
-
- @Test
- public void do_nothing_if_no_new_issue() {
- DefaultIssue i = createIssue("issue1", "rule1", Issue.STATUS_CONFIRMED, null);
- when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Collections.singleton(newShortBranchIssue(i)));
- copier.updateStatus(component, Collections.emptyList());
-
- verify(resolvedShortBranchIssuesLoader).loadCandidateIssuesForMergingInTargetBranch(component);
- verifyZeroInteractions(issueLifecycle);
- }
-
- @Test
- public void update_status_on_matches() {
- DefaultIssue issue1 = createIssue("issue1", "rule1", Issue.STATUS_CONFIRMED, null);
- ShortBranchIssue shortBranchIssue = newShortBranchIssue(issue1);
- DefaultIssue newIssue = createIssue("issue2", "rule1", Issue.STATUS_OPEN, null);
-
- when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Collections.singleton(shortBranchIssue));
- when(resolvedShortBranchIssuesLoader.loadDefaultIssuesWithChanges(anyListOf(ShortBranchIssue.class))).thenReturn(ImmutableMap.of(shortBranchIssue, issue1));
- copier.updateStatus(component, Collections.singleton(newIssue));
- ArgumentCaptor<Collection> captor = ArgumentCaptor.forClass(Collection.class);
- verify(resolvedShortBranchIssuesLoader).loadDefaultIssuesWithChanges(captor.capture());
- assertThat(captor.getValue()).containsOnly(shortBranchIssue);
- verify(issueLifecycle).mergeIssueFromShortLivingBranch(newIssue, issue1);
- }
-
- @Test
- public void prefer_resolved_issues() {
- ShortBranchIssue shortBranchIssue1 = newShortBranchIssue(createIssue("issue1", "rule1", Issue.STATUS_CONFIRMED, null));
- ShortBranchIssue shortBranchIssue2 = newShortBranchIssue(createIssue("issue2", "rule1", Issue.STATUS_CONFIRMED, null));
- DefaultIssue issue3 = createIssue("issue3", "rule1", Issue.STATUS_RESOLVED, Issue.RESOLUTION_FALSE_POSITIVE);
- ShortBranchIssue shortBranchIssue3 = newShortBranchIssue(issue3);
- DefaultIssue newIssue = createIssue("newIssue", "rule1", Issue.STATUS_OPEN, null);
-
- when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Arrays.asList(shortBranchIssue1, shortBranchIssue2, shortBranchIssue3));
- when(resolvedShortBranchIssuesLoader.loadDefaultIssuesWithChanges(anyListOf(ShortBranchIssue.class))).thenReturn(ImmutableMap.of(shortBranchIssue3, issue3));
- copier.updateStatus(component, Collections.singleton(newIssue));
- verify(issueLifecycle).mergeIssueFromShortLivingBranch(newIssue, issue3);
- }
-
- private static DefaultIssue createIssue(String key, String ruleKey, String status, @Nullable String resolution) {
- DefaultIssue issue = new DefaultIssue();
- issue.setKey(key);
- issue.setRuleKey(RuleKey.of("repo", ruleKey));
- issue.setMessage("msg");
- issue.setLine(1);
- issue.setStatus(status);
- issue.setResolution(resolution);
- return issue;
- }
-
- private ShortBranchIssue newShortBranchIssue(DefaultIssue i) {
- return new ShortBranchIssue(i.key(), i.line(), i.message(), i.getLineHash(), i.ruleKey(), i.status());
- }
-}
*/
package org.sonar.server.computation.task.projectanalysis.step;
+import java.util.Arrays;
+import java.util.List;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
+import org.sonar.db.issue.IssueChangeDto;
import org.sonar.db.issue.IssueDto;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.rule.RuleDefinitionDto;
.setStatus(Issue.STATUS_OPEN)
.setNew(false)
.setCopied(true)
- .setType(RuleType.BUG)).close();
+ .setType(RuleType.BUG)
+ .addComment(new DefaultIssueComment()
+ .setKey("COMMENT")
+ .setIssueKey("ISSUE")
+ .setUserLogin("john")
+ .setMarkdownText("Some text")
+ .setNew(true))
+ .setCurrentChange(new FieldDiffs()
+ .setIssueKey("ISSUE")
+ .setUserLogin("john")
+ .setDiff("technicalDebt", null, 1L)))
+ .close();
+
+ step.execute();
+
+ IssueDto result = dbClient.issueDao().selectOrFailByKey(session, "ISSUE");
+ assertThat(result.getKey()).isEqualTo("ISSUE");
+ assertThat(result.getRuleKey()).isEqualTo(rule.getKey());
+ assertThat(result.getComponentUuid()).isEqualTo(file.uuid());
+ assertThat(result.getProjectUuid()).isEqualTo(project.uuid());
+ assertThat(result.getSeverity()).isEqualTo(Severity.BLOCKER);
+ assertThat(result.getStatus()).isEqualTo(Issue.STATUS_OPEN);
+ assertThat(result.getType()).isEqualTo(RuleType.BUG.getDbConstant());
+
+ List<IssueChangeDto> changes = dbClient.issueChangeDao().selectByIssueKeys(session, Arrays.asList("ISSUE"));
+ assertThat(changes).extracting(IssueChangeDto::getChangeType).containsExactly(IssueChangeDto.TYPE_COMMENT, IssueChangeDto.TYPE_FIELD_CHANGE);
+ }
+
+ @Test
+ public void insert_merged_issue() {
+ RuleDefinitionDto rule = RuleTesting.newRule(RuleKey.of("xoo", "S01"));
+ dbTester.rules().insert(rule);
+ OrganizationDto organizationDto = dbTester.organizations().insert();
+ ComponentDto project = ComponentTesting.newPrivateProjectDto(organizationDto);
+ dbClient.componentDao().insert(session, project);
+ ComponentDto file = ComponentTesting.newFileDto(project, null);
+ dbClient.componentDao().insert(session, file);
+ session.commit();
+
+ issueCache.newAppender().append(new DefaultIssue()
+ .setKey("ISSUE")
+ .setType(RuleType.CODE_SMELL)
+ .setRuleKey(rule.getKey())
+ .setComponentUuid(file.uuid())
+ .setProjectUuid(project.uuid())
+ .setSeverity(Severity.BLOCKER)
+ .setStatus(Issue.STATUS_OPEN)
+ .setNew(true)
+ .setCopied(true)
+ .setType(RuleType.BUG)
+ .addComment(new DefaultIssueComment()
+ .setKey("COMMENT")
+ .setIssueKey("ISSUE")
+ .setUserLogin("john")
+ .setMarkdownText("Some text")
+ .setNew(true))
+ .setCurrentChange(new FieldDiffs()
+ .setIssueKey("ISSUE")
+ .setUserLogin("john")
+ .setDiff("technicalDebt", null, 1L)))
+ .close();
step.execute();
assertThat(result.getSeverity()).isEqualTo(Severity.BLOCKER);
assertThat(result.getStatus()).isEqualTo(Issue.STATUS_OPEN);
assertThat(result.getType()).isEqualTo(RuleType.BUG.getDbConstant());
+
+ List<IssueChangeDto> changes = dbClient.issueChangeDao().selectByIssueKeys(session, Arrays.asList("ISSUE"));
+ assertThat(changes).extracting(IssueChangeDto::getChangeType).containsExactly(IssueChangeDto.TYPE_COMMENT, IssueChangeDto.TYPE_FIELD_CHANGE);
}
@Test