diff options
author | Duarte Meneses <duarte.meneses@sonarsource.com> | 2017-08-09 15:32:47 +0200 |
---|---|---|
committer | Janos Gyerik <janos.gyerik@sonarsource.com> | 2017-09-12 10:55:10 +0200 |
commit | 8f27cc5968df9f258f94fbdc1de47a1784dfafb9 (patch) | |
tree | be81b1df3becad47a0e24b10ec7eed231e04fcea /server | |
parent | dd47b17bb4259ce422e3a643d07178f21185a406 (diff) | |
download | sonarqube-8f27cc5968df9f258f94fbdc1de47a1784dfafb9.tar.gz sonarqube-8f27cc5968df9f258f94fbdc1de47a1784dfafb9.zip |
SONAR-9692 - Perform issue tracking for short living branches
Diffstat (limited to 'server')
21 files changed, 850 insertions, 143 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java index d4d2b580bda..f0c8946a452 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java @@ -49,6 +49,7 @@ import org.sonar.server.computation.task.projectanalysis.filemove.SourceSimilari import org.sonar.server.computation.task.projectanalysis.filesystem.ComputationTempFolderProvider; import org.sonar.server.computation.task.projectanalysis.issue.BaseIssuesLoader; import org.sonar.server.computation.task.projectanalysis.issue.CloseIssuesOnRemovedComponentsVisitor; +import org.sonar.server.computation.task.projectanalysis.issue.ComponentIssuesLoader; import org.sonar.server.computation.task.projectanalysis.issue.ComponentIssuesRepositoryImpl; import org.sonar.server.computation.task.projectanalysis.issue.ComponentsWithUnprocessedIssues; import org.sonar.server.computation.task.projectanalysis.issue.DebtCalculator; @@ -60,19 +61,24 @@ import org.sonar.server.computation.task.projectanalysis.issue.IssueCache; 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.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.LoadComponentUuidsHavingOpenIssuesVisitor; +import org.sonar.server.computation.task.projectanalysis.issue.MergeBranchIssuesLoader; 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.NewEffortCalculator; +import org.sonar.server.computation.task.projectanalysis.issue.RemoveProcessedComponentsVisitor; 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; import org.sonar.server.computation.task.projectanalysis.issue.ScmAccountToUser; import org.sonar.server.computation.task.projectanalysis.issue.ScmAccountToUserLoader; +import org.sonar.server.computation.task.projectanalysis.issue.ShortBranchTrackerExecution; import org.sonar.server.computation.task.projectanalysis.issue.TrackerBaseInputFactory; import org.sonar.server.computation.task.projectanalysis.issue.TrackerExecution; +import org.sonar.server.computation.task.projectanalysis.issue.TrackerMergeBranchInputFactory; import org.sonar.server.computation.task.projectanalysis.issue.TrackerRawInputFactory; import org.sonar.server.computation.task.projectanalysis.issue.UpdateConflictResolver; import org.sonar.server.computation.task.projectanalysis.issue.commonrule.BranchCoverageRule; @@ -212,6 +218,7 @@ public final class ProjectAnalysisTaskContainerPopulator implements ContainerPop IssueCounter.class, MovedIssueVisitor.class, IssuesRepositoryVisitor.class, + RemoveProcessedComponentsVisitor.class, // visitors : order is important, measure computers must be executed at the end in order to access to every measures / issues LoadComponentUuidsHavingOpenIssuesVisitor.class, @@ -227,9 +234,14 @@ public final class ProjectAnalysisTaskContainerPopulator implements ContainerPop UpdateConflictResolver.class, TrackerBaseInputFactory.class, TrackerRawInputFactory.class, + TrackerMergeBranchInputFactory.class, Tracker.class, TrackerExecution.class, + ShortBranchTrackerExecution.class, + ComponentIssuesLoader.class, BaseIssuesLoader.class, + MergeBranchIssuesLoader.class, + IssueTrackingDelegator.class, // filemove SourceSimilarityImpl.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/BaseIssuesLoader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/BaseIssuesLoader.java index eef3eea4d0b..359be376b16 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/BaseIssuesLoader.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/BaseIssuesLoader.java @@ -19,59 +19,22 @@ */ package org.sonar.server.computation.task.projectanalysis.issue; -import java.util.ArrayList; -import java.util.List; import java.util.Set; -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.IssueMapper; import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolder; -import org.sonar.server.computation.task.projectanalysis.qualityprofile.ActiveRulesHolder; /** * Loads all the project open issues from database, including manual issues. - * */ public class BaseIssuesLoader { private final TreeRootHolder treeRootHolder; private final DbClient dbClient; - private final RuleRepository ruleRepository; - private final ActiveRulesHolder activeRulesHolder; - public BaseIssuesLoader(TreeRootHolder treeRootHolder, - DbClient dbClient, RuleRepository ruleRepository, ActiveRulesHolder activeRulesHolder) { - this.activeRulesHolder = activeRulesHolder; + public BaseIssuesLoader(TreeRootHolder treeRootHolder, DbClient dbClient) { this.treeRootHolder = treeRootHolder; this.dbClient = dbClient; - this.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(); - - // 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; - } - } - - private boolean isActive(RuleKey ruleKey) { - return activeRulesHolder.get(ruleKey).isPresent(); } /** diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/CloseIssuesOnRemovedComponentsVisitor.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/CloseIssuesOnRemovedComponentsVisitor.java index f74bcdb5ac5..7b178613b15 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/CloseIssuesOnRemovedComponentsVisitor.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/CloseIssuesOnRemovedComponentsVisitor.java @@ -34,15 +34,15 @@ import static org.sonar.server.computation.task.projectanalysis.component.Compon */ public class CloseIssuesOnRemovedComponentsVisitor extends TypeAwareVisitorAdapter { - private final BaseIssuesLoader baseIssuesLoader; + private final ComponentIssuesLoader issuesLoader; private final ComponentsWithUnprocessedIssues componentsWithUnprocessedIssues; private final IssueCache issueCache; private final IssueLifecycle issueLifecycle; - public CloseIssuesOnRemovedComponentsVisitor(BaseIssuesLoader baseIssuesLoader, ComponentsWithUnprocessedIssues componentsWithUnprocessedIssues, IssueCache issueCache, + public CloseIssuesOnRemovedComponentsVisitor(ComponentIssuesLoader issuesLoader, ComponentsWithUnprocessedIssues componentsWithUnprocessedIssues, IssueCache issueCache, IssueLifecycle issueLifecycle) { super(CrawlerDepthLimit.PROJECT, POST_ORDER); - this.baseIssuesLoader = baseIssuesLoader; + this.issuesLoader = issuesLoader; this.componentsWithUnprocessedIssues = componentsWithUnprocessedIssues; this.issueCache = issueCache; this.issueLifecycle = issueLifecycle; @@ -57,7 +57,7 @@ public class CloseIssuesOnRemovedComponentsVisitor extends TypeAwareVisitorAdapt DiskCache<DefaultIssue>.DiskAppender cacheAppender = issueCache.newAppender(); try { for (String deletedComponentUuid : deletedComponentUuids) { - List<DefaultIssue> issues = baseIssuesLoader.loadForComponentUuid(deletedComponentUuid); + List<DefaultIssue> issues = issuesLoader.loadForComponentUuid(deletedComponentUuid); for (DefaultIssue issue : issues) { issue.setBeingClosed(true); // TODO should be renamed diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ComponentIssuesLoader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ComponentIssuesLoader.java new file mode 100644 index 00000000000..2ae00cdd153 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ComponentIssuesLoader.java @@ -0,0 +1,67 @@ +/* + * 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.ArrayList; +import java.util.List; + +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.IssueMapper; +import org.sonar.server.computation.task.projectanalysis.qualityprofile.ActiveRulesHolder; + +public class ComponentIssuesLoader { + private final DbClient dbClient; + private final RuleRepository ruleRepository; + private final ActiveRulesHolder activeRulesHolder; + + public ComponentIssuesLoader(DbClient dbClient, RuleRepository ruleRepository, ActiveRulesHolder activeRulesHolder) { + this.activeRulesHolder = activeRulesHolder; + this.dbClient = dbClient; + this.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(); + + // 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; + } + } + + private boolean isActive(RuleKey ruleKey) { + return activeRulesHolder.get(ruleKey).isPresent(); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/DefaultTrackingInput.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/DefaultTrackingInput.java new file mode 100644 index 00000000000..5ac2a68ec25 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/DefaultTrackingInput.java @@ -0,0 +1,55 @@ +/* + * 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 org.sonar.core.issue.DefaultIssue; +import org.sonar.core.issue.tracking.BlockHashSequence; +import org.sonar.core.issue.tracking.Input; +import org.sonar.core.issue.tracking.LineHashSequence; + +public class DefaultTrackingInput implements Input<DefaultIssue> { + private final Collection<DefaultIssue> issues; + private final LineHashSequence lineHashes; + private final BlockHashSequence blockHashes; + + public DefaultTrackingInput(Collection<DefaultIssue> issues, LineHashSequence lineHashes, BlockHashSequence blockHashes) { + this.issues = issues; + this.lineHashes = lineHashes; + this.blockHashes = blockHashes; + } + + @Override + public LineHashSequence getLineHashSequence() { + return lineHashes; + } + + @Override + public BlockHashSequence getBlockHashSequence() { + return blockHashes; + } + + @Override + public Collection<DefaultIssue> getIssues() { + return issues; + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IntegrateIssuesVisitor.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IntegrateIssuesVisitor.java index b0bdbe309d7..528372d82e3 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IntegrateIssuesVisitor.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IntegrateIssuesVisitor.java @@ -21,6 +21,7 @@ package org.sonar.server.computation.task.projectanalysis.issue; import static org.sonar.server.computation.task.projectanalysis.component.ComponentVisitor.Order.POST_ORDER; +import java.util.Collection; import java.util.List; import java.util.Map; @@ -31,64 +32,45 @@ import org.sonar.server.computation.task.projectanalysis.component.Component; import org.sonar.server.computation.task.projectanalysis.component.Component.Status; import org.sonar.server.computation.task.projectanalysis.component.CrawlerDepthLimit; import org.sonar.server.computation.task.projectanalysis.component.TypeAwareVisitorAdapter; -import org.sonar.server.computation.task.projectanalysis.filemove.MovedFilesRepository; import org.sonar.server.util.cache.DiskCache; -import com.google.common.base.Optional; - public class IntegrateIssuesVisitor extends TypeAwareVisitorAdapter { - private final TrackerExecution tracker; private final IssueCache issueCache; private final IssueLifecycle issueLifecycle; private final IssueVisitors issueVisitors; - private final ComponentsWithUnprocessedIssues componentsWithUnprocessedIssues; - private final MovedFilesRepository movedFilesRepository; - private final BaseIssuesLoader baseIssuesLoader; + private final ComponentIssuesLoader issuesLoader; private final AnalysisMetadataHolder analysisMetadataHolder; + private final IssueTrackingDelegator issueTracking; - public IntegrateIssuesVisitor(TrackerExecution tracker, IssueCache issueCache, IssueLifecycle issueLifecycle, IssueVisitors issueVisitors, - ComponentsWithUnprocessedIssues componentsWithUnprocessedIssues, MovedFilesRepository movedFilesRepository, BaseIssuesLoader baseIssuesLoader, - AnalysisMetadataHolder analysisMetadataHolder) { + public IntegrateIssuesVisitor(IssueCache issueCache, IssueLifecycle issueLifecycle, IssueVisitors issueVisitors, ComponentIssuesLoader issuesLoader, + AnalysisMetadataHolder analysisMetadataHolder, IssueTrackingDelegator issueTracking) { super(CrawlerDepthLimit.FILE, POST_ORDER); - this.tracker = tracker; this.issueCache = issueCache; this.issueLifecycle = issueLifecycle; this.issueVisitors = issueVisitors; - this.componentsWithUnprocessedIssues = componentsWithUnprocessedIssues; - this.movedFilesRepository = movedFilesRepository; - this.baseIssuesLoader = baseIssuesLoader; + this.issuesLoader = issuesLoader; this.analysisMetadataHolder = analysisMetadataHolder; + this.issueTracking = issueTracking; } @Override public void visitAny(Component component) { - processIssues(component); - - componentsWithUnprocessedIssues.remove(component.getUuid()); - Optional<MovedFilesRepository.OriginalFile> originalFile = movedFilesRepository.getOriginalFile(component); - if (originalFile.isPresent()) { - componentsWithUnprocessedIssues.remove(originalFile.get().getUuid()); - } - } - - private void processIssues(Component component) { - DiskCache<DefaultIssue>.DiskAppender cacheAppender = issueCache.newAppender(); - try { + try (DiskCache<DefaultIssue>.DiskAppender cacheAppender = issueCache.newAppender()) { issueVisitors.beforeComponent(component); + if (isIncremental(component)) { - fillIncrementalOpenIssues(component, cacheAppender); + List<DefaultIssue> issues = issuesLoader.loadForComponentUuid(component.getUuid()); + fillIncrementalOpenIssues(component, issues, cacheAppender); } else { - Tracking<DefaultIssue, DefaultIssue> tracking = tracker.track(component); - fillNewOpenIssues(component, tracking, cacheAppender); - fillExistingOpenIssues(component, tracking, cacheAppender); - closeUnmatchedBaseIssues(component, tracking, cacheAppender); + Tracking<DefaultIssue, DefaultIssue> tracking = issueTracking.track(component); + fillNewOpenIssues(component, tracking.getUnmatchedRaws(), cacheAppender); + fillExistingOpenIssues(component, tracking.getMatchedRaws(), cacheAppender); + closeUnmatchedBaseIssues(component, tracking.getUnmatchedBases(), cacheAppender); } issueVisitors.afterComponent(component); } catch (Exception e) { throw new IllegalStateException(String.format("Fail to process issues of component '%s'", component.getKey()), e); - } finally { - cacheAppender.close(); } } @@ -96,24 +78,21 @@ public class IntegrateIssuesVisitor extends TypeAwareVisitorAdapter { return analysisMetadataHolder.isIncrementalAnalysis() && component.getStatus() == Status.SAME; } - private void fillNewOpenIssues(Component component, Tracking<DefaultIssue, DefaultIssue> tracking, DiskCache<DefaultIssue>.DiskAppender cacheAppender) { - for (DefaultIssue issue : tracking.getUnmatchedRaws()) { + private void fillNewOpenIssues(Component component, Iterable<DefaultIssue> issues, DiskCache<DefaultIssue>.DiskAppender cacheAppender) { + for (DefaultIssue issue : issues) { issueLifecycle.initNewOpenIssue(issue); process(component, issue, cacheAppender); } } - private void fillIncrementalOpenIssues(Component component, DiskCache<DefaultIssue>.DiskAppender cacheAppender) { - List<DefaultIssue> issues = baseIssuesLoader.loadForComponentUuid(component.getUuid()); - + private void fillIncrementalOpenIssues(Component component, Collection<DefaultIssue> issues, DiskCache<DefaultIssue>.DiskAppender cacheAppender) { for (DefaultIssue issue : issues) { - issueLifecycle.updateExistingOpenissue(issue); process(component, issue, cacheAppender); } } - private void fillExistingOpenIssues(Component component, Tracking<DefaultIssue, DefaultIssue> tracking, DiskCache<DefaultIssue>.DiskAppender cacheAppender) { - for (Map.Entry<DefaultIssue, DefaultIssue> entry : tracking.getMatchedRaws().entrySet()) { + private void fillExistingOpenIssues(Component component, Map<DefaultIssue, DefaultIssue> matched, DiskCache<DefaultIssue>.DiskAppender cacheAppender) { + for (Map.Entry<DefaultIssue, DefaultIssue> entry : matched.entrySet()) { DefaultIssue raw = entry.getKey(); DefaultIssue base = entry.getValue(); issueLifecycle.mergeExistingOpenIssue(raw, base); @@ -121,8 +100,8 @@ public class IntegrateIssuesVisitor extends TypeAwareVisitorAdapter { } } - private void closeUnmatchedBaseIssues(Component component, Tracking<DefaultIssue, DefaultIssue> tracking, DiskCache<DefaultIssue>.DiskAppender cacheAppender) { - for (DefaultIssue issue : tracking.getUnmatchedBases()) { + private void closeUnmatchedBaseIssues(Component component, Iterable<DefaultIssue> issues, DiskCache<DefaultIssue>.DiskAppender cacheAppender) { + for (DefaultIssue issue : issues) { // TODO should replace flag "beingClosed" by express call to transition "automaticClose" issue.setBeingClosed(true); // TODO manual issues -> was updater.setResolution(newIssue, Issue.RESOLUTION_REMOVED, changeContext);. Is it a problem ? diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueLifecycle.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueLifecycle.java index 6942c5c58c0..6ee0a12e76c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueLifecycle.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueLifecycle.java @@ -64,10 +64,6 @@ public class IssueLifecycle { issue.setStatus(Issue.STATUS_OPEN); issue.setEffort(debtCalculator.calculate(issue)); } - - public void updateExistingOpenissue(DefaultIssue base) { - // nothing to do - } public void mergeExistingOpenIssue(DefaultIssue raw, DefaultIssue base) { raw.setNew(false); diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueTrackingDelegator.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueTrackingDelegator.java new file mode 100644 index 00000000000..5e51c6c6982 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueTrackingDelegator.java @@ -0,0 +1,52 @@ +/* + * 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 org.sonar.core.issue.DefaultIssue; +import org.sonar.core.issue.tracking.Tracking; +import org.sonar.db.component.BranchType; +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.component.Component; + +public class IssueTrackingDelegator { + private final ShortBranchTrackerExecution shortBranchTracker; + private final TrackerExecution tracker; + private final AnalysisMetadataHolder analysisMetadataHolder; + + public IssueTrackingDelegator(ShortBranchTrackerExecution shortBranchTracker, TrackerExecution tracker, AnalysisMetadataHolder analysisMetadataHolder) { + this.shortBranchTracker = shortBranchTracker; + this.tracker = tracker; + this.analysisMetadataHolder = analysisMetadataHolder; + } + + private boolean isShortLivingBranch() { + java.util.Optional<Branch> branch = analysisMetadataHolder.getBranch(); + return branch.isPresent() && branch.get().getType() == BranchType.SHORT; + } + + public Tracking<DefaultIssue, DefaultIssue> track(Component component) { + if (isShortLivingBranch()) { + return shortBranchTracker.track(component); + } else { + return tracker.track(component); + } + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/MergeBranchIssuesLoader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/MergeBranchIssuesLoader.java new file mode 100644 index 00000000000..b32e08c43c7 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/MergeBranchIssuesLoader.java @@ -0,0 +1,71 @@ +/* + * 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 java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.sonar.core.issue.DefaultIssue; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.component.ComponentDto; +import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolder; + +public class MergeBranchIssuesLoader { + private final DbClient dbClient; + private final ComponentIssuesLoader issuesLoader; + private final AnalysisMetadataHolder analysisMetadataHolder; + private Map<String, String> uuidsByKey; + + public MergeBranchIssuesLoader(DbClient dbClient, ComponentIssuesLoader issuesLoader, AnalysisMetadataHolder analysisMetadataHolder) { + this.dbClient = dbClient; + this.issuesLoader = issuesLoader; + this.analysisMetadataHolder = analysisMetadataHolder; + } + + public void loadMergeBranchComponents() { + String mergeBranchUuid = analysisMetadataHolder.getBranch().get().getMergeBranchUuid().get(); + + uuidsByKey = new HashMap<>(); + try (DbSession dbSession = dbClient.openSession(false)) { + + List<ComponentDto> components = dbClient.componentDao().selectByProjectUuid(mergeBranchUuid, dbSession); + for (ComponentDto dto : components) { + uuidsByKey.put(dto.getDbKey(), dto.uuid()); + } + } + } + + public List<DefaultIssue> loadForKey(String componentKey) { + if (uuidsByKey == null) { + loadMergeBranchComponents(); + } + + String componentUuid = uuidsByKey.get(componentKey); + + if (componentUuid == null) { + return Collections.emptyList(); + } + + return issuesLoader.loadForComponentUuid(componentUuid); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/RemoveProcessedComponentsVisitor.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/RemoveProcessedComponentsVisitor.java new file mode 100644 index 00000000000..afff2f07464 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/RemoveProcessedComponentsVisitor.java @@ -0,0 +1,44 @@ +/* + * 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 org.sonar.server.computation.task.projectanalysis.component.Component; +import org.sonar.server.computation.task.projectanalysis.filemove.MovedFilesRepository; + +import com.google.common.base.Optional; + +public class RemoveProcessedComponentsVisitor extends IssueVisitor { + private final ComponentsWithUnprocessedIssues componentsWithUnprocessedIssues; + private final MovedFilesRepository movedFilesRepository; + + public RemoveProcessedComponentsVisitor(ComponentsWithUnprocessedIssues componentsWithUnprocessedIssues, MovedFilesRepository movedFilesRepository) { + this.componentsWithUnprocessedIssues = componentsWithUnprocessedIssues; + this.movedFilesRepository = movedFilesRepository; + } + + @Override + public void afterComponent(Component component) { + componentsWithUnprocessedIssues.remove(component.getUuid()); + Optional<MovedFilesRepository.OriginalFile> originalFile = movedFilesRepository.getOriginalFile(component); + if (originalFile.isPresent()) { + componentsWithUnprocessedIssues.remove(originalFile.get().getUuid()); + } + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchTrackerExecution.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchTrackerExecution.java new file mode 100644 index 00000000000..1666e3ea1ab --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchTrackerExecution.java @@ -0,0 +1,63 @@ +/* + * 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.ArrayList; +import java.util.List; + +import org.sonar.core.issue.DefaultIssue; +import org.sonar.core.issue.tracking.Input; +import org.sonar.core.issue.tracking.Tracker; +import org.sonar.core.issue.tracking.Tracking; +import org.sonar.server.computation.task.projectanalysis.component.Component; + +public class ShortBranchTrackerExecution { + private TrackerBaseInputFactory baseInputFactory; + private TrackerRawInputFactory rawInputFactory; + private TrackerMergeBranchInputFactory mergeInputFactory; + private Tracker<DefaultIssue, DefaultIssue> tracker; + + public ShortBranchTrackerExecution(TrackerBaseInputFactory baseInputFactory, TrackerRawInputFactory rawInputFactory, TrackerMergeBranchInputFactory mergeInputFactory, + Tracker<DefaultIssue, DefaultIssue> tracker) { + this.baseInputFactory = baseInputFactory; + this.rawInputFactory = rawInputFactory; + this.mergeInputFactory = mergeInputFactory; + this.tracker = tracker; + } + + public Tracking<DefaultIssue, DefaultIssue> track(Component component) { + Input<DefaultIssue> rawInput = rawInputFactory.create(component); + Input<DefaultIssue> baseInput = baseInputFactory.create(component); + Input<DefaultIssue> mergeInput = mergeInputFactory.create(component); + + Tracking<DefaultIssue, DefaultIssue> baseTracking = tracker.track(rawInput, mergeInput); + List<DefaultIssue> unmatchedRaws = toList(baseTracking.getUnmatchedRaws()); + Input<DefaultIssue> unmatchedRawInput = new DefaultTrackingInput(unmatchedRaws, rawInput.getLineHashSequence(), rawInput.getBlockHashSequence()); + + // do second tracking with base branch using raws issues that are still unmatched + return tracker.track(unmatchedRawInput, baseInput); + } + + private static <T> List<T> toList(Iterable<T> iterable) { + List<T> list = new ArrayList<>(); + iterable.forEach(list::add); + return list; + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/TrackerBaseInputFactory.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/TrackerBaseInputFactory.java index 01001bcdca8..f872f8e5f81 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/TrackerBaseInputFactory.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/TrackerBaseInputFactory.java @@ -39,12 +39,12 @@ import org.sonar.server.computation.task.projectanalysis.filemove.MovedFilesRepo public class TrackerBaseInputFactory { private static final LineHashSequence EMPTY_LINE_HASH_SEQUENCE = new LineHashSequence(Collections.<String>emptyList()); - private final BaseIssuesLoader baseIssuesLoader; + private final ComponentIssuesLoader issuesLoader; private final DbClient dbClient; private final MovedFilesRepository movedFilesRepository; - public TrackerBaseInputFactory(BaseIssuesLoader baseIssuesLoader, DbClient dbClient, MovedFilesRepository movedFilesRepository) { - this.baseIssuesLoader = baseIssuesLoader; + public TrackerBaseInputFactory(ComponentIssuesLoader issuesLoader, DbClient dbClient, MovedFilesRepository movedFilesRepository) { + this.issuesLoader = issuesLoader; this.dbClient = dbClient; this.movedFilesRepository = movedFilesRepository; } @@ -80,7 +80,7 @@ public class TrackerBaseInputFactory { @Override protected List<DefaultIssue> loadIssues() { - return baseIssuesLoader.loadForComponentUuid(effectiveUuid); + return issuesLoader.loadForComponentUuid(effectiveUuid); } } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/TrackerExecution.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/TrackerExecution.java index 60de97417ad..d62183f429c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/TrackerExecution.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/TrackerExecution.java @@ -26,9 +26,9 @@ import org.sonar.server.computation.task.projectanalysis.component.Component; public class TrackerExecution { - private final TrackerBaseInputFactory baseInputFactory; - private final TrackerRawInputFactory rawInputFactory; - private final Tracker<DefaultIssue, DefaultIssue> tracker; + protected final TrackerBaseInputFactory baseInputFactory; + protected final TrackerRawInputFactory rawInputFactory; + protected final Tracker<DefaultIssue, DefaultIssue> tracker; public TrackerExecution(TrackerBaseInputFactory baseInputFactory, TrackerRawInputFactory rawInputFactory, Tracker<DefaultIssue, DefaultIssue> tracker) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/TrackerMergeBranchInputFactory.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/TrackerMergeBranchInputFactory.java new file mode 100644 index 00000000000..44f65e4a798 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/TrackerMergeBranchInputFactory.java @@ -0,0 +1,76 @@ +/* + * 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 java.util.List; + +import org.sonar.core.issue.DefaultIssue; +import org.sonar.core.issue.tracking.Input; +import org.sonar.core.issue.tracking.LazyInput; +import org.sonar.core.issue.tracking.LineHashSequence; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.server.computation.task.projectanalysis.component.Component; + +public class TrackerMergeBranchInputFactory { + private static final LineHashSequence EMPTY_LINE_HASH_SEQUENCE = new LineHashSequence(Collections.<String>emptyList()); + + private final MergeBranchIssuesLoader mergeIssuesLoader; + private final DbClient dbClient; + + public TrackerMergeBranchInputFactory(MergeBranchIssuesLoader mergeIssuesLoader, DbClient dbClient) { + this.mergeIssuesLoader = mergeIssuesLoader; + this.dbClient = dbClient; + // TODO detect file moves? + } + + public Input<DefaultIssue> create(Component component) { + return new MergeLazyInput(component); + } + + private class MergeLazyInput extends LazyInput<DefaultIssue> { + private final Component component; + + private MergeLazyInput(Component component) { + this.component = component; + } + + @Override + protected LineHashSequence loadLineHashSequence() { + if (component.getType() != Component.Type.FILE) { + return EMPTY_LINE_HASH_SEQUENCE; + } + + try (DbSession session = dbClient.openSession(false)) { + List<String> hashes = dbClient.fileSourceDao().selectLineHashes(session, component.getUuid()); + if (hashes == null || hashes.isEmpty()) { + return EMPTY_LINE_HASH_SEQUENCE; + } + return new LineHashSequence(hashes); + } + } + + @Override + protected List<DefaultIssue> loadIssues() { + return mergeIssuesLoader.loadForKey(component.getUuid()); + } + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/CloseIssuesOnRemovedComponentsVisitorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/CloseIssuesOnRemovedComponentsVisitorTest.java index 92dc35229f5..3adcf83d6ca 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/CloseIssuesOnRemovedComponentsVisitorTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/CloseIssuesOnRemovedComponentsVisitorTest.java @@ -48,7 +48,7 @@ public class CloseIssuesOnRemovedComponentsVisitorTest { @Rule public TemporaryFolder temp = new TemporaryFolder(); - BaseIssuesLoader baseIssuesLoader = mock(BaseIssuesLoader.class); + ComponentIssuesLoader issuesLoader = mock(ComponentIssuesLoader.class); ComponentsWithUnprocessedIssues componentsWithUnprocessedIssues = mock(ComponentsWithUnprocessedIssues.class); IssueLifecycle issueLifecycle = mock(IssueLifecycle.class); IssueCache issueCache; @@ -57,7 +57,7 @@ public class CloseIssuesOnRemovedComponentsVisitorTest { @Before public void setUp() throws Exception { issueCache = new IssueCache(temp.newFile(), System2.INSTANCE); - underTest = new VisitorsCrawler(Arrays.<ComponentVisitor>asList(new CloseIssuesOnRemovedComponentsVisitor(baseIssuesLoader, componentsWithUnprocessedIssues, issueCache, issueLifecycle))); + underTest = new VisitorsCrawler(Arrays.<ComponentVisitor>asList(new CloseIssuesOnRemovedComponentsVisitor(issuesLoader, componentsWithUnprocessedIssues, issueCache, issueLifecycle))); } @Test @@ -67,7 +67,7 @@ public class CloseIssuesOnRemovedComponentsVisitorTest { when(componentsWithUnprocessedIssues.getUuids()).thenReturn(newHashSet(fileUuid)); DefaultIssue issue = new DefaultIssue().setKey(issueUuid); - when(baseIssuesLoader.loadForComponentUuid(fileUuid)).thenReturn(Collections.singletonList(issue)); + when(issuesLoader.loadForComponentUuid(fileUuid)).thenReturn(Collections.singletonList(issue)); underTest.visit(ReportComponent.builder(PROJECT, 1).build()); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/DefaultTrackingInputTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/DefaultTrackingInputTest.java new file mode 100644 index 00000000000..44e5cd2c08c --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/DefaultTrackingInputTest.java @@ -0,0 +1,54 @@ +/* + * 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 static org.assertj.core.api.Assertions.assertThat; + +import java.util.Collection; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.sonar.core.issue.DefaultIssue; +import org.sonar.core.issue.tracking.BlockHashSequence; +import org.sonar.core.issue.tracking.LineHashSequence; + +public class DefaultTrackingInputTest { + @Mock + private Collection<DefaultIssue> issues; + @Mock + private BlockHashSequence blockHashes; + @Mock + private LineHashSequence lineHashes; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void test_getters() { + DefaultTrackingInput underTest = new DefaultTrackingInput(issues, lineHashes, blockHashes); + assertThat(underTest.getBlockHashSequence()).isEqualTo(blockHashes); + assertThat(underTest.getLineHashSequence()).isEqualTo(lineHashes); + assertThat(underTest.getIssues()).isEqualTo(issues); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IntegrateIssuesVisitorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IntegrateIssuesVisitorTest.java index 55a2d58583a..33f85ac5d33 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IntegrateIssuesVisitorTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IntegrateIssuesVisitorTest.java @@ -20,7 +20,6 @@ package org.sonar.server.computation.task.projectanalysis.issue; import static com.google.common.collect.Lists.newArrayList; -import static com.google.common.collect.Sets.newHashSet; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Matchers.any; @@ -29,7 +28,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.sonar.server.computation.task.projectanalysis.component.ReportComponent.builder; -import java.util.Collections; import java.util.List; import org.junit.Before; @@ -54,12 +52,13 @@ import org.sonar.db.rule.RuleDto; import org.sonar.db.rule.RuleTesting; import org.sonar.scanner.protocol.Constants; import org.sonar.scanner.protocol.output.ScannerReport; -import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolderRule; +import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolder; 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.Component.Status; +import org.sonar.server.computation.task.projectanalysis.component.DefaultBranchImpl; import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolderRule; import org.sonar.server.computation.task.projectanalysis.component.TypeAwareVisitor; -import org.sonar.server.computation.task.projectanalysis.component.Component.Status; import org.sonar.server.computation.task.projectanalysis.filemove.MovedFilesRepository; import org.sonar.server.computation.task.projectanalysis.issue.commonrule.CommonRuleEngineImpl; import org.sonar.server.computation.task.projectanalysis.issue.filter.IssueFilter; @@ -104,23 +103,24 @@ public class IntegrateIssuesVisitorTest { public SourceLinesRepositoryRule fileSourceRepository = new SourceLinesRepositoryRule(); @Mock - AnalysisMetadataHolderRule analysisMetadataHolder; + private AnalysisMetadataHolder analysisMetadataHolder; @Mock - IssueFilter issueFilter; + private IssueFilter issueFilter; @Mock - MovedFilesRepository movedFilesRepository; + private MovedFilesRepository movedFilesRepository; @Mock - IssueLifecycle issueLifecycle; + private IssueLifecycle issueLifecycle; @Mock - IssueVisitor issueVisitor; + private IssueVisitor issueVisitor; ArgumentCaptor<DefaultIssue> defaultIssueCaptor = ArgumentCaptor.forClass(DefaultIssue.class); - BaseIssuesLoader baseIssuesLoader = new BaseIssuesLoader(treeRootHolder, dbTester.getDbClient(), ruleRepositoryRule, activeRulesHolderRule); - + ComponentIssuesLoader issuesLoader = new ComponentIssuesLoader(dbTester.getDbClient(), ruleRepositoryRule, activeRulesHolderRule); + MergeBranchIssuesLoader mergeIssuesLoader = new MergeBranchIssuesLoader(dbTester.getDbClient(), issuesLoader, analysisMetadataHolder); + IssueTrackingDelegator trackingDelegator; TrackerExecution tracker; + ShortBranchTrackerExecution shortBranchTracker; IssueCache issueCache; - ComponentsWithUnprocessedIssues componentsWithUnprocessedIssues = new ComponentsWithUnprocessedIssues(); TypeAwareVisitor underTest; @@ -129,23 +129,26 @@ public class IntegrateIssuesVisitorTest { MockitoAnnotations.initMocks(this); IssueVisitors issueVisitors = new IssueVisitors(new IssueVisitor[] {issueVisitor}); - tracker = new TrackerExecution(new TrackerBaseInputFactory(baseIssuesLoader, dbTester.getDbClient(), movedFilesRepository), - new TrackerRawInputFactory(treeRootHolder, reportReader, fileSourceRepository, new CommonRuleEngineImpl(), issueFilter), - new Tracker<>()); + when(movedFilesRepository.getOriginalFile(any(Component.class))).thenReturn(Optional.<MovedFilesRepository.OriginalFile>absent()); + + TrackerRawInputFactory rawInputFactory = new TrackerRawInputFactory(treeRootHolder, reportReader, fileSourceRepository, new CommonRuleEngineImpl(), issueFilter); + TrackerBaseInputFactory baseInputFactory = new TrackerBaseInputFactory(issuesLoader, dbTester.getDbClient(), movedFilesRepository); + TrackerMergeBranchInputFactory mergeInputFactory = new TrackerMergeBranchInputFactory(mergeIssuesLoader, dbTester.getDbClient()); + tracker = new TrackerExecution(baseInputFactory, rawInputFactory, new Tracker<>()); + shortBranchTracker = new ShortBranchTrackerExecution(baseInputFactory, rawInputFactory, mergeInputFactory, new Tracker<>()); + trackingDelegator = new IssueTrackingDelegator(shortBranchTracker, tracker, analysisMetadataHolder); treeRootHolder.setRoot(PROJECT); issueCache = new IssueCache(temp.newFile(), System2.INSTANCE); when(analysisMetadataHolder.isIncrementalAnalysis()).thenReturn(false); + when(analysisMetadataHolder.getBranch()).thenReturn(java.util.Optional.of(new DefaultBranchImpl())); when(issueFilter.accept(any(DefaultIssue.class), eq(FILE))).thenReturn(true); - when(movedFilesRepository.getOriginalFile(any(Component.class))).thenReturn(Optional.<MovedFilesRepository.OriginalFile>absent()); - underTest = new IntegrateIssuesVisitor(tracker, issueCache, issueLifecycle, issueVisitors, componentsWithUnprocessedIssues, - movedFilesRepository, baseIssuesLoader, analysisMetadataHolder); + underTest = new IntegrateIssuesVisitor(issueCache, issueLifecycle, issueVisitors, issuesLoader, analysisMetadataHolder, trackingDelegator); } @Test public void process_issues_on_incremental_mode() { when(analysisMetadataHolder.isIncrementalAnalysis()).thenReturn(true); - componentsWithUnprocessedIssues.setUuids(Collections.singleton(FILE_UUID)); Component file = builder(Component.Type.FILE, FILE_REF) .setKey(FILE_KEY) @@ -157,20 +160,19 @@ public class IntegrateIssuesVisitorTest { underTest.visitAny(file); - verify(issueLifecycle).updateExistingOpenissue(defaultIssueCaptor.capture()); - assertThat(defaultIssueCaptor.getValue().ruleKey().rule()).isEqualTo("x1"); - verify(issueLifecycle).doAutomaticTransition(defaultIssueCaptor.capture()); assertThat(defaultIssueCaptor.getValue().ruleKey().rule()).isEqualTo("x1"); assertThat(newArrayList(issueCache.traverse())).hasSize(1); - assertThat(componentsWithUnprocessedIssues.getUuids()).isEmpty(); } @Test - public void process_new_issue() throws Exception { - componentsWithUnprocessedIssues.setUuids(Collections.<String>emptySet()); + public void process_short_branch_issues() { + //TODO + } + @Test + public void process_new_issue() throws Exception { ScannerReport.Issue reportIssue = ScannerReport.Issue.newBuilder() .setMsg("the message") .setRuleRepository("xoo") @@ -189,12 +191,10 @@ public class IntegrateIssuesVisitorTest { assertThat(defaultIssueCaptor.getValue().ruleKey().rule()).isEqualTo("S001"); assertThat(newArrayList(issueCache.traverse())).hasSize(1); - assertThat(componentsWithUnprocessedIssues.getUuids()).isEmpty(); } @Test public void process_existing_issue() throws Exception { - componentsWithUnprocessedIssues.setUuids(newHashSet(FILE_UUID)); RuleKey ruleKey = RuleTesting.XOO_X1; // Issue from db has severity major @@ -224,12 +224,10 @@ public class IntegrateIssuesVisitorTest { assertThat(issues).hasSize(1); assertThat(issues.get(0).severity()).isEqualTo(Severity.BLOCKER); - assertThat(componentsWithUnprocessedIssues.getUuids()).isEmpty(); } @Test public void execute_issue_visitors() throws Exception { - componentsWithUnprocessedIssues.setUuids(Collections.<String>emptySet()); ScannerReport.Issue reportIssue = ScannerReport.Issue.newBuilder() .setMsg("the message") .setRuleRepository("xoo") @@ -249,7 +247,6 @@ public class IntegrateIssuesVisitorTest { @Test public void close_unmatched_base_issue() throws Exception { - componentsWithUnprocessedIssues.setUuids(newHashSet(FILE_UUID)); RuleKey ruleKey = RuleTesting.XOO_X1; addBaseIssue(ruleKey); @@ -261,20 +258,19 @@ public class IntegrateIssuesVisitorTest { assertThat(defaultIssueCaptor.getValue().isBeingClosed()).isTrue(); List<DefaultIssue> issues = newArrayList(issueCache.traverse()); assertThat(issues).hasSize(1); - - assertThat(componentsWithUnprocessedIssues.getUuids()).isEmpty(); } @Test public void remove_uuid_of_original_file_from_componentsWithUnprocessedIssues_if_component_has_one() { String originalFileUuid = "original file uuid"; - componentsWithUnprocessedIssues.setUuids(newHashSet(FILE_UUID, originalFileUuid)); when(movedFilesRepository.getOriginalFile(FILE)) .thenReturn(Optional.of(new MovedFilesRepository.OriginalFile(4851, originalFileUuid, "original file key"))); underTest.visitAny(FILE); + } + + private void addMergeIssue(RuleKey ruleKey) { - assertThat(componentsWithUnprocessedIssues.getUuids()).isEmpty(); } private void addBaseIssue(RuleKey ruleKey) { @@ -293,5 +289,4 @@ public class IntegrateIssuesVisitorTest { dbTester.getDbClient().issueDao().insert(dbTester.getSession(), issue); dbTester.getSession().commit(); } - } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IssueTrackingDelegatorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IssueTrackingDelegatorTest.java new file mode 100644 index 00000000000..e484c69fb4a --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IssueTrackingDelegatorTest.java @@ -0,0 +1,78 @@ +/* + * 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 static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +import java.util.Optional; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.sonar.db.component.BranchType; +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.component.Component; + +public class IssueTrackingDelegatorTest { + @Mock + private ShortBranchTrackerExecution shortBranchTracker; + @Mock + private TrackerExecution tracker; + @Mock + private AnalysisMetadataHolder analysisMetadataHolder; + @Mock + private Component component; + + private IssueTrackingDelegator underTest; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + underTest = new IssueTrackingDelegator(shortBranchTracker, tracker, analysisMetadataHolder); + } + + @Test + public void delegate_regular_tracker() { + when(analysisMetadataHolder.getBranch()).thenReturn(Optional.empty()); + + underTest.track(component); + + verify(tracker).track(component); + verifyZeroInteractions(shortBranchTracker); + } + + @Test + public void delegate_short_branch_tracker() { + Branch branch = mock(Branch.class); + when(branch.getType()).thenReturn(BranchType.SHORT); + when(analysisMetadataHolder.getBranch()).thenReturn(Optional.of(branch)); + + underTest.track(component); + + verify(shortBranchTracker).track(component); + verifyZeroInteractions(tracker); + + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/RemoveProcessedComponentsVisitorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/RemoveProcessedComponentsVisitorTest.java new file mode 100644 index 00000000000..366fa67a145 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/RemoveProcessedComponentsVisitorTest.java @@ -0,0 +1,72 @@ +/* + * 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 static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import org.junit.Before; +import org.junit.Test; +import org.sonar.server.computation.task.projectanalysis.component.Component; +import org.sonar.server.computation.task.projectanalysis.filemove.MovedFilesRepository; +import org.sonar.server.computation.task.projectanalysis.filemove.MovedFilesRepository.OriginalFile; + +import com.google.common.base.Optional; + +public class RemoveProcessedComponentsVisitorTest { + private static final String UUID = "uuid"; + private ComponentsWithUnprocessedIssues componentsWithUnprocessedIssues = mock(ComponentsWithUnprocessedIssues.class); + private MovedFilesRepository movedFilesRepository = mock(MovedFilesRepository.class); + private Component component = mock(Component.class); + private RemoveProcessedComponentsVisitor underTest = new RemoveProcessedComponentsVisitor(componentsWithUnprocessedIssues, movedFilesRepository); + + @Before + public void setUp() { + when(component.getUuid()).thenReturn(UUID); + } + + @Test + public void remove_processed_files() { + when(movedFilesRepository.getOriginalFile(any(Component.class))).thenReturn(Optional.absent()); + underTest.afterComponent(component); + + verify(movedFilesRepository).getOriginalFile(component); + verify(componentsWithUnprocessedIssues).remove(UUID); + verifyNoMoreInteractions(componentsWithUnprocessedIssues); + } + + @Test + public void also_remove_moved_files() { + String uuid2 = "uuid2"; + OriginalFile movedFile = new OriginalFile(0, uuid2, "key"); + when(movedFilesRepository.getOriginalFile(any(Component.class))).thenReturn(Optional.of(movedFile)); + + underTest.afterComponent(component); + + verify(movedFilesRepository).getOriginalFile(component); + verify(componentsWithUnprocessedIssues).remove(UUID); + verify(componentsWithUnprocessedIssues).remove(uuid2); + + verifyNoMoreInteractions(componentsWithUnprocessedIssues); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchTrackerExecutionTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchTrackerExecutionTest.java new file mode 100644 index 00000000000..6947b4c4574 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchTrackerExecutionTest.java @@ -0,0 +1,132 @@ +/* + * 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 static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; +import static org.sonar.server.computation.task.projectanalysis.component.ReportComponent.builder; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +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.tracking.BlockHashSequence; +import org.sonar.core.issue.tracking.Input; +import org.sonar.core.issue.tracking.LineHashSequence; +import org.sonar.core.issue.tracking.Tracker; +import org.sonar.core.issue.tracking.Tracking; +import org.sonar.db.rule.RuleTesting; +import org.sonar.server.computation.task.projectanalysis.component.Component; + +public class ShortBranchTrackerExecutionTest { + static final String FILE_UUID = "FILE_UUID"; + static final String FILE_KEY = "FILE_KEY"; + static final int FILE_REF = 2; + + static final Component FILE = builder(Component.Type.FILE, FILE_REF) + .setKey(FILE_KEY) + .setUuid(FILE_UUID) + .build(); + + @Mock + private TrackerRawInputFactory rawFactory; + @Mock + private TrackerBaseInputFactory baseFactory; + @Mock + private TrackerMergeBranchInputFactory mergeFactory; + + private ShortBranchTrackerExecution underTest; + + private List<DefaultIssue> rawIssues = new ArrayList<>(); + private List<DefaultIssue> baseIssues = new ArrayList<>(); + private List<DefaultIssue> mergeBranchIssues = new ArrayList<>(); + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + when(rawFactory.create(FILE)).thenReturn(createInput(rawIssues)); + when(baseFactory.create(FILE)).thenReturn(createInput(baseIssues)); + when(mergeFactory.create(FILE)).thenReturn(createInput(mergeBranchIssues)); + + Tracker<DefaultIssue, DefaultIssue> tracker = new Tracker<>(); + underTest = new ShortBranchTrackerExecution(baseFactory, rawFactory, mergeFactory, tracker); + } + + @Test + public void simple_tracking() { + rawIssues.add(createIssue(1, RuleTesting.XOO_X1)); + Tracking<DefaultIssue, DefaultIssue> tracking = underTest.track(FILE); + assertThat(tracking.getUnmatchedBases()).isEmpty(); + assertThat(tracking.getMatchedRaws()).isEmpty(); + assertThat(tracking.getUnmatchedRaws()).containsOnly(rawIssues.get(0)); + } + + @Test + public void tracking_with_all_results() { + rawIssues.add(createIssue(1, RuleTesting.XOO_X1)); + rawIssues.add(createIssue(2, RuleTesting.XOO_X2)); + rawIssues.add(createIssue(3, RuleTesting.XOO_X3)); + + mergeBranchIssues.add(rawIssues.get(0)); + + baseIssues.add(rawIssues.get(0)); + baseIssues.add(rawIssues.get(1)); + + Tracking<DefaultIssue, DefaultIssue> tracking = underTest.track(FILE); + assertThat(tracking.getMatchedRaws()).isEqualTo(Collections.singletonMap(rawIssues.get(1), rawIssues.get(1))); + assertThat(tracking.getUnmatchedRaws()).containsOnly(rawIssues.get(2)); + } + + private DefaultIssue createIssue(int line, RuleKey ruleKey) { + DefaultIssue issue = new DefaultIssue() + .setRuleKey(ruleKey) + .setLine(line) + .setMessage("msg" + line); + return issue; + } + + private Input<DefaultIssue> createInput(Collection<DefaultIssue> issues) { + return new Input<DefaultIssue>() { + @Override + public LineHashSequence getLineHashSequence() { + return LineHashSequence.createForLines(Arrays.asList("line1", "line2", "line3")); + } + + @Override + public BlockHashSequence getBlockHashSequence() { + return BlockHashSequence.create(getLineHashSequence()); + } + + @Override + public Collection<DefaultIssue> getIssues() { + return issues; + } + }; + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/TrackerBaseInputFactoryTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/TrackerBaseInputFactoryTest.java index db5b0aa1319..7fc57673ce0 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/TrackerBaseInputFactoryTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/TrackerBaseInputFactoryTest.java @@ -39,14 +39,14 @@ public class TrackerBaseInputFactoryTest { private static final String FILE_UUID = "uuid"; private static final ReportComponent FILE = ReportComponent.builder(Component.Type.FILE, 1).setUuid(FILE_UUID).build(); - private BaseIssuesLoader baseIssuesLoader = mock(BaseIssuesLoader.class); + private ComponentIssuesLoader issuesLoader = mock(ComponentIssuesLoader.class); private DbClient dbClient = mock(DbClient.class); private DbSession dbSession = mock(DbSession.class); private FileSourceDao fileSourceDao = mock(FileSourceDao.class); private MovedFilesRepository movedFilesRepository = mock(MovedFilesRepository.class); - private TrackerBaseInputFactory underTest = new TrackerBaseInputFactory(baseIssuesLoader, dbClient, movedFilesRepository); + private TrackerBaseInputFactory underTest = new TrackerBaseInputFactory(issuesLoader, dbClient, movedFilesRepository); @Before public void setUp() throws Exception { @@ -68,8 +68,7 @@ public class TrackerBaseInputFactoryTest { String originalUuid = "original uuid"; when(movedFilesRepository.getOriginalFile(FILE)).thenReturn( - Optional.of(new MovedFilesRepository.OriginalFile(6542, originalUuid, "original key")) - ); + Optional.of(new MovedFilesRepository.OriginalFile(6542, originalUuid, "original key"))); underTest.create(FILE).getLineHashSequence(); @@ -81,7 +80,7 @@ public class TrackerBaseInputFactoryTest { public void create_returns_Input_which_retrieves_issues_of_specified_file_component_when_it_has_no_original_file() { underTest.create(FILE).getIssues(); - verify(baseIssuesLoader).loadForComponentUuid(FILE_UUID); + verify(issuesLoader).loadForComponentUuid(FILE_UUID); } @Test @@ -89,12 +88,11 @@ public class TrackerBaseInputFactoryTest { String originalUuid = "original uuid"; when(movedFilesRepository.getOriginalFile(FILE)).thenReturn( - Optional.of(new MovedFilesRepository.OriginalFile(6542, originalUuid, "original key")) - ); + Optional.of(new MovedFilesRepository.OriginalFile(6542, originalUuid, "original key"))); underTest.create(FILE).getIssues(); - verify(baseIssuesLoader).loadForComponentUuid(originalUuid); - verify(baseIssuesLoader, times(0)).loadForComponentUuid(FILE_UUID); + verify(issuesLoader).loadForComponentUuid(originalUuid); + verify(issuesLoader, times(0)).loadForComponentUuid(FILE_UUID); } } |