When there are multiple candidates (from multiple branches), prefer any of the RESOLVED one, before
taking any of the CONFIRMED ones.
return ruleKey;
}
+ @Override
public String getStatus() {
return status;
}
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.IssueStatusCopier;
+import org.sonar.server.computation.task.projectanalysis.issue.ShortBranchIssueStatusCopier;
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;
import org.sonar.server.computation.task.projectanalysis.issue.MovedIssueVisitor;
import org.sonar.server.computation.task.projectanalysis.issue.NewEffortAggregator;
import org.sonar.server.computation.task.projectanalysis.issue.RemoveProcessedComponentsVisitor;
-import org.sonar.server.computation.task.projectanalysis.issue.ResolvedShortBranchIssuesLoader;
+import org.sonar.server.computation.task.projectanalysis.issue.ShortBranchIssuesLoader;
import org.sonar.server.computation.task.projectanalysis.issue.RuleRepositoryImpl;
import org.sonar.server.computation.task.projectanalysis.issue.RuleTagsCopier;
import org.sonar.server.computation.task.projectanalysis.issue.RuleTypeCopier;
BaseIssuesLoader.class,
IssueTrackingDelegator.class,
BranchPersister.class,
- ResolvedShortBranchIssuesLoader.class,
- IssueStatusCopier.class,
+ ShortBranchIssuesLoader.class,
+ ShortBranchIssueStatusCopier.class,
// filemove
SourceSimilarityImpl.class,
private final IssueLifecycle issueLifecycle;
private final IssueVisitors issueVisitors;
private final IssueTrackingDelegator issueTracking;
- private final IssueStatusCopier issueStatusCopier;
+ private final ShortBranchIssueStatusCopier issueStatusCopier;
private final AnalysisMetadataHolder analysisMetadataHolder;
public IntegrateIssuesVisitor(IssueCache issueCache, IssueLifecycle issueLifecycle, IssueVisitors issueVisitors,
- AnalysisMetadataHolder analysisMetadataHolder, IssueTrackingDelegator issueTracking, IssueStatusCopier issueStatusCopier) {
+ AnalysisMetadataHolder analysisMetadataHolder, IssueTrackingDelegator issueTracking, ShortBranchIssueStatusCopier issueStatusCopier) {
super(CrawlerDepthLimit.FILE, POST_ORDER);
this.issueCache = issueCache;
this.issueLifecycle = issueLifecycle;
+++ /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 IssueStatusCopier {
- private final ResolvedShortBranchIssuesLoader resolvedShortBranchIssuesLoader;
- private final SimpleTracker<DefaultIssue, ShortBranchIssue> tracker;
- private final IssueLifecycle issueLifecycle;
-
- public IssueStatusCopier(ResolvedShortBranchIssuesLoader resolvedShortBranchIssuesLoader, IssueLifecycle issueLifecycle) {
- this(resolvedShortBranchIssuesLoader, new SimpleTracker<>(), issueLifecycle);
- }
-
- public IssueStatusCopier(ResolvedShortBranchIssuesLoader resolvedShortBranchIssuesLoader, SimpleTracker<DefaultIssue, ShortBranchIssue> tracker, IssueLifecycle issueLifecycle) {
- this.resolvedShortBranchIssuesLoader = resolvedShortBranchIssuesLoader;
- this.tracker = tracker;
- this.issueLifecycle = issueLifecycle;
- }
-
- public void updateStatus(Component component, Collection<DefaultIssue> newIssues) {
- Collection<ShortBranchIssue> shortBranchIssues = resolvedShortBranchIssuesLoader.create(component);
- Tracking<DefaultIssue, ShortBranchIssue> tracking = tracker.track(newIssues, shortBranchIssues);
-
- for (Map.Entry<DefaultIssue, ShortBranchIssue> e : tracking.getMatchedRaws().entrySet()) {
- ShortBranchIssue issue = e.getValue();
- issueLifecycle.copyResolution(e.getKey(), issue.getStatus(), issue.getResolution());
- }
- }
-}
+++ /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.Collections;
-import java.util.Set;
-import java.util.stream.Collectors;
-import org.sonar.core.issue.ShortBranchIssue;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.db.component.ComponentDto;
-import org.sonar.db.issue.ShortBranchIssueDto;
-import org.sonar.server.computation.task.projectanalysis.component.Component;
-import org.sonar.server.computation.task.projectanalysis.component.ShortBranchComponentsWithIssues;
-
-public class ResolvedShortBranchIssuesLoader {
-
- private final ShortBranchComponentsWithIssues shortBranchComponentsWithIssues;
- private final DbClient dbClient;
-
- public ResolvedShortBranchIssuesLoader(ShortBranchComponentsWithIssues shortBranchComponentsWithIssues, DbClient dbClient) {
- this.shortBranchComponentsWithIssues = shortBranchComponentsWithIssues;
- this.dbClient = dbClient;
- }
-
- public Collection<ShortBranchIssue> create(Component component) {
- String componentKey = ComponentDto.removeBranchFromKey(component.getKey());
- Set<String> uuids = shortBranchComponentsWithIssues.getUuids(componentKey);
- if (uuids.isEmpty()) {
- return Collections.emptyList();
- }
- try (DbSession session = dbClient.openSession(false)) {
- return dbClient.issueDao().selectResolvedOrConfirmedByComponentUuids(session, uuids)
- .stream()
- .map(ShortBranchIssueDto::toShortBranchIssue)
- .collect(Collectors.toList());
- }
- }
-}
--- /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 resolvedShortBranchIssuesLoader;
+ private final SimpleTracker<DefaultIssue, ShortBranchIssue> tracker;
+ private final IssueLifecycle issueLifecycle;
+
+ public ShortBranchIssueStatusCopier(ShortBranchIssuesLoader resolvedShortBranchIssuesLoader, IssueLifecycle issueLifecycle) {
+ this(resolvedShortBranchIssuesLoader, new SimpleTracker<>(), issueLifecycle);
+ }
+
+ public ShortBranchIssueStatusCopier(ShortBranchIssuesLoader resolvedShortBranchIssuesLoader, SimpleTracker<DefaultIssue, ShortBranchIssue> tracker,
+ IssueLifecycle issueLifecycle) {
+ this.resolvedShortBranchIssuesLoader = resolvedShortBranchIssuesLoader;
+ this.tracker = tracker;
+ this.issueLifecycle = issueLifecycle;
+ }
+
+ public void updateStatus(Component component, Collection<DefaultIssue> newIssues) {
+ Collection<ShortBranchIssue> shortBranchIssues = resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component);
+ Tracking<DefaultIssue, ShortBranchIssue> tracking = tracker.track(newIssues, shortBranchIssues);
+
+ for (Map.Entry<DefaultIssue, ShortBranchIssue> e : tracking.getMatchedRaws().entrySet()) {
+ ShortBranchIssue issue = e.getValue();
+ issueLifecycle.copyResolution(e.getKey(), issue.getStatus(), issue.getResolution());
+ }
+ }
+}
--- /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.Collections;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.sonar.core.issue.ShortBranchIssue;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.issue.ShortBranchIssueDto;
+import org.sonar.server.computation.task.projectanalysis.component.Component;
+import org.sonar.server.computation.task.projectanalysis.component.ShortBranchComponentsWithIssues;
+
+public class ShortBranchIssuesLoader {
+
+ private final ShortBranchComponentsWithIssues shortBranchComponentsWithIssues;
+ private final DbClient dbClient;
+
+ public ShortBranchIssuesLoader(ShortBranchComponentsWithIssues shortBranchComponentsWithIssues, DbClient dbClient) {
+ this.shortBranchComponentsWithIssues = shortBranchComponentsWithIssues;
+ this.dbClient = dbClient;
+ }
+
+ public Collection<ShortBranchIssue> loadCandidateIssuesForMergingInTargetBranch(Component component) {
+ String componentKey = ComponentDto.removeBranchFromKey(component.getKey());
+ Set<String> uuids = shortBranchComponentsWithIssues.getUuids(componentKey);
+ if (uuids.isEmpty()) {
+ return Collections.emptyList();
+ }
+ try (DbSession session = dbClient.openSession(false)) {
+ return dbClient.issueDao().selectResolvedOrConfirmedByComponentUuids(session, uuids)
+ .stream()
+ .map(ShortBranchIssueDto::toShortBranchIssue)
+ .collect(Collectors.toList());
+ }
+ }
+}
@Mock
private MergeBranchComponentUuids mergeBranchComponentsUuids;
@Mock
- private IssueStatusCopier issueStatusCopier;
+ private ShortBranchIssueStatusCopier issueStatusCopier;
ArgumentCaptor<DefaultIssue> defaultIssueCaptor;
+++ /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.Collections;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.sonar.api.rule.RuleKey;
-import org.sonar.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.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-
-public class IssueStatusCopierTest {
- @Mock
- private ResolvedShortBranchIssuesLoader resolvedShortBranchIssuesLoader;
- @Mock
- private IssueLifecycle issueLifecycle;
- @Mock
- private Component component;
-
- private SimpleTracker<DefaultIssue, ShortBranchIssue> tracker = new SimpleTracker<>();
- private IssueStatusCopier copier;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- copier = new IssueStatusCopier(resolvedShortBranchIssuesLoader, tracker, issueLifecycle);
- }
-
- @Test
- public void do_nothing_if_no_match() {
- when(resolvedShortBranchIssuesLoader.create(component)).thenReturn(Collections.emptyList());
- DefaultIssue i = createIssue("issue1", "rule1");
- copier.updateStatus(component, Collections.singleton(i));
-
- verify(resolvedShortBranchIssuesLoader).create(component);
- verifyZeroInteractions(issueLifecycle);
- }
-
- @Test
- public void do_nothing_if_no_new_issue() {
- DefaultIssue i = createIssue("issue1", "rule1");
- when(resolvedShortBranchIssuesLoader.create(component)).thenReturn(Collections.singleton(newShortBranchIssue(i)));
- copier.updateStatus(component, Collections.emptyList());
-
- verify(resolvedShortBranchIssuesLoader).create(component);
- verifyZeroInteractions(issueLifecycle);
- }
-
- @Test
- public void update_status_on_matches() {
- ShortBranchIssue shortBranchIssue = newShortBranchIssue(createIssue("issue1", "rule1"));
- DefaultIssue newIssue = createIssue("issue2", "rule1");
-
- when(resolvedShortBranchIssuesLoader.create(component)).thenReturn(Collections.singleton(shortBranchIssue));
- copier.updateStatus(component, Collections.singleton(newIssue));
- verify(issueLifecycle).copyResolution(newIssue, shortBranchIssue.getStatus(), shortBranchIssue.getResolution());
- }
-
- private static DefaultIssue createIssue(String key, String ruleKey) {
- DefaultIssue issue = new DefaultIssue();
- issue.setKey(key);
- issue.setRuleKey(RuleKey.of("repo", ruleKey));
- issue.setMessage("msg");
- issue.setLine(1);
- return issue;
- }
-
- private ShortBranchIssue newShortBranchIssue(DefaultIssue i) {
- return new ShortBranchIssue(i.line(), i.message(), i.getLineHash(), i.ruleKey(), i.status(), i.resolution());
- }
-}
--- /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.Arrays;
+import java.util.Collections;
+import javax.annotation.Nullable;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.ShortBranchIssue;
+import org.sonar.core.issue.tracking.SimpleTracker;
+import org.sonar.server.computation.task.projectanalysis.component.Component;
+
+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() {
+ ShortBranchIssue shortBranchIssue = newShortBranchIssue(createIssue("issue1", "rule1", Issue.STATUS_CONFIRMED, null));
+ DefaultIssue newIssue = createIssue("issue2", "rule1", Issue.STATUS_OPEN, null);
+
+ when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Collections.singleton(shortBranchIssue));
+ copier.updateStatus(component, Collections.singleton(newIssue));
+ verify(issueLifecycle).copyResolution(newIssue, shortBranchIssue.getStatus(), shortBranchIssue.getResolution());
+ }
+
+ @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));
+ ShortBranchIssue shortBranchIssue3 = newShortBranchIssue(createIssue("issue3", "rule1", Issue.STATUS_RESOLVED, Issue.RESOLUTION_FALSE_POSITIVE));
+ DefaultIssue newIssue = createIssue("newIssue", "rule1", Issue.STATUS_OPEN, null);
+
+ when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Arrays.asList(shortBranchIssue1, shortBranchIssue2, shortBranchIssue3));
+ copier.updateStatus(component, Collections.singleton(newIssue));
+ verify(issueLifecycle).copyResolution(newIssue, Issue.STATUS_RESOLVED, Issue.RESOLUTION_FALSE_POSITIVE);
+ }
+
+ 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.line(), i.message(), i.getLineHash(), i.ruleKey(), i.status(), i.resolution());
+ }
+}
public RuleKey getRuleKey() {
return ruleKey;
}
+
+ @Override
+ public String getStatus() {
+ return status;
+ }
}
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import java.util.Collection;
-import java.util.Iterator;
import java.util.Objects;
+import java.util.function.Function;
import javax.annotation.Nonnull;
import org.apache.commons.lang.StringUtils;
+import org.sonar.api.issue.Issue;
import org.sonar.api.rule.RuleKey;
public class AbstractTracker<RAW extends Trackable, BASE extends Trackable> {
- protected void match(Tracking<RAW, BASE> tracking, SearchKeyFactory factory) {
- match(tracking, factory, false);
+ protected void match(Tracking<RAW, BASE> tracking, Function<Trackable, SearchKey> searchKeyFactory) {
+ match(tracking, searchKeyFactory, false);
}
- protected void match(Tracking<RAW, BASE> tracking, SearchKeyFactory factory, boolean rejectMultipleMatches) {
+ protected void match(Tracking<RAW, BASE> tracking, Function<Trackable, SearchKey> searchKeyFactory, boolean preferResolved) {
if (tracking.isComplete()) {
return;
Multimap<SearchKey, BASE> baseSearch = ArrayListMultimap.create();
for (BASE base : tracking.getUnmatchedBases()) {
- baseSearch.put(factory.create(base), base);
+ baseSearch.put(searchKeyFactory.apply(base), base);
}
for (RAW raw : tracking.getUnmatchedRaws()) {
- SearchKey rawKey = factory.create(raw);
+ SearchKey rawKey = searchKeyFactory.apply(raw);
Collection<BASE> bases = baseSearch.get(rawKey);
if (!bases.isEmpty()) {
- Iterator<BASE> it = bases.iterator();
- BASE match = it.next();
- if (rejectMultipleMatches && it.hasNext()) {
- continue;
+ BASE match;
+ if (preferResolved) {
+ match = bases.stream()
+ .filter(i -> Issue.STATUS_RESOLVED.equals(i.getStatus()))
+ .findFirst()
+ .orElse(bases.iterator().next());
+ } else {
+ // TODO taking the first one. Could be improved if there are more than 2 issues on the same line.
+ // Message could be checked to take the best one.
+ match = bases.iterator().next();
}
- // TODO taking the first one. Could be improved if there are more than 2 issues on the same line.
- // Message could be checked to take the best one.
tracking.match(raw, match);
baseSearch.remove(rawKey, match);
}
}
}
- private interface SearchKey {
+ protected interface SearchKey {
}
- private interface SearchKeyFactory {
- SearchKey create(Trackable trackable);
- }
-
- private static class LineAndLineHashKey implements SearchKey {
+ protected static class LineAndLineHashKey implements SearchKey {
private final RuleKey ruleKey;
private final String lineHash;
private final Integer line;
- LineAndLineHashKey(Trackable trackable) {
+ protected LineAndLineHashKey(Trackable trackable) {
this.ruleKey = trackable.getRuleKey();
this.line = trackable.getLine();
this.lineHash = StringUtils.defaultString(trackable.getLineHash(), "");
}
}
- protected enum LineAndLineHashKeyFactory implements SearchKeyFactory {
- INSTANCE;
- @Override
- public SearchKey create(Trackable t) {
- return new LineAndLineHashKey(t);
- }
- }
-
- private static class LineHashAndMessageKey implements SearchKey {
+ protected static class LineHashAndMessageKey implements SearchKey {
private final RuleKey ruleKey;
private final String message;
private final String lineHash;
}
}
- protected enum LineHashAndMessageKeyFactory implements SearchKeyFactory {
- INSTANCE;
- @Override
- public SearchKey create(Trackable t) {
- return new LineHashAndMessageKey(t);
- }
- }
-
- private static class LineAndMessageKey implements SearchKey {
+ protected static class LineAndMessageKey implements SearchKey {
private final RuleKey ruleKey;
private final String message;
private final Integer line;
}
}
- protected enum LineAndMessageKeyFactory implements SearchKeyFactory {
- INSTANCE;
- @Override
- public SearchKey create(Trackable t) {
- return new LineAndMessageKey(t);
- }
- }
-
- private static class LineHashKey implements SearchKey {
+ protected static class LineHashKey implements SearchKey {
private final RuleKey ruleKey;
private final String lineHash;
}
}
- protected enum LineHashKeyFactory implements SearchKeyFactory {
- INSTANCE;
- @Override
- public SearchKey create(Trackable t) {
- return new LineHashKey(t);
- }
- }
}
Tracking<RAW, BASE> tracking = new Tracking<>(rawInput, baseInput);
// 1. match issues with same rule, same line and same line hash, but not necessarily with same message
- match(tracking, LineAndLineHashKeyFactory.INSTANCE, true);
+ match(tracking, LineAndLineHashKey::new, true);
// 2. match issues with same rule, same message and same line hash
- match(tracking, LineHashAndMessageKeyFactory.INSTANCE, true);
+ match(tracking, LineHashAndMessageKey::new, true);
return tracking;
}
String getLineHash();
RuleKey getRuleKey();
+
+ String getStatus();
}
*/
package org.sonar.core.issue.tracking;
-import org.sonar.api.batch.ScannerSide;
import org.sonar.api.batch.InstantiationStrategy;
+import org.sonar.api.batch.ScannerSide;
@InstantiationStrategy(InstantiationStrategy.PER_BATCH)
@ScannerSide
Tracking<RAW, BASE> tracking = new Tracking<>(rawInput.getIssues(), baseInput.getIssues());
// 1. match issues with same rule, same line and same line hash, but not necessarily with same message
- match(tracking, LineAndLineHashKeyFactory.INSTANCE);
+ match(tracking, LineAndLineHashKey::new);
// 2. detect code moves by comparing blocks of codes
detectCodeMoves(rawInput, baseInput, tracking);
// 3. match issues with same rule, same message and same line hash
- match(tracking, LineHashAndMessageKeyFactory.INSTANCE);
+ match(tracking, LineHashAndMessageKey::new);
// 4. match issues with same rule, same line and same message
- match(tracking, LineAndMessageKeyFactory.INSTANCE);
+ match(tracking, LineAndMessageKey::new);
// 5. match issues with same rule and same line hash but different line and different message.
// See SONAR-2812
- match(tracking, LineHashKeyFactory.INSTANCE);
+ match(tracking, LineHashKey::new);
return tracking;
}
@Test
public void do_not_fail_if_raw_line_does_not_exist() {
FakeInput baseInput = new FakeInput();
- FakeInput rawInput = new FakeInput("H1").addIssue(new Issue(200, "H200", RULE_SYSTEM_PRINT, "msg"));
+ FakeInput rawInput = new FakeInput("H1").addIssue(new Issue(200, "H200", RULE_SYSTEM_PRINT, "msg", org.sonar.api.issue.Issue.STATUS_OPEN));
Tracking<Issue, Issue> tracking = tracker.track(rawInput, baseInput);
private final RuleKey ruleKey;
private final Integer line;
private final String message, lineHash;
+ private final String status;
- Issue(@Nullable Integer line, String lineHash, RuleKey ruleKey, String message) {
+ Issue(@Nullable Integer line, String lineHash, RuleKey ruleKey, String message, String status) {
this.line = line;
this.lineHash = lineHash;
this.ruleKey = ruleKey;
+ this.status = status;
this.message = trim(message);
}
public RuleKey getRuleKey() {
return ruleKey;
}
+
+ @Override
+ public String getStatus() {
+ return status;
+ }
}
private static class FakeInput implements Input<Issue> {
}
Issue createIssueOnLine(int line, RuleKey ruleKey, String message) {
- Issue issue = new Issue(line, lineHashes.get(line - 1), ruleKey, message);
+ Issue issue = new Issue(line, lineHashes.get(line - 1), ruleKey, message, org.sonar.api.issue.Issue.STATUS_OPEN);
issues.add(issue);
return issue;
}
* No line (line 0)
*/
Issue createIssue(RuleKey ruleKey, String message) {
- Issue issue = new Issue(null, "", ruleKey, message);
+ Issue issue = new Issue(null, "", ruleKey, message, org.sonar.api.issue.Issue.STATUS_OPEN);
issues.add(issue);
return issue;
}
return dto.hasMsg() ? trim(dto.getMsg()) : "";
}
+ @Override
+ public String getStatus() {
+ throw new UnsupportedOperationException();
+ }
+
}
return ruleKey;
}
+ @Override
+ public String getStatus() {
+ return status;
+ }
+
public String severity() {
return severity;
}