diff options
author | Julien HENRY <julien.henry@sonarsource.com> | 2015-02-25 14:04:39 +0100 |
---|---|---|
committer | Julien HENRY <julien.henry@sonarsource.com> | 2015-02-25 14:04:39 +0100 |
commit | 908b9d0b5b9c21fabd10d697a5466bcc12d80d83 (patch) | |
tree | e95396347367ae8e3e6ea8336aad505f91214ee8 /sonar-batch | |
parent | 3414d95ee371418e1e0e45b62f5538a401c33a53 (diff) | |
download | sonarqube-908b9d0b5b9c21fabd10d697a5466bcc12d80d83.tar.gz sonarqube-908b9d0b5b9c21fabd10d697a5466bcc12d80d83.zip |
SONAR-5927, SONAR-6012 Rework incremental preview mode to speedup issue retrieval
Diffstat (limited to 'sonar-batch')
29 files changed, 805 insertions, 334 deletions
diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/DefaultAnalysisMode.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/DefaultAnalysisMode.java index 2b5e7bd5577..b13e7b8bab4 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/DefaultAnalysisMode.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/DefaultAnalysisMode.java @@ -26,6 +26,7 @@ import org.sonar.api.batch.AnalysisMode; import org.sonar.batch.mediumtest.BatchMediumTester; import java.text.MessageFormat; +import java.util.Map; /** * @since 4.0 @@ -38,8 +39,8 @@ public class DefaultAnalysisMode implements AnalysisMode { private boolean incremental; private boolean mediumTestMode; - public DefaultAnalysisMode(BootstrapProperties bootstrapProps) { - init(bootstrapProps); + public DefaultAnalysisMode(Map<String, String> props) { + init(props); } public boolean isDb() { @@ -60,17 +61,17 @@ public class DefaultAnalysisMode implements AnalysisMode { return mediumTestMode; } - private void init(BootstrapProperties bootstrapProps) { - if (bootstrapProps.properties().containsKey(CoreProperties.DRY_RUN)) { + private void init(Map<String, String> props) { + if (props.containsKey(CoreProperties.DRY_RUN)) { LOG.warn(MessageFormat.format("Property {0} is deprecated. Please use {1} instead.", CoreProperties.DRY_RUN, CoreProperties.ANALYSIS_MODE)); - preview = "true".equals(bootstrapProps.property(CoreProperties.DRY_RUN)); + preview = "true".equals(props.get(CoreProperties.DRY_RUN)); incremental = false; } else { - String mode = bootstrapProps.property(CoreProperties.ANALYSIS_MODE); + String mode = props.get(CoreProperties.ANALYSIS_MODE); preview = CoreProperties.ANALYSIS_MODE_PREVIEW.equals(mode); incremental = CoreProperties.ANALYSIS_MODE_INCREMENTAL.equals(mode); } - mediumTestMode = "true".equals(bootstrapProps.property(BatchMediumTester.MEDIUM_TEST_ENABLED)); + mediumTestMode = "true".equals(props.get(BatchMediumTester.MEDIUM_TEST_ENABLED)); if (incremental) { LOG.info("Incremental mode"); } else if (preview) { @@ -81,7 +82,7 @@ public class DefaultAnalysisMode implements AnalysisMode { } // To stay compatible with plugins that use the old property to check mode if (incremental || preview) { - bootstrapProps.properties().put(CoreProperties.DRY_RUN, "true"); + props.put(CoreProperties.DRY_RUN, "true"); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java index 80525697653..7cf0bd61ef8 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/GlobalContainer.java @@ -19,14 +19,6 @@ */ package org.sonar.batch.bootstrap; -import org.sonar.batch.components.PastSnapshotFinder; - -import org.sonar.batch.deprecated.components.PastSnapshotFinderByDate; -import org.sonar.batch.deprecated.components.PastSnapshotFinderByDays; -import org.sonar.batch.deprecated.components.PastSnapshotFinderByPreviousAnalysis; -import org.sonar.batch.deprecated.components.PastSnapshotFinderByPreviousVersion; -import org.sonar.batch.deprecated.components.PastSnapshotFinderByVersion; -import org.sonar.batch.repository.user.UserRepository; import org.sonar.api.Plugin; import org.sonar.api.config.EmailSettings; import org.sonar.api.platform.ComponentContainer; @@ -36,14 +28,21 @@ import org.sonar.api.utils.HttpDownloader; import org.sonar.api.utils.System2; import org.sonar.api.utils.UriReader; import org.sonar.api.utils.internal.TempFolderCleaner; +import org.sonar.batch.components.PastSnapshotFinder; +import org.sonar.batch.deprecated.components.PastSnapshotFinderByDate; +import org.sonar.batch.deprecated.components.PastSnapshotFinderByDays; +import org.sonar.batch.deprecated.components.PastSnapshotFinderByPreviousAnalysis; +import org.sonar.batch.deprecated.components.PastSnapshotFinderByPreviousVersion; +import org.sonar.batch.deprecated.components.PastSnapshotFinderByVersion; import org.sonar.batch.platform.DefaultServer; import org.sonar.batch.repository.DefaultGlobalRepositoriesLoader; -import org.sonar.batch.repository.DefaultPreviousIssuesLoader; import org.sonar.batch.repository.DefaultProjectRepositoriesLoader; +import org.sonar.batch.repository.DefaultServerIssuesLoader; import org.sonar.batch.repository.GlobalRepositoriesLoader; import org.sonar.batch.repository.GlobalRepositoriesProvider; -import org.sonar.batch.repository.PreviousIssuesLoader; import org.sonar.batch.repository.ProjectRepositoriesLoader; +import org.sonar.batch.repository.ServerIssuesLoader; +import org.sonar.batch.repository.user.UserRepository; import org.sonar.core.cluster.NullQueue; import org.sonar.core.config.Logback; import org.sonar.core.i18n.DefaultI18n; @@ -81,7 +80,7 @@ public class GlobalContainer extends ComponentContainer { @Override protected void doBeforeStart() { BootstrapProperties bootstrapProps = new BootstrapProperties(bootstrapProperties); - DefaultAnalysisMode analysisMode = new DefaultAnalysisMode(bootstrapProps); + DefaultAnalysisMode analysisMode = new DefaultAnalysisMode(bootstrapProps.properties()); add(bootstrapProps, analysisMode); addBootstrapComponents(); if (analysisMode.isDb()) { @@ -117,8 +116,8 @@ public class GlobalContainer extends ComponentContainer { if (getComponentByType(ProjectRepositoriesLoader.class) == null) { add(DefaultProjectRepositoriesLoader.class); } - if (getComponentByType(PreviousIssuesLoader.class) == null) { - add(DefaultPreviousIssuesLoader.class); + if (getComponentByType(ServerIssuesLoader.class) == null) { + add(DefaultServerIssuesLoader.class); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/InitialOpenIssuesStack.java b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/InitialOpenIssuesStack.java index acc9ebf21b8..6d246b74606 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/InitialOpenIssuesStack.java +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/InitialOpenIssuesStack.java @@ -49,11 +49,11 @@ public class InitialOpenIssuesStack implements BatchExtension { return this; } - public List<PreviousIssue> selectAndRemoveIssues(String componentKey) { + public List<ServerIssue> selectAndRemoveIssues(String componentKey) { Iterable<IssueDto> issues = issuesCache.values(componentKey); - List<PreviousIssue> result = newArrayList(); + List<ServerIssue> result = newArrayList(); for (IssueDto issue : issues) { - result.add(new PreviousIssueFromDb(issue)); + result.add(new ServerIssueFromDb(issue)); } issuesCache.clear(componentKey); return result; diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/IssueTracking.java b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/IssueTracking.java index 2c1c49afa97..54c7970bc34 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/IssueTracking.java +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/IssueTracking.java @@ -44,7 +44,7 @@ public class IssueTracking implements BatchComponent { /** * @param sourceHashHolder Null when working on resource that is not a file (directory/project) */ - public IssueTrackingResult track(@Nullable SourceHashHolder sourceHashHolder, Collection<PreviousIssue> previousIssues, Collection<DefaultIssue> newIssues) { + public IssueTrackingResult track(@Nullable SourceHashHolder sourceHashHolder, Collection<ServerIssue> previousIssues, Collection<DefaultIssue> newIssues) { IssueTrackingResult result = new IssueTrackingResult(); if (sourceHashHolder != null) { @@ -69,7 +69,7 @@ public class IssueTracking implements BatchComponent { } @VisibleForTesting - void mapIssues(Collection<DefaultIssue> newIssues, @Nullable Collection<PreviousIssue> previousIssues, @Nullable SourceHashHolder sourceHashHolder, IssueTrackingResult result) { + void mapIssues(Collection<DefaultIssue> newIssues, @Nullable Collection<ServerIssue> previousIssues, @Nullable SourceHashHolder sourceHashHolder, IssueTrackingResult result) { boolean hasLastScan = false; if (previousIssues != null) { @@ -89,8 +89,8 @@ public class IssueTracking implements BatchComponent { } } - private void mapLastIssues(Collection<DefaultIssue> newIssues, Collection<PreviousIssue> previousIssues, IssueTrackingResult result) { - for (PreviousIssue lastIssue : previousIssues) { + private void mapLastIssues(Collection<DefaultIssue> newIssues, Collection<ServerIssue> previousIssues, IssueTrackingResult result) { + for (ServerIssue lastIssue : previousIssues) { result.addUnmatched(lastIssue); } @@ -118,7 +118,7 @@ public class IssueTracking implements BatchComponent { RollingFileHashes b = RollingFileHashes.create(hashedSource, 5); Multimap<Integer, DefaultIssue> newIssuesByLines = newIssuesByLines(newIssues, rec, result); - Multimap<Integer, PreviousIssue> lastIssuesByLines = lastIssuesByLines(result.unmatched(), rec); + Multimap<Integer, ServerIssue> lastIssuesByLines = lastIssuesByLines(result.unmatched(), rec); Map<Integer, HashOccurrence> map = Maps.newHashMap(); @@ -204,10 +204,10 @@ public class IssueTracking implements BatchComponent { } } - private void map(Collection<DefaultIssue> newIssues, Collection<PreviousIssue> previousIssues, IssueTrackingResult result) { + private void map(Collection<DefaultIssue> newIssues, Collection<ServerIssue> previousIssues, IssueTrackingResult result) { for (DefaultIssue newIssue : newIssues) { if (isNotAlreadyMapped(newIssue, result)) { - for (PreviousIssue previousIssue : previousIssues) { + for (ServerIssue previousIssue : previousIssues) { if (isNotAlreadyMapped(previousIssue, result) && Objects.equal(newIssue.ruleKey(), previousIssue.ruleKey())) { mapIssue(newIssue, previousIssue, result); break; @@ -227,9 +227,9 @@ public class IssueTracking implements BatchComponent { return newIssuesByLines; } - private Multimap<Integer, PreviousIssue> lastIssuesByLines(Collection<PreviousIssue> previousIssues, IssueTrackingBlocksRecognizer rec) { - Multimap<Integer, PreviousIssue> previousIssuesByLines = LinkedHashMultimap.create(); - for (PreviousIssue previousIssue : previousIssues) { + private Multimap<Integer, ServerIssue> lastIssuesByLines(Collection<ServerIssue> previousIssues, IssueTrackingBlocksRecognizer rec) { + Multimap<Integer, ServerIssue> previousIssuesByLines = LinkedHashMultimap.create(); + for (ServerIssue previousIssue : previousIssues) { if (rec.isValidLineInReference(previousIssue.line())) { previousIssuesByLines.put(previousIssue.line(), previousIssue); } @@ -237,8 +237,8 @@ public class IssueTracking implements BatchComponent { return previousIssuesByLines; } - private PreviousIssue findLastIssueWithSameChecksum(DefaultIssue newIssue, Collection<PreviousIssue> previousIssues) { - for (PreviousIssue previousIssue : previousIssues) { + private ServerIssue findLastIssueWithSameChecksum(DefaultIssue newIssue, Collection<ServerIssue> previousIssues) { + for (ServerIssue previousIssue : previousIssues) { if (isSameChecksum(newIssue, previousIssue)) { return previousIssue; } @@ -246,8 +246,8 @@ public class IssueTracking implements BatchComponent { return null; } - private PreviousIssue findLastIssueWithSameLineAndMessage(DefaultIssue newIssue, Collection<PreviousIssue> previousIssues) { - for (PreviousIssue previousIssue : previousIssues) { + private ServerIssue findLastIssueWithSameLineAndMessage(DefaultIssue newIssue, Collection<ServerIssue> previousIssues) { + for (ServerIssue previousIssue : previousIssues) { if (isSameLine(newIssue, previousIssue) && isSameMessage(newIssue, previousIssue)) { return previousIssue; } @@ -255,8 +255,8 @@ public class IssueTracking implements BatchComponent { return null; } - private PreviousIssue findLastIssueWithSameChecksumAndMessage(DefaultIssue newIssue, Collection<PreviousIssue> previousIssues) { - for (PreviousIssue previousIssue : previousIssues) { + private ServerIssue findLastIssueWithSameChecksumAndMessage(DefaultIssue newIssue, Collection<ServerIssue> previousIssues) { + for (ServerIssue previousIssue : previousIssues) { if (isSameChecksum(newIssue, previousIssue) && isSameMessage(newIssue, previousIssue)) { return previousIssue; } @@ -264,15 +264,15 @@ public class IssueTracking implements BatchComponent { return null; } - private PreviousIssue findLastIssueWithSameLineAndChecksum(DefaultIssue newIssue, IssueTrackingResult result) { - Collection<PreviousIssue> sameRuleAndSameLineAndSameChecksum = result.unmatchedForRuleAndForLineAndForChecksum(newIssue.ruleKey(), newIssue.line(), newIssue.checksum()); + private ServerIssue findLastIssueWithSameLineAndChecksum(DefaultIssue newIssue, IssueTrackingResult result) { + Collection<ServerIssue> sameRuleAndSameLineAndSameChecksum = result.unmatchedForRuleAndForLineAndForChecksum(newIssue.ruleKey(), newIssue.line(), newIssue.checksum()); if (!sameRuleAndSameLineAndSameChecksum.isEmpty()) { return sameRuleAndSameLineAndSameChecksum.iterator().next(); } return null; } - private boolean isNotAlreadyMapped(PreviousIssue previousIssue, IssueTrackingResult result) { + private boolean isNotAlreadyMapped(ServerIssue previousIssue, IssueTrackingResult result) { return result.unmatched().contains(previousIssue); } @@ -280,19 +280,19 @@ public class IssueTracking implements BatchComponent { return !result.isMatched(newIssue); } - private boolean isSameChecksum(DefaultIssue newIssue, PreviousIssue previousIssue) { + private boolean isSameChecksum(DefaultIssue newIssue, ServerIssue previousIssue) { return Objects.equal(previousIssue.checksum(), newIssue.checksum()); } - private boolean isSameLine(DefaultIssue newIssue, PreviousIssue previousIssue) { + private boolean isSameLine(DefaultIssue newIssue, ServerIssue previousIssue) { return Objects.equal(previousIssue.line(), newIssue.line()); } - private boolean isSameMessage(DefaultIssue newIssue, PreviousIssue previousIssue) { + private boolean isSameMessage(DefaultIssue newIssue, ServerIssue previousIssue) { return Objects.equal(newIssue.message(), previousIssue.message()); } - private void mapIssue(DefaultIssue issue, @Nullable PreviousIssue ref, IssueTrackingResult result) { + private void mapIssue(DefaultIssue issue, @Nullable ServerIssue ref, IssueTrackingResult result) { if (ref != null) { result.setMatch(issue, ref); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/IssueTrackingDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/IssueTrackingDecorator.java index c1ae052034c..406c9d0518f 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/IssueTrackingDecorator.java +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/IssueTrackingDecorator.java @@ -125,7 +125,7 @@ public class IssueTrackingDecorator implements Decorator { // issues = all the issues created by rule engines during this module scan and not excluded by filters // all the issues that are not closed in db before starting this module scan, including manual issues - Collection<PreviousIssue> dbOpenIssues = initialOpenIssues.selectAndRemoveIssues(resource.getEffectiveKey()); + Collection<ServerIssue> dbOpenIssues = initialOpenIssues.selectAndRemoveIssues(resource.getEffectiveKey()); SourceHashHolder sourceHashHolder = null; if (ResourceUtils.isFile(resource)) { @@ -159,7 +159,7 @@ public class IssueTrackingDecorator implements Decorator { @VisibleForTesting protected void mergeMatched(IssueTrackingResult result) { for (DefaultIssue issue : result.matched()) { - IssueDto ref = ((PreviousIssueFromDb) result.matching(issue)).getDto(); + IssueDto ref = ((ServerIssueFromDb) result.matching(issue)).getDto(); // invariant fields issue.setKey(ref.getKee()); @@ -208,9 +208,9 @@ public class IssueTrackingDecorator implements Decorator { } } - private void addUnmatched(Collection<PreviousIssue> unmatchedIssues, SourceHashHolder sourceHashHolder, Collection<DefaultIssue> issues) { - for (PreviousIssue unmatchedIssue : unmatchedIssues) { - IssueDto unmatchedDto = ((PreviousIssueFromDb) unmatchedIssue).getDto(); + private void addUnmatched(Collection<ServerIssue> unmatchedIssues, SourceHashHolder sourceHashHolder, Collection<DefaultIssue> issues) { + for (ServerIssue unmatchedIssue : unmatchedIssues) { + IssueDto unmatchedDto = ((ServerIssueFromDb) unmatchedIssue).getDto(); DefaultIssue unmatched = unmatchedDto.toDefaultIssue(); if (StringUtils.isNotBlank(unmatchedDto.getReporter()) && !Issue.STATUS_CLOSED.equals(unmatchedDto.getStatus())) { relocateManualIssue(unmatched, unmatchedDto, sourceHashHolder); diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/IssueTrackingResult.java b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/IssueTrackingResult.java index f7b4e8d119e..8fe3549e66b 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/IssueTrackingResult.java +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/IssueTrackingResult.java @@ -34,29 +34,29 @@ import java.util.HashMap; import java.util.Map; class IssueTrackingResult { - private final Map<String, PreviousIssue> unmatchedByKey = new HashMap<>(); - private final Map<RuleKey, Map<String, PreviousIssue>> unmatchedByRuleAndKey = new HashMap<>(); - private final Map<RuleKey, Map<Integer, Multimap<String, PreviousIssue>>> unmatchedByRuleAndLineAndChecksum = new HashMap<>(); - private final Map<DefaultIssue, PreviousIssue> matched = Maps.newIdentityHashMap(); + private final Map<String, ServerIssue> unmatchedByKey = new HashMap<>(); + private final Map<RuleKey, Map<String, ServerIssue>> unmatchedByRuleAndKey = new HashMap<>(); + private final Map<RuleKey, Map<Integer, Multimap<String, ServerIssue>>> unmatchedByRuleAndLineAndChecksum = new HashMap<>(); + private final Map<DefaultIssue, ServerIssue> matched = Maps.newIdentityHashMap(); - Collection<PreviousIssue> unmatched() { + Collection<ServerIssue> unmatched() { return unmatchedByKey.values(); } - Map<String, PreviousIssue> unmatchedByKeyForRule(RuleKey ruleKey) { - return unmatchedByRuleAndKey.containsKey(ruleKey) ? unmatchedByRuleAndKey.get(ruleKey) : Collections.<String, PreviousIssue>emptyMap(); + Map<String, ServerIssue> unmatchedByKeyForRule(RuleKey ruleKey) { + return unmatchedByRuleAndKey.containsKey(ruleKey) ? unmatchedByRuleAndKey.get(ruleKey) : Collections.<String, ServerIssue>emptyMap(); } - Collection<PreviousIssue> unmatchedForRuleAndForLineAndForChecksum(RuleKey ruleKey, @Nullable Integer line, @Nullable String checksum) { + Collection<ServerIssue> unmatchedForRuleAndForLineAndForChecksum(RuleKey ruleKey, @Nullable Integer line, @Nullable String checksum) { if (!unmatchedByRuleAndLineAndChecksum.containsKey(ruleKey)) { return Collections.emptyList(); } - Map<Integer, Multimap<String, PreviousIssue>> unmatchedForRule = unmatchedByRuleAndLineAndChecksum.get(ruleKey); + Map<Integer, Multimap<String, ServerIssue>> unmatchedForRule = unmatchedByRuleAndLineAndChecksum.get(ruleKey); Integer lineNotNull = line != null ? line : 0; if (!unmatchedForRule.containsKey(lineNotNull)) { return Collections.emptyList(); } - Multimap<String, PreviousIssue> unmatchedForRuleAndLine = unmatchedForRule.get(lineNotNull); + Multimap<String, ServerIssue> unmatchedForRuleAndLine = unmatchedForRule.get(lineNotNull); String checksumNotNull = StringUtils.defaultString(checksum, ""); if (!unmatchedForRuleAndLine.containsKey(checksumNotNull)) { return Collections.emptyList(); @@ -72,34 +72,34 @@ class IssueTrackingResult { return matched.containsKey(issue); } - PreviousIssue matching(DefaultIssue issue) { + ServerIssue matching(DefaultIssue issue) { return matched.get(issue); } - void addUnmatched(PreviousIssue i) { + void addUnmatched(ServerIssue i) { unmatchedByKey.put(i.key(), i); RuleKey ruleKey = i.ruleKey(); if (!unmatchedByRuleAndKey.containsKey(ruleKey)) { - unmatchedByRuleAndKey.put(ruleKey, new HashMap<String, PreviousIssue>()); - unmatchedByRuleAndLineAndChecksum.put(ruleKey, new HashMap<Integer, Multimap<String, PreviousIssue>>()); + unmatchedByRuleAndKey.put(ruleKey, new HashMap<String, ServerIssue>()); + unmatchedByRuleAndLineAndChecksum.put(ruleKey, new HashMap<Integer, Multimap<String, ServerIssue>>()); } unmatchedByRuleAndKey.get(ruleKey).put(i.key(), i); - Map<Integer, Multimap<String, PreviousIssue>> unmatchedForRule = unmatchedByRuleAndLineAndChecksum.get(ruleKey); + Map<Integer, Multimap<String, ServerIssue>> unmatchedForRule = unmatchedByRuleAndLineAndChecksum.get(ruleKey); Integer lineNotNull = lineNotNull(i); if (!unmatchedForRule.containsKey(lineNotNull)) { - unmatchedForRule.put(lineNotNull, HashMultimap.<String, PreviousIssue>create()); + unmatchedForRule.put(lineNotNull, HashMultimap.<String, ServerIssue>create()); } - Multimap<String, PreviousIssue> unmatchedForRuleAndLine = unmatchedForRule.get(lineNotNull); + Multimap<String, ServerIssue> unmatchedForRuleAndLine = unmatchedForRule.get(lineNotNull); String checksumNotNull = StringUtils.defaultString(i.checksum(), ""); unmatchedForRuleAndLine.put(checksumNotNull, i); } - private Integer lineNotNull(PreviousIssue i) { + private Integer lineNotNull(ServerIssue i) { Integer line = i.line(); return line != null ? line : 0; } - void setMatch(DefaultIssue issue, PreviousIssue matching) { + void setMatch(DefaultIssue issue, ServerIssue matching) { matched.put(issue, matching); RuleKey ruleKey = matching.ruleKey(); unmatchedByRuleAndKey.get(ruleKey).remove(matching.key()); diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/LocalIssueTracking.java b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/LocalIssueTracking.java index c4d9a65769f..7d67696196e 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/LocalIssueTracking.java +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/LocalIssueTracking.java @@ -24,6 +24,7 @@ import com.google.common.collect.Lists; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.BatchComponent; +import org.sonar.api.batch.AnalysisMode; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.rule.ActiveRule; import org.sonar.api.batch.rule.ActiveRules; @@ -39,11 +40,13 @@ import org.sonar.batch.issue.IssueCache; import org.sonar.batch.protocol.input.ProjectRepositories; import org.sonar.batch.scan.LastLineHashes; import org.sonar.batch.scan.filesystem.InputPathCache; +import org.sonar.core.component.ComponentKeys; import org.sonar.core.issue.IssueUpdater; import org.sonar.core.issue.workflow.IssueWorkflow; import java.util.ArrayList; import java.util.Collection; +import java.util.Date; public class LocalIssueTracking implements BatchComponent { @@ -58,12 +61,14 @@ public class LocalIssueTracking implements BatchComponent { private final ActiveRules activeRules; private final InputPathCache inputPathCache; private final ResourceCache resourceCache; - private final PreviousIssueRepository previousIssueCache; + private final ServerIssueRepository serverIssueRepository; private final ProjectRepositories projectRepositories; + private final AnalysisMode analysisMode; public LocalIssueTracking(ResourceCache resourceCache, IssueCache issueCache, IssueTracking tracking, LastLineHashes lastLineHashes, IssueWorkflow workflow, IssueUpdater updater, - ActiveRules activeRules, InputPathCache inputPathCache, PreviousIssueRepository previousIssueCache, ProjectRepositories projectRepositories) { + ActiveRules activeRules, InputPathCache inputPathCache, ServerIssueRepository serverIssueRepository, + ProjectRepositories projectRepositories, AnalysisMode analysisMode) { this.resourceCache = resourceCache; this.issueCache = issueCache; this.tracking = tracking; @@ -71,8 +76,9 @@ public class LocalIssueTracking implements BatchComponent { this.workflow = workflow; this.updater = updater; this.inputPathCache = inputPathCache; - this.previousIssueCache = previousIssueCache; + this.serverIssueRepository = serverIssueRepository; this.projectRepositories = projectRepositories; + this.analysisMode = analysisMode; this.changeContext = IssueChangeContext.createScan(((Project) resourceCache.getRoot().resource()).getAnalysisDate()); this.activeRules = activeRules; } @@ -83,7 +89,7 @@ public class LocalIssueTracking implements BatchComponent { return; } - previousIssueCache.load(); + serverIssueRepository.load(); for (BatchResource component : resourceCache.all()) { trackIssues(component); @@ -99,10 +105,15 @@ public class LocalIssueTracking implements BatchComponent { issueCache.clear(component.resource().getEffectiveKey()); // issues = all the issues created by rule engines during this module scan and not excluded by filters + if (analysisMode.isIncremental() && !component.isFile()) { + // No need to report issues on project or directories in preview mode since it is likely to be wrong anyway + return; + } + // all the issues that are not closed in db before starting this module scan, including manual issues - Collection<PreviousIssue> previousIssues = new ArrayList<>(); - for (org.sonar.batch.protocol.input.issues.PreviousIssue previousIssue : previousIssueCache.byComponent(component)) { - previousIssues.add(new PreviousIssueFromWs(previousIssue)); + Collection<ServerIssue> previousIssues = new ArrayList<>(); + for (org.sonar.batch.protocol.input.BatchInput.ServerIssue previousIssue : serverIssueRepository.byComponent(component)) { + previousIssues.add(new ServerIssueFromWs(previousIssue)); } SourceHashHolder sourceHashHolder = null; @@ -135,10 +146,10 @@ public class LocalIssueTracking implements BatchComponent { @VisibleForTesting protected void mergeMatched(IssueTrackingResult result) { for (DefaultIssue issue : result.matched()) { - org.sonar.batch.protocol.input.issues.PreviousIssue ref = ((PreviousIssueFromWs) result.matching(issue)).getDto(); + org.sonar.batch.protocol.input.BatchInput.ServerIssue ref = ((ServerIssueFromWs) result.matching(issue)).getDto(); // invariant fields - issue.setKey(ref.key()); + issue.setKey(ref.getKey()); // non-persisted fields issue.setNew(false); @@ -146,23 +157,23 @@ public class LocalIssueTracking implements BatchComponent { issue.setOnDisabledRule(false); // fields to update with old values - issue.setResolution(ref.resolution()); - issue.setStatus(ref.status()); - issue.setAssignee(ref.assigneeLogin()); - issue.setCreationDate(ref.creationDate()); + issue.setResolution(ref.hasResolution() ? ref.getResolution() : null); + issue.setStatus(ref.getStatus()); + issue.setAssignee(ref.hasAssigneeLogin() ? ref.getAssigneeLogin() : null); + issue.setCreationDate(new Date(ref.getCreationDate())); - if (ref.isManualSeverity()) { + if (ref.getManualSeverity()) { // Severity overriden by user - issue.setSeverity(ref.severity()); + issue.setSeverity(ref.getSeverity().name()); } } } - private void addUnmatched(Collection<PreviousIssue> unmatchedIssues, SourceHashHolder sourceHashHolder, Collection<DefaultIssue> issues) { - for (PreviousIssue unmatchedIssue : unmatchedIssues) { - org.sonar.batch.protocol.input.issues.PreviousIssue unmatchedPreviousIssue = ((PreviousIssueFromWs) unmatchedIssue).getDto(); + private void addUnmatched(Collection<ServerIssue> unmatchedIssues, SourceHashHolder sourceHashHolder, Collection<DefaultIssue> issues) { + for (ServerIssue unmatchedIssue : unmatchedIssues) { + org.sonar.batch.protocol.input.BatchInput.ServerIssue unmatchedPreviousIssue = ((ServerIssueFromWs) unmatchedIssue).getDto(); DefaultIssue unmatched = toUnmatchedIssue(unmatchedPreviousIssue); - if (unmatchedIssue.ruleKey().isManual() && !Issue.STATUS_CLOSED.equals(unmatchedPreviousIssue.status())) { + if (unmatchedIssue.ruleKey().isManual() && !Issue.STATUS_CLOSED.equals(unmatchedPreviousIssue.getStatus())) { relocateManualIssue(unmatched, unmatchedIssue, sourceHashHolder); } updateUnmatchedIssue(unmatched, false /* manual issues can be kept open */); @@ -171,26 +182,26 @@ public class LocalIssueTracking implements BatchComponent { } private void addIssuesOnDeletedComponents(Collection<DefaultIssue> issues) { - for (org.sonar.batch.protocol.input.issues.PreviousIssue previous : previousIssueCache.issuesOnMissingComponents()) { + for (org.sonar.batch.protocol.input.BatchInput.ServerIssue previous : serverIssueRepository.issuesOnMissingComponents()) { DefaultIssue dead = toUnmatchedIssue(previous); updateUnmatchedIssue(dead, true); issues.add(dead); } } - private DefaultIssue toUnmatchedIssue(org.sonar.batch.protocol.input.issues.PreviousIssue previous) { + private DefaultIssue toUnmatchedIssue(org.sonar.batch.protocol.input.BatchInput.ServerIssue serverIssue) { DefaultIssue issue = new DefaultIssue(); - issue.setKey(previous.key()); - issue.setStatus(previous.status()); - issue.setResolution(previous.resolution()); - issue.setMessage(previous.message()); - issue.setLine(previous.line()); - issue.setSeverity(previous.severity()); - issue.setAssignee(previous.assigneeLogin()); - issue.setComponentKey(previous.componentKey()); - issue.setManualSeverity(previous.isManualSeverity()); - issue.setCreationDate(previous.creationDate()); - issue.setRuleKey(RuleKey.of(previous.ruleRepo(), previous.ruleKey())); + issue.setKey(serverIssue.getKey()); + issue.setStatus(serverIssue.getStatus()); + issue.setResolution(serverIssue.hasResolution() ? serverIssue.getResolution() : null); + issue.setMessage(serverIssue.hasMsg() ? serverIssue.getMsg() : null); + issue.setLine(serverIssue.hasLine() ? serverIssue.getLine() : null); + issue.setSeverity(serverIssue.getSeverity().name()); + issue.setAssignee(serverIssue.hasAssigneeLogin() ? serverIssue.getAssigneeLogin() : null); + issue.setComponentKey(ComponentKeys.createEffectiveKey(serverIssue.getModuleKey(), serverIssue.hasPath() ? serverIssue.getPath() : null)); + issue.setManualSeverity(serverIssue.getManualSeverity()); + issue.setCreationDate(new Date(serverIssue.getCreationDate())); + issue.setRuleKey(RuleKey.of(serverIssue.getRuleRepository(), serverIssue.getRuleKey())); issue.setNew(false); return issue; } @@ -209,7 +220,7 @@ public class LocalIssueTracking implements BatchComponent { issue.setOnDisabledRule(isRemovedRule); } - private void relocateManualIssue(DefaultIssue newIssue, PreviousIssue oldIssue, SourceHashHolder sourceHashHolder) { + private void relocateManualIssue(DefaultIssue newIssue, ServerIssue oldIssue, SourceHashHolder sourceHashHolder) { Integer previousLine = oldIssue.line(); if (previousLine == null) { return; diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/PreviousIssueRepository.java b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/PreviousIssueRepository.java deleted file mode 100644 index ce9ff46803f..00000000000 --- a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/PreviousIssueRepository.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.batch.issue.tracking; - -import com.google.common.base.Function; -import org.sonar.api.BatchComponent; -import org.sonar.api.batch.InstantiationStrategy; -import org.sonar.api.batch.bootstrap.ProjectReactor; -import org.sonar.api.utils.log.Logger; -import org.sonar.api.utils.log.Loggers; -import org.sonar.api.utils.log.Profiler; -import org.sonar.batch.index.BatchResource; -import org.sonar.batch.index.Cache; -import org.sonar.batch.index.Caches; -import org.sonar.batch.index.ResourceCache; -import org.sonar.batch.protocol.input.issues.PreviousIssue; -import org.sonar.batch.repository.PreviousIssuesLoader; - -import javax.annotation.Nullable; - -@InstantiationStrategy(InstantiationStrategy.PER_BATCH) -public class PreviousIssueRepository implements BatchComponent { - - private static final Logger LOG = Loggers.get(PreviousIssueRepository.class); - - private final Caches caches; - private Cache<PreviousIssue> issuesCache; - private final PreviousIssuesLoader previousIssuesLoader; - private final ProjectReactor reactor; - private final ResourceCache resourceCache; - - public PreviousIssueRepository(Caches caches, PreviousIssuesLoader previousIssuesLoader, ProjectReactor reactor, ResourceCache resourceCache) { - this.caches = caches; - this.previousIssuesLoader = previousIssuesLoader; - this.reactor = reactor; - this.resourceCache = resourceCache; - } - - public void load() { - Profiler profiler = Profiler.create(LOG).startInfo("Load previous issues"); - this.issuesCache = caches.createCache("previousIssues"); - previousIssuesLoader.load(reactor, new Function<PreviousIssue, Void>() { - - @Override - public Void apply(@Nullable PreviousIssue issue) { - if (issue == null) { - return null; - } - String componentKey = issue.componentKey(); - BatchResource r = resourceCache.get(componentKey); - if (r == null) { - // Deleted resource - issuesCache.put(0, issue.key(), issue); - } else { - issuesCache.put(r.batchId(), issue.key(), issue); - } - return null; - } - }); - profiler.stopDebug(); - } - - public Iterable<PreviousIssue> byComponent(BatchResource component) { - return issuesCache.values(component.batchId()); - } - - public Iterable<PreviousIssue> issuesOnMissingComponents() { - return issuesCache.values(0); - } -} diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/PreviousIssue.java b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/ServerIssue.java index dcef8caaa5a..5c492edefe8 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/PreviousIssue.java +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/ServerIssue.java @@ -23,7 +23,7 @@ import org.sonar.api.rule.RuleKey; import javax.annotation.CheckForNull; -public interface PreviousIssue { +public interface ServerIssue { String key(); diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/PreviousIssueFromDb.java b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/ServerIssueFromDb.java index f20f420def4..d858d3a745f 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/PreviousIssueFromDb.java +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/ServerIssueFromDb.java @@ -22,11 +22,11 @@ package org.sonar.batch.issue.tracking; import org.sonar.api.rule.RuleKey; import org.sonar.core.issue.db.IssueDto; -public class PreviousIssueFromDb implements PreviousIssue { +public class ServerIssueFromDb implements ServerIssue { private IssueDto dto; - public PreviousIssueFromDb(IssueDto dto) { + public ServerIssueFromDb(IssueDto dto) { this.dto = dto; } diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/PreviousIssueFromWs.java b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/ServerIssueFromWs.java index a9a435abb8c..116c2aa90fd 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/PreviousIssueFromWs.java +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/ServerIssueFromWs.java @@ -21,41 +21,41 @@ package org.sonar.batch.issue.tracking; import org.sonar.api.rule.RuleKey; -public class PreviousIssueFromWs implements PreviousIssue { +public class ServerIssueFromWs implements ServerIssue { - private org.sonar.batch.protocol.input.issues.PreviousIssue dto; + private org.sonar.batch.protocol.input.BatchInput.ServerIssue dto; - public PreviousIssueFromWs(org.sonar.batch.protocol.input.issues.PreviousIssue dto) { + public ServerIssueFromWs(org.sonar.batch.protocol.input.BatchInput.ServerIssue dto) { this.dto = dto; } - public org.sonar.batch.protocol.input.issues.PreviousIssue getDto() { + public org.sonar.batch.protocol.input.BatchInput.ServerIssue getDto() { return dto; } @Override public String key() { - return dto.key(); + return dto.getKey(); } @Override public RuleKey ruleKey() { - return RuleKey.of(dto.ruleRepo(), dto.ruleKey()); + return RuleKey.of(dto.getRuleRepository(), dto.getRuleKey()); } @Override public String checksum() { - return dto.checksum(); + return dto.hasChecksum() ? dto.getChecksum() : null; } @Override public Integer line() { - return dto.line(); + return dto.hasLine() ? dto.getLine() : null; } @Override public String message() { - return dto.message(); + return dto.hasMsg() ? dto.getMsg() : null; } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/ServerIssueRepository.java b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/ServerIssueRepository.java new file mode 100644 index 00000000000..6fa0e45c368 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/ServerIssueRepository.java @@ -0,0 +1,122 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.batch.issue.tracking; + +import com.google.common.base.Function; +import org.sonar.api.BatchComponent; +import org.sonar.api.batch.AnalysisMode; +import org.sonar.api.batch.InstantiationStrategy; +import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.api.utils.log.Profiler; +import org.sonar.batch.index.BatchResource; +import org.sonar.batch.index.Cache; +import org.sonar.batch.index.Caches; +import org.sonar.batch.index.ResourceCache; +import org.sonar.batch.protocol.input.BatchInput.ServerIssue; +import org.sonar.batch.repository.ServerIssuesLoader; +import org.sonar.core.component.ComponentKeys; + +import javax.annotation.Nullable; + +import java.util.ArrayList; +import java.util.List; + +@InstantiationStrategy(InstantiationStrategy.PER_BATCH) +public class ServerIssueRepository implements BatchComponent { + + private static final Logger LOG = Loggers.get(ServerIssueRepository.class); + + private final Caches caches; + private Cache<ServerIssue> issuesCache; + private final ServerIssuesLoader previousIssuesLoader; + private final ProjectReactor reactor; + private final ResourceCache resourceCache; + private final AnalysisMode analysisMode; + + public ServerIssueRepository(Caches caches, ServerIssuesLoader previousIssuesLoader, ProjectReactor reactor, ResourceCache resourceCache, AnalysisMode analysisMode) { + this.caches = caches; + this.previousIssuesLoader = previousIssuesLoader; + this.reactor = reactor; + this.resourceCache = resourceCache; + this.analysisMode = analysisMode; + } + + public void load() { + if (analysisMode.isIncremental()) { + return; + } + Profiler profiler = Profiler.create(LOG).startInfo("Load server issues"); + this.issuesCache = caches.createCache("previousIssues"); + caches.registerValueCoder(ServerIssue.class, new ServerIssueValueCoder()); + previousIssuesLoader.load(reactor.getRoot().getKeyWithBranch(), new Function<ServerIssue, Void>() { + + @Override + public Void apply(@Nullable ServerIssue issue) { + if (issue == null) { + return null; + } + String componentKey = ComponentKeys.createEffectiveKey(issue.getModuleKey(), issue.hasPath() ? issue.getPath() : null); + BatchResource r = resourceCache.get(componentKey); + if (r == null) { + // Deleted resource + issuesCache.put(0, issue.getKey(), issue); + } else { + issuesCache.put(r.batchId(), issue.getKey(), issue); + } + return null; + } + }, false); + profiler.stopDebug(); + } + + public Iterable<ServerIssue> byComponent(BatchResource component) { + if (analysisMode.isIncremental()) { + if (!component.isFile()) { + throw new UnsupportedOperationException("Incremental mode should only get issues on files"); + } + Profiler profiler = Profiler.create(LOG).startInfo("Load server issues for " + component.resource().getPath()); + final List<ServerIssue> result = new ArrayList<>(); + previousIssuesLoader.load(component.key(), new Function<ServerIssue, Void>() { + + @Override + public Void apply(@Nullable ServerIssue issue) { + if (issue == null) { + return null; + } + result.add(issue); + return null; + } + }, true); + profiler.stopDebug(); + return result; + } else { + return issuesCache.values(component.batchId()); + } + } + + public Iterable<ServerIssue> issuesOnMissingComponents() { + if (analysisMode.isIncremental()) { + throw new UnsupportedOperationException("Only issues of analyzed components are loaded in incremental mode"); + } + return issuesCache.values(0); + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/ServerIssueValueCoder.java b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/ServerIssueValueCoder.java new file mode 100644 index 00000000000..321d09a852b --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/tracking/ServerIssueValueCoder.java @@ -0,0 +1,47 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.batch.issue.tracking; + +import com.persistit.Value; +import com.persistit.encoding.CoderContext; +import com.persistit.encoding.ValueCoder; +import com.persistit.exception.ConversionException; +import org.sonar.batch.protocol.input.BatchInput.ServerIssue; + +import java.io.IOException; + +public class ServerIssueValueCoder implements ValueCoder { + + @Override + public void put(Value value, Object object, CoderContext context) throws ConversionException { + ServerIssue issue = (ServerIssue) object; + value.putByteArray(issue.toByteArray()); + } + + @Override + public Object get(Value value, Class<?> clazz, CoderContext context) throws ConversionException { + try { + return ServerIssue.parseFrom(value.getByteArray()); + } catch (IOException e) { + throw new IllegalStateException("Unable to read issue from cache", e); + } + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java b/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java index 5f5f74b3a1a..f7517a7be8f 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java +++ b/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java @@ -34,13 +34,14 @@ import org.sonar.batch.bootstrap.TaskProperties; import org.sonar.batch.bootstrapper.Batch; import org.sonar.batch.bootstrapper.EnvironmentInformation; import org.sonar.batch.protocol.input.ActiveRule; +import org.sonar.batch.protocol.input.BatchInput.ServerIssue; import org.sonar.batch.protocol.input.FileData; import org.sonar.batch.protocol.input.GlobalRepositories; import org.sonar.batch.protocol.input.ProjectRepositories; -import org.sonar.batch.protocol.input.issues.PreviousIssue; import org.sonar.batch.repository.GlobalRepositoriesLoader; -import org.sonar.batch.repository.PreviousIssuesLoader; import org.sonar.batch.repository.ProjectRepositoriesLoader; +import org.sonar.batch.repository.ServerIssuesLoader; +import org.sonar.core.component.ComponentKeys; import org.sonar.core.plugins.DefaultPluginMetadata; import org.sonar.core.plugins.RemotePlugin; @@ -75,7 +76,7 @@ public class BatchMediumTester { private final FakeGlobalRepositoriesLoader globalRefProvider = new FakeGlobalRepositoriesLoader(); private final FakeProjectRepositoriesLoader projectRefProvider = new FakeProjectRepositoriesLoader(); private final FakePluginsRepository pluginsReferential = new FakePluginsRepository(); - private final FakePreviousIssuesLoader previousIssues = new FakePreviousIssuesLoader(); + private final FakeServerIssuesLoader serverIssues = new FakeServerIssuesLoader(); private final Map<String, String> bootstrapProperties = new HashMap<String, String>(); public BatchMediumTester build() { @@ -135,8 +136,8 @@ public class BatchMediumTester { return this; } - public BatchMediumTesterBuilder addPreviousIssue(PreviousIssue issue) { - previousIssues.getPreviousIssues().add(issue); + public BatchMediumTesterBuilder mockServerIssue(ServerIssue issue) { + serverIssues.getServerIssues().add(issue); return this; } @@ -158,7 +159,7 @@ public class BatchMediumTester { builder.pluginsReferential, builder.globalRefProvider, builder.projectRefProvider, - builder.previousIssues, + builder.serverIssues, new DefaultDebtModel()) .setBootstrapProperties(builder.bootstrapProperties) .build(); @@ -305,18 +306,20 @@ public class BatchMediumTester { } - private static class FakePreviousIssuesLoader implements PreviousIssuesLoader { + private static class FakeServerIssuesLoader implements ServerIssuesLoader { - List<PreviousIssue> previousIssues = new ArrayList<>(); + private List<ServerIssue> serverIssues = new ArrayList<>(); - public List<PreviousIssue> getPreviousIssues() { - return previousIssues; + public List<ServerIssue> getServerIssues() { + return serverIssues; } @Override - public void load(ProjectReactor reactor, Function<PreviousIssue, Void> consumer) { - for (PreviousIssue previousIssue : previousIssues) { - consumer.apply(previousIssue); + public void load(String componentKey, Function<ServerIssue, Void> consumer, boolean incremental) { + for (ServerIssue serverIssue : serverIssues) { + if (!incremental || ComponentKeys.createEffectiveKey(serverIssue.getModuleKey(), serverIssue.hasPath() ? serverIssue.getPath() : null).equals(componentKey)) { + consumer.apply(serverIssue); + } } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultPreviousIssuesLoader.java b/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultServerIssuesLoader.java index f9ea0792c7f..8f32995522a 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultPreviousIssuesLoader.java +++ b/sonar-batch/src/main/java/org/sonar/batch/repository/DefaultServerIssuesLoader.java @@ -19,34 +19,31 @@ */ package org.sonar.batch.repository; -import com.google.common.base.Charsets; import com.google.common.base.Function; import com.google.common.io.InputSupplier; -import org.sonar.api.batch.bootstrap.ProjectReactor; import org.sonar.api.utils.HttpDownloader; import org.sonar.batch.bootstrap.ServerClient; -import org.sonar.batch.protocol.input.issues.PreviousIssue; -import org.sonar.batch.protocol.input.issues.PreviousIssueHelper; +import org.sonar.batch.protocol.input.BatchInput.ServerIssue; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -public class DefaultPreviousIssuesLoader implements PreviousIssuesLoader { +public class DefaultServerIssuesLoader implements ServerIssuesLoader { private final ServerClient serverClient; - public DefaultPreviousIssuesLoader(ServerClient serverClient) { + public DefaultServerIssuesLoader(ServerClient serverClient) { this.serverClient = serverClient; } @Override - public void load(ProjectReactor reactor, Function<PreviousIssue, Void> consumer) { - InputSupplier<InputStream> request = serverClient.doRequest("/batch/issues?key=" + ServerClient.encodeForUrl(reactor.getRoot().getKeyWithBranch()), "GET", null); - try (InputStream is = request.getInput(); Reader reader = new InputStreamReader(is, Charsets.UTF_8)) { - for (PreviousIssue issue : PreviousIssueHelper.getIssues(reader)) { - consumer.apply(issue); + public void load(String componentKey, Function<ServerIssue, Void> consumer, boolean incremental) { + InputSupplier<InputStream> request = serverClient.doRequest("/batch/issues?key=" + ServerClient.encodeForUrl(componentKey), "GET", null); + try (InputStream is = request.getInput()) { + ServerIssue previousIssue = ServerIssue.parseDelimitedFrom(is); + while (previousIssue != null) { + consumer.apply(previousIssue); + previousIssue = ServerIssue.parseDelimitedFrom(is); } } catch (HttpDownloader.HttpException e) { throw serverClient.handleHttpException(e); diff --git a/sonar-batch/src/main/java/org/sonar/batch/repository/PreviousIssuesLoader.java b/sonar-batch/src/main/java/org/sonar/batch/repository/ServerIssuesLoader.java index fe706da3e73..eae1d2fb3ea 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/repository/PreviousIssuesLoader.java +++ b/sonar-batch/src/main/java/org/sonar/batch/repository/ServerIssuesLoader.java @@ -20,11 +20,10 @@ package org.sonar.batch.repository; import com.google.common.base.Function; -import org.sonar.api.batch.bootstrap.ProjectReactor; -import org.sonar.batch.protocol.input.issues.PreviousIssue; +import org.sonar.batch.protocol.input.BatchInput.ServerIssue; -public interface PreviousIssuesLoader { +public interface ServerIssuesLoader { - void load(ProjectReactor reactor, Function<PreviousIssue, Void> consumer); + void load(String componentKey, Function<ServerIssue, Void> consumer, boolean incremental); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java index a2f46a03fe4..0cc044e2036 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java @@ -62,7 +62,7 @@ import org.sonar.batch.issue.DefaultProjectIssues; import org.sonar.batch.issue.IssueCache; import org.sonar.batch.issue.tracking.InitialOpenIssuesStack; import org.sonar.batch.issue.tracking.LocalIssueTracking; -import org.sonar.batch.issue.tracking.PreviousIssueRepository; +import org.sonar.batch.issue.tracking.ServerIssueRepository; import org.sonar.batch.mediumtest.ScanTaskObservers; import org.sonar.batch.phases.GraphPersister; import org.sonar.batch.profiling.PhasesSumUpTimeProfiler; @@ -165,7 +165,7 @@ public class ProjectScanContainer extends ComponentContainer { DefaultProjectIssues.class, IssueChangelogDebtCalculator.class, LocalIssueTracking.class, - PreviousIssueRepository.class, + ServerIssueRepository.class, // tests TestPlanPerspectiveLoader.class, diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/report/ConsoleReport.java b/sonar-batch/src/main/java/org/sonar/batch/scan/report/ConsoleReport.java index 4e50af72e52..88493d10d64 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/report/ConsoleReport.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/report/ConsoleReport.java @@ -21,42 +21,43 @@ package org.sonar.batch.scan.report; import com.google.common.annotations.VisibleForTesting; import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.sonar.api.Properties; import org.sonar.api.Property; import org.sonar.api.PropertyType; import org.sonar.api.config.Settings; import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.api.rule.Severity; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; import org.sonar.batch.issue.IssueCache; +import org.sonar.batch.scan.filesystem.InputPathCache; @Properties({ @Property(key = ConsoleReport.CONSOLE_REPORT_ENABLED_KEY, name = "Enable console report", description = "Set this to true to generate a report in console output", type = PropertyType.BOOLEAN, defaultValue = "false")}) public class ConsoleReport implements Reporter { - private static final Logger LOG = LoggerFactory.getLogger(ConsoleReport.class); + + @VisibleForTesting + public static final String HEADER = "------------- Issues Report -------------"; + + private static final Logger LOG = Loggers.get(ConsoleReport.class); public static final String CONSOLE_REPORT_ENABLED_KEY = "sonar.issuesReport.console.enable"; private static final int LEFT_PAD = 10; private Settings settings; - private Logger logger; - private IssueCache issueCache; - - public ConsoleReport(Settings settings, IssueCache issueCache) { - this(settings, issueCache, LOG); - } + private InputPathCache inputPathCache; @VisibleForTesting - public ConsoleReport(Settings settings, IssueCache issueCache, Logger logger) { + public ConsoleReport(Settings settings, IssueCache issueCache, InputPathCache inputPathCache) { this.settings = settings; this.issueCache = issueCache; - this.logger = logger; + this.inputPathCache = inputPathCache; } private static class Report { + boolean noFile = false; int totalNewIssues = 0; int newBlockerIssues = 0; int newCriticalIssues = 0; @@ -88,24 +89,40 @@ public class ConsoleReport implements Reporter { } } } + + public void setNoFile(boolean value) { + this.noFile = value; + } } @Override public void execute() { if (settings.getBoolean(CONSOLE_REPORT_ENABLED_KEY)) { Report r = new Report(); + r.setNoFile(!inputPathCache.allFiles().iterator().hasNext()); for (DefaultIssue issue : issueCache.all()) { r.process(issue); } - printNewIssues(r); + printReport(r); } } - public void printNewIssues(Report r) { + public void printReport(Report r) { StringBuilder sb = new StringBuilder(); + sb.append("\n\n" + HEADER + "\n\n"); + if (r.noFile) { + sb.append(" No file analyzed\n"); + } else { + printNewIssues(r, sb); + } + sb.append("\n-------------------------------------------\n\n"); + + LOG.info(sb.toString()); + } + + private void printNewIssues(Report r, StringBuilder sb) { int newIssues = r.totalNewIssues; - sb.append("\n\n------------- Issues Report -------------\n\n"); if (newIssues > 0) { sb.append(StringUtils.leftPad("+" + newIssues, LEFT_PAD)).append(" issue" + (newIssues > 1 ? "s" : "")).append("\n\n"); printNewIssues(sb, r.newBlockerIssues, Severity.BLOCKER, "blocking"); @@ -116,9 +133,6 @@ public class ConsoleReport implements Reporter { } else { sb.append(" No new issue").append("\n"); } - sb.append("\n-------------------------------------------\n\n"); - - logger.info(sb.toString()); } private void printNewIssues(StringBuilder sb, int issueCount, String severity, String severityLabel) { diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/report/IssuesReport.java b/sonar-batch/src/main/java/org/sonar/batch/scan/report/IssuesReport.java index a4513f65929..fb23eed4c21 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/report/IssuesReport.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/report/IssuesReport.java @@ -35,6 +35,7 @@ public class IssuesReport { public static final int TOO_MANY_ISSUES_THRESHOLD = 1000; private String title; private Date date; + private boolean noFile; private final ReportSummary summary = new ReportSummary(); private final Map<BatchResource, ResourceReport> resourceReportsByResource = Maps.newLinkedHashMap(); @@ -61,6 +62,14 @@ public class IssuesReport { this.date = date; } + public boolean isNoFile() { + return noFile; + } + + public void setNoFile(boolean noFile) { + this.noFile = noFile; + } + public Map<BatchResource, ResourceReport> getResourceReportsByResource() { return resourceReportsByResource; } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/report/IssuesReportBuilder.java b/sonar-batch/src/main/java/org/sonar/batch/scan/report/IssuesReportBuilder.java index 12dd08e79c0..36af32c9c92 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/report/IssuesReportBuilder.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/report/IssuesReportBuilder.java @@ -33,6 +33,7 @@ import org.sonar.batch.ProjectTree; import org.sonar.batch.index.BatchResource; import org.sonar.batch.index.ResourceCache; import org.sonar.batch.issue.IssueCache; +import org.sonar.batch.scan.filesystem.InputPathCache; import javax.annotation.CheckForNull; @@ -44,17 +45,20 @@ public class IssuesReportBuilder implements BatchExtension { private final RuleFinder ruleFinder; private final ResourceCache resourceCache; private final ProjectTree projectTree; + private final InputPathCache inputPathCache; - public IssuesReportBuilder(IssueCache issueCache, RuleFinder ruleFinder, ResourceCache resourceCache, ProjectTree projectTree) { + public IssuesReportBuilder(IssueCache issueCache, RuleFinder ruleFinder, ResourceCache resourceCache, ProjectTree projectTree, InputPathCache inputPathCache) { this.issueCache = issueCache; this.ruleFinder = ruleFinder; this.resourceCache = resourceCache; this.projectTree = projectTree; + this.inputPathCache = inputPathCache; } public IssuesReport buildReport() { Project project = projectTree.getRootProject(); IssuesReport issuesReport = new IssuesReport(); + issuesReport.setNoFile(!inputPathCache.allFiles().iterator().hasNext()); issuesReport.setTitle(project.getName()); issuesReport.setDate(project.getAnalysisDate()); diff --git a/sonar-batch/src/main/resources/org/sonar/batch/scan/report/issuesreport.ftl b/sonar-batch/src/main/resources/org/sonar/batch/scan/report/issuesreport.ftl index 5f757cccd30..86ba1631eaa 100644 --- a/sonar-batch/src/main/resources/org/sonar/batch/scan/report/issuesreport.ftl +++ b/sonar-batch/src/main/resources/org/sonar/batch/scan/report/issuesreport.ftl @@ -168,6 +168,11 @@ <div class="subtitle">${report.getTitle()} - ${report.getDate()?datetime}</div> </div> +<#if report.isNoFile()> +<div id="content"> + <div class="banner">No file analyzed</div> +</div> +<#else> <div id="content"> <#if !complete> @@ -452,5 +457,6 @@ refreshFilters(true); }); </script> +</#if> </body> </html> diff --git a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/DefaultAnalysisModeTest.java b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/DefaultAnalysisModeTest.java index f4d129bb4ff..65c7d548b9a 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/bootstrap/DefaultAnalysisModeTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/bootstrap/DefaultAnalysisModeTest.java @@ -20,10 +20,12 @@ package org.sonar.batch.bootstrap; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; import org.junit.Test; import org.sonar.api.CoreProperties; import java.util.Collections; +import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; @@ -31,13 +33,12 @@ public class DefaultAnalysisModeTest { @Test public void regular_analysis_by_default() { - DefaultAnalysisMode mode = new DefaultAnalysisMode(new BootstrapProperties(Collections.<String, String>emptyMap())); + DefaultAnalysisMode mode = new DefaultAnalysisMode(Collections.<String, String>emptyMap()); assertThat(mode.isPreview()).isFalse(); assertThat(mode.isIncremental()).isFalse(); - BootstrapProperties bootstrapProps = new BootstrapProperties(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, "pouet")); - mode = new DefaultAnalysisMode(bootstrapProps); + mode = new DefaultAnalysisMode(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, "pouet")); assertThat(mode.isPreview()).isFalse(); assertThat(mode.isIncremental()).isFalse(); @@ -45,8 +46,7 @@ public class DefaultAnalysisModeTest { @Test public void support_analysis_mode() { - BootstrapProperties bootstrapProps = new BootstrapProperties(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ANALYSIS)); - DefaultAnalysisMode mode = new DefaultAnalysisMode(bootstrapProps); + DefaultAnalysisMode mode = new DefaultAnalysisMode(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_ANALYSIS)); assertThat(mode.isPreview()).isFalse(); assertThat(mode.isIncremental()).isFalse(); @@ -54,30 +54,29 @@ public class DefaultAnalysisModeTest { @Test public void support_preview_mode() { - BootstrapProperties bootstrapProps = new BootstrapProperties(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_PREVIEW)); - DefaultAnalysisMode mode = new DefaultAnalysisMode(bootstrapProps); + Map<String, String> props = Maps.newHashMap(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_PREVIEW)); + DefaultAnalysisMode mode = new DefaultAnalysisMode(props); assertThat(mode.isPreview()).isTrue(); assertThat(mode.isIncremental()).isFalse(); - assertThat(bootstrapProps.property(CoreProperties.DRY_RUN)).isEqualTo("true"); + assertThat(props.get(CoreProperties.DRY_RUN)).isEqualTo("true"); } @Test public void support_incremental_mode() { - BootstrapProperties bootstrapProps = new BootstrapProperties(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_INCREMENTAL)); - DefaultAnalysisMode mode = new DefaultAnalysisMode(bootstrapProps); + Map<String, String> props = Maps.newHashMap(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_INCREMENTAL)); + DefaultAnalysisMode mode = new DefaultAnalysisMode(props); assertThat(mode.isPreview()).isTrue(); assertThat(mode.isIncremental()).isTrue(); - assertThat(bootstrapProps.property(CoreProperties.DRY_RUN)).isEqualTo("true"); + assertThat(props.get(CoreProperties.DRY_RUN)).isEqualTo("true"); } @Test public void support_deprecated_dryrun_property() { - BootstrapProperties bootstrapProps = new BootstrapProperties(ImmutableMap.of(CoreProperties.DRY_RUN, "true")); - DefaultAnalysisMode mode = new DefaultAnalysisMode(bootstrapProps); + DefaultAnalysisMode mode = new DefaultAnalysisMode(Maps.newHashMap(ImmutableMap.of(CoreProperties.DRY_RUN, "true"))); assertThat(mode.isPreview()).isTrue(); assertThat(mode.isIncremental()).isFalse(); diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/tracking/InitialOpenIssuesStackTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/tracking/InitialOpenIssuesStackTest.java index 34adce38fa1..a98952d5ec9 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/issue/tracking/InitialOpenIssuesStackTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/issue/tracking/InitialOpenIssuesStackTest.java @@ -73,7 +73,7 @@ public class InitialOpenIssuesStackTest { IssueDto issueDto = new IssueDto().setComponentKey("org.struts.Action").setKee("ISSUE-1"); stack.addIssue(issueDto); - List<PreviousIssue> issueDtos = stack.selectAndRemoveIssues("org.struts.Action"); + List<ServerIssue> issueDtos = stack.selectAndRemoveIssues("org.struts.Action"); assertThat(issueDtos).hasSize(1); assertThat(issueDtos.get(0).key()).isEqualTo("ISSUE-1"); @@ -85,7 +85,7 @@ public class InitialOpenIssuesStackTest { stack.addIssue(new IssueDto().setComponentKey("org.struts.Action").setKee("ISSUE-1")); stack.addIssue(new IssueDto().setComponentKey("org.struts.Action").setKee("ISSUE-2")); - List<PreviousIssue> issueDtos = stack.selectAndRemoveIssues("org.struts.Action"); + List<ServerIssue> issueDtos = stack.selectAndRemoveIssues("org.struts.Action"); assertThat(issueDtos).hasSize(2); assertThat(stack.selectAllIssues()).isEmpty(); @@ -95,7 +95,7 @@ public class InitialOpenIssuesStackTest { public void get_and_remove_do_nothing_if_resource_not_found() { stack.addIssue(new IssueDto().setComponentKey("org.struts.Action").setKee("ISSUE-1")); - List<PreviousIssue> issueDtos = stack.selectAndRemoveIssues("Other"); + List<ServerIssue> issueDtos = stack.selectAndRemoveIssues("Other"); assertThat(issueDtos).hasSize(0); assertThat(stack.selectAllIssues()).hasSize(1); diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/tracking/IssueTrackingDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/tracking/IssueTrackingDecoratorTest.java index 439457caa78..cd3746ef401 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/issue/tracking/IssueTrackingDecoratorTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/issue/tracking/IssueTrackingDecoratorTest.java @@ -130,7 +130,7 @@ public class IssueTrackingDecoratorTest { // INPUT : one issue, no open issues during previous scan, no filtering when(issueCache.byComponent("struts:Action.java")).thenReturn(Arrays.asList(issue)); - List<PreviousIssue> dbIssues = Collections.emptyList(); + List<ServerIssue> dbIssues = Collections.emptyList(); when(initialOpenIssues.selectAndRemoveIssues("struts:Action.java")).thenReturn(dbIssues); when(inputPathCache.getFile("foo", "Action.java")).thenReturn(mock(DefaultInputFile.class)); when(inputPathCache.getFileMetadata("foo", "Action.java")).thenReturn(new InputFileMetadata()); @@ -155,7 +155,7 @@ public class IssueTrackingDecoratorTest { Resource file = File.create("Action.java").setEffectiveKey("struts:Action.java").setId(123); // INPUT : one issue existing during previous scan - PreviousIssue unmatchedIssue = new PreviousIssueFromDb(new IssueDto().setKee("ABCDE").setResolution(null).setStatus("OPEN").setRuleKey("squid", "AvoidCycle")); + ServerIssue unmatchedIssue = new ServerIssueFromDb(new IssueDto().setKee("ABCDE").setResolution(null).setStatus("OPEN").setRuleKey("squid", "AvoidCycle")); IssueTrackingResult trackingResult = new IssueTrackingResult(); trackingResult.addUnmatched(unmatchedIssue); @@ -181,7 +181,7 @@ public class IssueTrackingDecoratorTest { @Test public void manual_issues_should_be_moved_if_matching_line_found() throws Exception { // INPUT : one issue existing during previous scan - PreviousIssue unmatchedIssue = new PreviousIssueFromDb(new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(6).setStatus("OPEN").setRuleKey("manual", "Performance")); + ServerIssue unmatchedIssue = new ServerIssueFromDb(new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(6).setStatus("OPEN").setRuleKey("manual", "Performance")); when(ruleFinder.findByKey(RuleKey.of("manual", "Performance"))).thenReturn(new Rule("manual", "Performance")); IssueTrackingResult trackingResult = new IssueTrackingResult(); @@ -240,7 +240,7 @@ public class IssueTrackingDecoratorTest { public void manual_issues_should_be_untouched_if_already_closed() throws Exception { // INPUT : one issue existing during previous scan - PreviousIssue unmatchedIssue = new PreviousIssueFromDb(new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(1).setStatus("CLOSED").setRuleKey("manual", "Performance")); + ServerIssue unmatchedIssue = new ServerIssueFromDb(new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(1).setStatus("CLOSED").setRuleKey("manual", "Performance")); when(ruleFinder.findByKey(RuleKey.of("manual", "Performance"))).thenReturn(new Rule("manual", "Performance")); IssueTrackingResult trackingResult = new IssueTrackingResult(); @@ -272,7 +272,7 @@ public class IssueTrackingDecoratorTest { public void manual_issues_should_be_untouched_if_line_is_null() throws Exception { // INPUT : one issue existing during previous scan - PreviousIssue unmatchedIssue = new PreviousIssueFromDb(new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(null).setStatus("OPEN").setRuleKey("manual", "Performance")); + ServerIssue unmatchedIssue = new ServerIssueFromDb(new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(null).setStatus("OPEN").setRuleKey("manual", "Performance")); when(ruleFinder.findByKey(RuleKey.of("manual", "Performance"))).thenReturn(new Rule("manual", "Performance")); IssueTrackingResult trackingResult = new IssueTrackingResult(); @@ -306,7 +306,7 @@ public class IssueTrackingDecoratorTest { // INPUT : one issue existing during previous scan final int issueOnLine = 6; - PreviousIssue unmatchedIssue = new PreviousIssueFromDb(new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(issueOnLine).setStatus("OPEN") + ServerIssue unmatchedIssue = new ServerIssueFromDb(new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(issueOnLine).setStatus("OPEN") .setRuleKey("manual", "Performance")); when(ruleFinder.findByKey(RuleKey.of("manual", "Performance"))).thenReturn(new Rule("manual", "Performance")); @@ -354,7 +354,7 @@ public class IssueTrackingDecoratorTest { // INPUT : one issue existing during previous scan final int issueOnLine = 3; - PreviousIssue unmatchedIssue = new PreviousIssueFromDb(new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(issueOnLine).setStatus("OPEN") + ServerIssue unmatchedIssue = new ServerIssueFromDb(new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(issueOnLine).setStatus("OPEN") .setRuleKey("manual", "Performance")); when(ruleFinder.findByKey(RuleKey.of("manual", "Performance"))).thenReturn(new Rule("manual", "Performance")); @@ -400,7 +400,7 @@ public class IssueTrackingDecoratorTest { // "Unmatched" issues existed in previous scan but not in current one -> they have to be closed // INPUT : one issue existing during previous scan - PreviousIssue unmatchedIssue = new PreviousIssueFromDb(new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(1).setStatus("OPEN").setRuleKey("manual", "Performance")); + ServerIssue unmatchedIssue = new ServerIssueFromDb(new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(1).setStatus("OPEN").setRuleKey("manual", "Performance")); when(ruleFinder.findByKey(RuleKey.of("manual", "Performance"))).thenReturn(new Rule("manual", "Performance").setStatus(Rule.STATUS_REMOVED)); IssueTrackingResult trackingResult = new IssueTrackingResult(); @@ -431,7 +431,7 @@ public class IssueTrackingDecoratorTest { // "Unmatched" issues existed in previous scan but not in current one -> they have to be closed // INPUT : one issue existing during previous scan - PreviousIssue unmatchedIssue = new PreviousIssueFromDb(new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(1).setStatus("OPEN").setRuleKey("manual", "Performance")); + ServerIssue unmatchedIssue = new ServerIssueFromDb(new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(1).setStatus("OPEN").setRuleKey("manual", "Performance")); when(ruleFinder.findByKey(RuleKey.of("manual", "Performance"))).thenReturn(null); IssueTrackingResult trackingResult = new IssueTrackingResult(); @@ -462,7 +462,7 @@ public class IssueTrackingDecoratorTest { // "Unmatched" issues existed in previous scan but not in current one -> they have to be closed // INPUT : one issue existing during previous scan - PreviousIssue unmatchedIssue = new PreviousIssueFromDb(new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(6).setStatus("OPEN").setRuleKey("manual", "Performance")); + ServerIssue unmatchedIssue = new ServerIssueFromDb(new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(6).setStatus("OPEN").setRuleKey("manual", "Performance")); when(ruleFinder.findByKey(RuleKey.of("manual", "Performance"))).thenReturn(null); IssueTrackingResult trackingResult = new IssueTrackingResult(); @@ -527,7 +527,7 @@ public class IssueTrackingDecoratorTest { @Test public void merge_matched_issue() throws Exception { - PreviousIssue previousIssue = new PreviousIssueFromDb(new IssueDto().setKee("ABCDE").setResolution(null).setStatus("OPEN").setRuleKey("squid", "AvoidCycle") + ServerIssue previousIssue = new ServerIssueFromDb(new IssueDto().setKee("ABCDE").setResolution(null).setStatus("OPEN").setRuleKey("squid", "AvoidCycle") .setLine(10).setSeverity("MAJOR").setMessage("Message").setEffortToFix(1.5).setDebt(1L).setProjectKey("sample")); DefaultIssue issue = new DefaultIssue(); @@ -546,7 +546,7 @@ public class IssueTrackingDecoratorTest { @Test public void merge_matched_issue_on_manual_severity() throws Exception { - PreviousIssue previousIssue = new PreviousIssueFromDb(new IssueDto().setKee("ABCDE").setResolution(null).setStatus("OPEN").setRuleKey("squid", "AvoidCycle") + ServerIssue previousIssue = new ServerIssueFromDb(new IssueDto().setKee("ABCDE").setResolution(null).setStatus("OPEN").setRuleKey("squid", "AvoidCycle") .setLine(10).setManualSeverity(true).setSeverity("MAJOR").setMessage("Message").setEffortToFix(1.5).setDebt(1L)); DefaultIssue issue = new DefaultIssue(); @@ -564,7 +564,7 @@ public class IssueTrackingDecoratorTest { public void merge_issue_changelog_with_previous_changelog() throws Exception { when(initialOpenIssues.selectChangelog("ABCDE")).thenReturn(newArrayList(new IssueChangeDto().setIssueKey("ABCD").setCreatedAt(System2.INSTANCE.now()))); - PreviousIssue previousIssue = new PreviousIssueFromDb(new IssueDto().setKee("ABCDE").setResolution(null).setStatus("OPEN").setRuleKey("squid", "AvoidCycle") + ServerIssue previousIssue = new ServerIssueFromDb(new IssueDto().setKee("ABCDE").setResolution(null).setStatus("OPEN").setRuleKey("squid", "AvoidCycle") .setLine(10).setMessage("Message").setEffortToFix(1.5).setDebt(1L).setCreatedAt(System2.INSTANCE.now())); DefaultIssue issue = new DefaultIssue(); diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/tracking/IssueTrackingTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/tracking/IssueTrackingTest.java index 095429f3cbf..a58f1bb7068 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/issue/tracking/IssueTrackingTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/issue/tracking/IssueTrackingTest.java @@ -71,24 +71,24 @@ public class IssueTrackingTest { @Test public void key_should_be_the_prioritary_field_to_check() { - PreviousIssueFromDb referenceIssue1 = newReferenceIssue("message", 10, "squid", "AvoidCycle", "checksum1"); + ServerIssueFromDb referenceIssue1 = newReferenceIssue("message", 10, "squid", "AvoidCycle", "checksum1"); referenceIssue1.getDto().setKee("100"); - PreviousIssueFromDb referenceIssue2 = newReferenceIssue("message", 10, "squid", "AvoidCycle", "checksum2"); + ServerIssueFromDb referenceIssue2 = newReferenceIssue("message", 10, "squid", "AvoidCycle", "checksum2"); referenceIssue2.getDto().setKee("200"); // exactly the fields of referenceIssue1 but not the same key DefaultIssue newIssue = newDefaultIssue("message", 10, RuleKey.of("squid", "AvoidCycle"), "checksum1").setKey("200"); IssueTrackingResult result = new IssueTrackingResult(); - tracking.mapIssues(newArrayList(newIssue), Lists.<PreviousIssue>newArrayList(referenceIssue1, referenceIssue2), null, result); + tracking.mapIssues(newArrayList(newIssue), Lists.<ServerIssue>newArrayList(referenceIssue1, referenceIssue2), null, result); // same key assertThat(result.matching(newIssue)).isSameAs(referenceIssue2); } @Test public void checksum_should_have_greater_priority_than_line() { - PreviousIssue referenceIssue1 = newReferenceIssue("message", 1, "squid", "AvoidCycle", "checksum1"); - PreviousIssue referenceIssue2 = newReferenceIssue("message", 3, "squid", "AvoidCycle", "checksum2"); + ServerIssue referenceIssue1 = newReferenceIssue("message", 1, "squid", "AvoidCycle", "checksum1"); + ServerIssue referenceIssue2 = newReferenceIssue("message", 3, "squid", "AvoidCycle", "checksum2"); DefaultIssue newIssue1 = newDefaultIssue("message", 3, RuleKey.of("squid", "AvoidCycle"), "checksum1"); DefaultIssue newIssue2 = newDefaultIssue("message", 5, RuleKey.of("squid", "AvoidCycle"), "checksum2"); @@ -105,7 +105,7 @@ public class IssueTrackingTest { @Test public void same_rule_and_null_line_and_checksum_but_different_messages() { DefaultIssue newIssue = newDefaultIssue("new message", null, RuleKey.of("squid", "AvoidCycle"), "checksum1"); - PreviousIssue referenceIssue = newReferenceIssue("old message", null, "squid", "AvoidCycle", "checksum1"); + ServerIssue referenceIssue = newReferenceIssue("old message", null, "squid", "AvoidCycle", "checksum1"); IssueTrackingResult result = new IssueTrackingResult(); tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, result); @@ -115,7 +115,7 @@ public class IssueTrackingTest { @Test public void same_rule_and_line_and_checksum_but_different_messages() { DefaultIssue newIssue = newDefaultIssue("new message", 1, RuleKey.of("squid", "AvoidCycle"), "checksum1"); - PreviousIssue referenceIssue = newReferenceIssue("old message", 1, "squid", "AvoidCycle", "checksum1"); + ServerIssue referenceIssue = newReferenceIssue("old message", 1, "squid", "AvoidCycle", "checksum1"); IssueTrackingResult result = new IssueTrackingResult(); tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, result); @@ -125,7 +125,7 @@ public class IssueTrackingTest { @Test public void same_rule_and_line_message() { DefaultIssue newIssue = newDefaultIssue("message", 1, RuleKey.of("squid", "AvoidCycle"), "checksum1"); - PreviousIssue referenceIssue = newReferenceIssue("message", 1, "squid", "AvoidCycle", "checksum2"); + ServerIssue referenceIssue = newReferenceIssue("message", 1, "squid", "AvoidCycle", "checksum2"); IssueTrackingResult result = new IssueTrackingResult(); tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, result); @@ -135,7 +135,7 @@ public class IssueTrackingTest { @Test public void should_ignore_reference_measure_without_checksum() { DefaultIssue newIssue = newDefaultIssue("message", 1, RuleKey.of("squid", "AvoidCycle"), null); - PreviousIssue referenceIssue = newReferenceIssue("message", 1, "squid", "NullDeref", null); + ServerIssue referenceIssue = newReferenceIssue("message", 1, "squid", "NullDeref", null); IssueTrackingResult result = new IssueTrackingResult(); tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, result); @@ -145,7 +145,7 @@ public class IssueTrackingTest { @Test public void same_rule_and_message_and_checksum_but_different_line() { DefaultIssue newIssue = newDefaultIssue("message", 1, RuleKey.of("squid", "AvoidCycle"), "checksum1"); - PreviousIssue referenceIssue = newReferenceIssue("message", 2, "squid", "AvoidCycle", "checksum1"); + ServerIssue referenceIssue = newReferenceIssue("message", 2, "squid", "AvoidCycle", "checksum1"); IssueTrackingResult result = new IssueTrackingResult(); tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, result); @@ -158,7 +158,7 @@ public class IssueTrackingTest { @Test public void same_checksum_and_rule_but_different_line_and_different_message() { DefaultIssue newIssue = newDefaultIssue("new message", 1, RuleKey.of("squid", "AvoidCycle"), "checksum1"); - PreviousIssue referenceIssue = newReferenceIssue("old message", 2, "squid", "AvoidCycle", "checksum1"); + ServerIssue referenceIssue = newReferenceIssue("old message", 2, "squid", "AvoidCycle", "checksum1"); IssueTrackingResult result = new IssueTrackingResult(); tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, result); @@ -168,7 +168,7 @@ public class IssueTrackingTest { @Test public void should_create_new_issue_when_same_rule_same_message_but_different_line_and_checksum() { DefaultIssue newIssue = newDefaultIssue("message", 1, RuleKey.of("squid", "AvoidCycle"), "checksum1"); - PreviousIssue referenceIssue = newReferenceIssue("message", 2, "squid", "AvoidCycle", "checksum2"); + ServerIssue referenceIssue = newReferenceIssue("message", 2, "squid", "AvoidCycle", "checksum2"); IssueTrackingResult result = new IssueTrackingResult(); tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, result); @@ -178,7 +178,7 @@ public class IssueTrackingTest { @Test public void should_not_track_issue_if_different_rule() { DefaultIssue newIssue = newDefaultIssue("message", 1, RuleKey.of("squid", "AvoidCycle"), "checksum1"); - PreviousIssue referenceIssue = newReferenceIssue("message", 1, "squid", "NullDeref", "checksum1"); + ServerIssue referenceIssue = newReferenceIssue("message", 1, "squid", "NullDeref", "checksum1"); IssueTrackingResult result = new IssueTrackingResult(); tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, result); @@ -190,7 +190,7 @@ public class IssueTrackingTest { // issue messages are trimmed and can be abbreviated when persisted in database. // Comparing issue messages must use the same format. DefaultIssue newIssue = newDefaultIssue(" message ", 1, RuleKey.of("squid", "AvoidCycle"), "checksum1"); - PreviousIssue referenceIssue = newReferenceIssue("message", 1, "squid", "AvoidCycle", "checksum2"); + ServerIssue referenceIssue = newReferenceIssue("message", 1, "squid", "AvoidCycle", "checksum2"); IssueTrackingResult result = new IssueTrackingResult(); tracking.mapIssues(newArrayList(newIssue), newArrayList(referenceIssue), null, result); @@ -202,7 +202,7 @@ public class IssueTrackingTest { initLastHashes("example2-v1", "example2-v2"); DefaultIssue newIssue = newDefaultIssue("Indentation", 9, RuleKey.of("squid", "AvoidCycle"), "foo"); - PreviousIssue referenceIssue = newReferenceIssue("2 branches need to be covered", null, "squid", "AvoidCycle", null); + ServerIssue referenceIssue = newReferenceIssue("2 branches need to be covered", null, "squid", "AvoidCycle", null); IssueTrackingResult result = tracking.track(sourceHashHolder, newArrayList(referenceIssue), newArrayList(newIssue)); @@ -214,7 +214,7 @@ public class IssueTrackingTest { initLastHashes("example2-v1", "example2-v2"); DefaultIssue newIssue = newDefaultIssue("1 branch need to be covered", null, RuleKey.of("squid", "AvoidCycle"), "foo"); - PreviousIssue referenceIssue = newReferenceIssue("Indentationd", 7, "squid", "AvoidCycle", null); + ServerIssue referenceIssue = newReferenceIssue("Indentationd", 7, "squid", "AvoidCycle", null); IssueTrackingResult result = tracking.track(sourceHashHolder, newArrayList(referenceIssue), newArrayList(newIssue)); @@ -229,7 +229,7 @@ public class IssueTrackingTest { initLastHashes("example2-v1", "example2-v2"); DefaultIssue newIssue = newDefaultIssue("1 branch need to be covered", null, RuleKey.of("squid", "AvoidCycle"), null); - PreviousIssue referenceIssue = newReferenceIssue("2 branches need to be covered", null, "squid", "AvoidCycle", null); + ServerIssue referenceIssue = newReferenceIssue("2 branches need to be covered", null, "squid", "AvoidCycle", null); IssueTrackingResult result = tracking.track(sourceHashHolder, newArrayList(referenceIssue), newArrayList(newIssue)); @@ -243,8 +243,8 @@ public class IssueTrackingTest { public void should_track_issues_based_on_blocks_recognition_on_example1() throws Exception { initLastHashes("example1-v1", "example1-v2"); - PreviousIssue referenceIssue1 = newReferenceIssue("Indentation", 7, "squid", "AvoidCycle", null); - PreviousIssue referenceIssue2 = newReferenceIssue("Indentation", 11, "squid", "AvoidCycle", null); + ServerIssue referenceIssue1 = newReferenceIssue("Indentation", 7, "squid", "AvoidCycle", null); + ServerIssue referenceIssue2 = newReferenceIssue("Indentation", 11, "squid", "AvoidCycle", null); DefaultIssue newIssue1 = newDefaultIssue("Indentation", 9, RuleKey.of("squid", "AvoidCycle"), null); DefaultIssue newIssue2 = newDefaultIssue("Indentation", 13, RuleKey.of("squid", "AvoidCycle"), null); @@ -266,7 +266,7 @@ public class IssueTrackingTest { public void should_track_issues_based_on_blocks_recognition_on_example2() throws Exception { initLastHashes("example2-v1", "example2-v2"); - PreviousIssue referenceIssue1 = newReferenceIssue("SystemPrintln", 5, "squid", "AvoidCycle", null); + ServerIssue referenceIssue1 = newReferenceIssue("SystemPrintln", 5, "squid", "AvoidCycle", null); DefaultIssue newIssue1 = newDefaultIssue("SystemPrintln", 6, RuleKey.of("squid", "AvoidCycle"), null); DefaultIssue newIssue2 = newDefaultIssue("SystemPrintln", 10, RuleKey.of("squid", "AvoidCycle"), null); @@ -287,9 +287,9 @@ public class IssueTrackingTest { public void should_track_issues_based_on_blocks_recognition_on_example3() throws Exception { initLastHashes("example3-v1", "example3-v2"); - PreviousIssue referenceIssue1 = newReferenceIssue("Avoid unused local variables such as 'j'.", 6, "squid", "AvoidCycle", "63c11570fc0a76434156be5f8138fa03"); - PreviousIssue referenceIssue2 = newReferenceIssue("Avoid unused private methods such as 'myMethod()'.", 13, "squid", "NullDeref", "ef23288705d1ef1e512448ace287586e"); - PreviousIssue referenceIssue3 = newReferenceIssue("Method 'avoidUtilityClass' is not designed for extension - needs to be abstract, final or empty.", 9, "pmd", + ServerIssue referenceIssue1 = newReferenceIssue("Avoid unused local variables such as 'j'.", 6, "squid", "AvoidCycle", "63c11570fc0a76434156be5f8138fa03"); + ServerIssue referenceIssue2 = newReferenceIssue("Avoid unused private methods such as 'myMethod()'.", 13, "squid", "NullDeref", "ef23288705d1ef1e512448ace287586e"); + ServerIssue referenceIssue3 = newReferenceIssue("Method 'avoidUtilityClass' is not designed for extension - needs to be abstract, final or empty.", 9, "pmd", "UnusedLocalVariable", "ed5cdd046fda82727d6fedd1d8e3a310"); // New issue @@ -322,7 +322,7 @@ public class IssueTrackingTest { public void dont_load_checksum_if_no_new_issue() throws Exception { sourceHashHolder = mock(SourceHashHolder.class); - PreviousIssue referenceIssue = newReferenceIssue("2 branches need to be covered", null, "squid", "AvoidCycle", null); + ServerIssue referenceIssue = newReferenceIssue("2 branches need to be covered", null, "squid", "AvoidCycle", null); tracking.track(sourceHashHolder, newArrayList(referenceIssue), Collections.<DefaultIssue>emptyList()); @@ -337,7 +337,7 @@ public class IssueTrackingTest { return new DefaultIssue().setMessage(message).setLine(line).setRuleKey(ruleKey).setChecksum(checksum).setStatus(Issue.STATUS_OPEN); } - private PreviousIssueFromDb newReferenceIssue(String message, Integer lineId, String ruleRepo, String ruleKey, String lineChecksum) { + private ServerIssueFromDb newReferenceIssue(String message, Integer lineId, String ruleRepo, String ruleKey, String lineChecksum) { IssueDto referenceIssue = new IssueDto(); Long id = violationId++; referenceIssue.setId(id); @@ -348,7 +348,7 @@ public class IssueTrackingTest { referenceIssue.setChecksum(lineChecksum); referenceIssue.setResolution(null); referenceIssue.setStatus(Issue.STATUS_OPEN); - return new PreviousIssueFromDb(referenceIssue); + return new ServerIssueFromDb(referenceIssue); } private void initLastHashes(String reference, String newSource) throws IOException { diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/preview/IncrementalModeMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/preview/IncrementalModeMediumTest.java new file mode 100644 index 00000000000..f1b91f30b68 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/preview/IncrementalModeMediumTest.java @@ -0,0 +1,156 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.batch.mediumtest.preview; + +import com.google.common.collect.ImmutableMap; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.io.FileUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.CoreProperties; +import org.sonar.api.issue.Issue; +import org.sonar.batch.mediumtest.BatchMediumTester; +import org.sonar.batch.mediumtest.TaskResult; +import org.sonar.batch.protocol.Constants.Severity; +import org.sonar.batch.protocol.input.ActiveRule; +import org.sonar.batch.protocol.input.FileData; +import org.sonar.xoo.XooPlugin; + +import java.io.File; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +import static org.assertj.core.api.Assertions.assertThat; + +public class IncrementalModeMediumTest { + + private static final String SAMPLE_CONTENT = "Sample content\nwith\n4\nlines"; + + @org.junit.Rule + public TemporaryFolder temp = new TemporaryFolder(); + + private static SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy"); + + private static Long date(String date) { + try { + return sdf.parse(date).getTime(); + } catch (ParseException e) { + throw new IllegalStateException(e); + } + } + + public BatchMediumTester tester = BatchMediumTester.builder() + .bootstrapProperties(ImmutableMap.of(CoreProperties.ANALYSIS_MODE, CoreProperties.ANALYSIS_MODE_INCREMENTAL)) + .registerPlugin("xoo", new XooPlugin()) + .addDefaultQProfile("xoo", "Sonar Way") + .activateRule(new ActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", null, "xoo")) + .activateRule(new ActiveRule("manual", "MyManualIssue", null, "My manual issue", "MAJOR", null, null)) + .setPreviousAnalysisDate(new Date()) + .addFileData("sample", "xources/hello/HelloJava.xoo", new FileData(DigestUtils.md5Hex(SAMPLE_CONTENT), false, null, null, null)) + // Existing issue + .mockServerIssue(org.sonar.batch.protocol.input.BatchInput.ServerIssue.newBuilder().setKey("xyz") + .setModuleKey("sample") + .setPath("src/sample.xoo") + .setRuleRepository("xoo") + .setRuleKey("OneIssuePerLine") + .setLine(1) + .setSeverity(Severity.MAJOR) + .setCreationDate(date("14/03/2004")) + .setChecksum(DigestUtils.md5Hex("Samplecontent")) + .setStatus("OPEN") + .build()) + // Resolved issue + .mockServerIssue(org.sonar.batch.protocol.input.BatchInput.ServerIssue.newBuilder().setKey("resolved") + .setModuleKey("sample") + .setPath("src/sample.xoo") + .setRuleRepository("xoo") + .setRuleKey("OneIssuePerLine") + .setLine(1) + .setSeverity(Severity.MAJOR) + .setCreationDate(date("14/03/2004")) + .setChecksum(DigestUtils.md5Hex("dontexist")) + .setStatus("OPEN") + .build()) + // Manual issue + .mockServerIssue(org.sonar.batch.protocol.input.BatchInput.ServerIssue.newBuilder().setKey("manual") + .setModuleKey("sample") + .setPath("src/sample.xoo") + .setRuleRepository("manual") + .setRuleKey("MyManualIssue") + .setLine(1) + .setSeverity(Severity.MAJOR) + .setCreationDate(date("14/03/2004")) + .setChecksum(DigestUtils.md5Hex("Samplecontent")) + .setStatus("OPEN") + .build()) + .build(); + + @Before + public void prepare() { + tester.start(); + } + + @After + public void stop() { + tester.stop(); + } + + @Test + public void testIssueTrackingIncrementalMode() throws Exception { + File baseDir = temp.newFolder(); + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + File xooFile = new File(srcDir, "sample.xoo"); + FileUtils.write(xooFile, SAMPLE_CONTENT); + + TaskResult result = tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "sample") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", "src") + .build()) + .start(); + + int newIssues = 0; + int openIssues = 0; + int resolvedIssue = 0; + for (Issue issue : result.issues()) { + if (issue.isNew()) { + newIssues++; + } else if (issue.resolution() != null) { + resolvedIssue++; + } else { + openIssues++; + } + } + assertThat(newIssues).isEqualTo(3); + assertThat(openIssues).isEqualTo(2); + assertThat(resolvedIssue).isEqualTo(1); + } + +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/preview/PreviewAndReportsMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/preview/PreviewAndReportsMediumTest.java index 6cb9e22fb00..c8368f37246 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/preview/PreviewAndReportsMediumTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/preview/PreviewAndReportsMediumTest.java @@ -21,16 +21,20 @@ package org.sonar.batch.mediumtest.preview; import com.google.common.collect.ImmutableMap; import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.io.FileUtils; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.sonar.api.CoreProperties; import org.sonar.api.issue.Issue; +import org.sonar.api.utils.log.LogTester; import org.sonar.batch.mediumtest.BatchMediumTester; import org.sonar.batch.mediumtest.TaskResult; +import org.sonar.batch.protocol.Constants.Severity; import org.sonar.batch.protocol.input.ActiveRule; -import org.sonar.batch.protocol.input.issues.PreviousIssue; +import org.sonar.batch.scan.report.ConsoleReport; import org.sonar.xoo.XooPlugin; import java.io.File; @@ -42,14 +46,17 @@ import static org.assertj.core.api.Assertions.assertThat; public class PreviewAndReportsMediumTest { - @org.junit.Rule + @Rule public TemporaryFolder temp = new TemporaryFolder(); + @Rule + public LogTester logTester = new LogTester(); + private static SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy"); - private static Date date(String date) { + private static Long date(String date) { try { - return sdf.parse(date); + return sdf.parse(date).getTime(); } catch (ParseException e) { throw new IllegalStateException(e); } @@ -62,33 +69,51 @@ public class PreviewAndReportsMediumTest { .activateRule(new ActiveRule("xoo", "OneIssuePerLine", null, "One issue per line", "MAJOR", null, "xoo")) .activateRule(new ActiveRule("manual", "MyManualIssue", null, "My manual issue", "MAJOR", null, null)) .setPreviousAnalysisDate(new Date()) - // Existing issue - .addPreviousIssue(new PreviousIssue().setKey("xyz") - .setComponentKey("sample:xources/hello/HelloJava.xoo") - .setRuleKey("xoo", "OneIssuePerLine") + // Existing issue that is still detected + .mockServerIssue(org.sonar.batch.protocol.input.BatchInput.ServerIssue.newBuilder().setKey("xyz") + .setModuleKey("sample") + .setPath("xources/hello/HelloJava.xoo") + .setRuleRepository("xoo") + .setRuleKey("OneIssuePerLine") .setLine(1) - .setSeverity("MAJOR") + .setSeverity(Severity.MAJOR) .setCreationDate(date("14/03/2004")) .setChecksum(DigestUtils.md5Hex("packagehello;")) - .setStatus("OPEN")) - // Resolved issue - .addPreviousIssue(new PreviousIssue().setKey("resolved") - .setComponentKey("sample:xources/hello/HelloJava.xoo") - .setRuleKey("xoo", "OneIssuePerLine") + .setStatus("OPEN") + .build()) + // Existing issue that is no more detected (will be closed) + .mockServerIssue(org.sonar.batch.protocol.input.BatchInput.ServerIssue.newBuilder().setKey("resolved") + .setModuleKey("sample") + .setPath("xources/hello/HelloJava.xoo") + .setRuleRepository("xoo") + .setRuleKey("OneIssuePerLine") .setLine(1) - .setSeverity("MAJOR") + .setSeverity(Severity.MAJOR) .setCreationDate(date("14/03/2004")) .setChecksum(DigestUtils.md5Hex("dontexist")) - .setStatus("OPEN")) + .setStatus("OPEN") + .build()) + // Existing issue on project that is no more detected + .mockServerIssue(org.sonar.batch.protocol.input.BatchInput.ServerIssue.newBuilder().setKey("resolved-on-project") + .setModuleKey("sample") + .setRuleRepository("xoo") + .setRuleKey("OneIssuePerModule") + .setSeverity(Severity.CRITICAL) + .setCreationDate(date("14/03/2004")) + .setStatus("OPEN") + .build()) // Manual issue - .addPreviousIssue(new PreviousIssue().setKey("manual") - .setComponentKey("sample:xources/hello/HelloJava.xoo") - .setRuleKey("manual", "MyManualIssue") + .mockServerIssue(org.sonar.batch.protocol.input.BatchInput.ServerIssue.newBuilder().setKey("manual") + .setModuleKey("sample") + .setPath("xources/hello/HelloJava.xoo") + .setRuleRepository("manual") + .setRuleKey("MyManualIssue") .setLine(1) - .setSeverity("MAJOR") + .setSeverity(Severity.MAJOR) .setCreationDate(date("14/03/2004")) .setChecksum(DigestUtils.md5Hex("packagehello;")) - .setStatus("OPEN")) + .setStatus("OPEN") + .build()) .build(); @Before @@ -123,26 +148,35 @@ public class PreviewAndReportsMediumTest { } assertThat(newIssues).isEqualTo(13); assertThat(openIssues).isEqualTo(2); - assertThat(resolvedIssue).isEqualTo(1); + assertThat(resolvedIssue).isEqualTo(2); } @Test public void testConsoleReport() throws Exception { File projectDir = new File(PreviewAndReportsMediumTest.class.getResource("/mediumtest/xoo/sample").toURI()); - TaskResult result = tester + tester .newScanTask(new File(projectDir, "sonar-project.properties")) .property("sonar.issuesReport.console.enable", "true") .start(); - // TODO wait for ability to assert on logs + assertThat(getReportLog()).contains("+13 issues", "+13 major"); + } + + private String getReportLog() { + for (String log : logTester.logs()) { + if (log.contains(ConsoleReport.HEADER)) { + return log; + } + } + throw new IllegalStateException("No console report"); } @Test public void testHtmlReport() throws Exception { File projectDir = new File(PreviewAndReportsMediumTest.class.getResource("/mediumtest/xoo/sample").toURI()); - TaskResult result = tester + tester .newScanTask(new File(projectDir, "sonar-project.properties")) .property("sonar.issuesReport.html.enable", "true") .start(); @@ -151,4 +185,27 @@ public class PreviewAndReportsMediumTest { assertThat(new File(projectDir, ".sonar/issues-report/issues-report-light.html")).exists(); } + @Test + public void testHtmlReportNoFile() throws Exception { + File baseDir = temp.newFolder(); + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "sample") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project") + .put("sonar.sources", "src") + .put("sonar.issuesReport.html.enable", "true") + .build()) + .start(); + + assertThat(FileUtils.readFileToString(new File(baseDir, ".sonar/issues-report/issues-report.html"))).contains("No file analyzed"); + assertThat(FileUtils.readFileToString(new File(baseDir, ".sonar/issues-report/issues-report-light.html"))).contains("No file analyzed"); + } + } diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectScanContainerTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectScanContainerTest.java index d80410825e3..5c98be19187 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectScanContainerTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/ProjectScanContainerTest.java @@ -62,7 +62,7 @@ public class ProjectScanContainerTest { public void prepare() { projectBootstrapper = mock(ProjectBootstrapper.class); bootstrapProperties = new BootstrapProperties(Collections.<String, String>emptyMap()); - DefaultAnalysisMode analysisMode = new DefaultAnalysisMode(bootstrapProperties); + DefaultAnalysisMode analysisMode = new DefaultAnalysisMode(Collections.<String, String>emptyMap()); when(projectBootstrapper.bootstrap()).thenReturn(new ProjectReactor(ProjectDefinition.create())); parentContainer = new ComponentContainer(); parentContainer.add(System2.INSTANCE); diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/report/ConsoleReportTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/report/ConsoleReportTest.java new file mode 100644 index 00000000000..6bbc93aba52 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/report/ConsoleReportTest.java @@ -0,0 +1,134 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.batch.scan.report; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.config.Settings; +import org.sonar.api.issue.internal.DefaultIssue; +import org.sonar.api.rule.Severity; +import org.sonar.api.utils.log.LogTester; +import org.sonar.batch.issue.IssueCache; +import org.sonar.batch.scan.filesystem.InputPathCache; + +import java.util.Arrays; +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ConsoleReportTest { + + @Rule + public LogTester logTester = new LogTester(); + + private Settings settings; + private IssueCache issueCache; + private InputPathCache inputPathCache; + private ConsoleReport report; + + @Before + public void prepare() { + settings = new Settings(); + issueCache = mock(IssueCache.class); + inputPathCache = mock(InputPathCache.class); + report = new ConsoleReport(settings, issueCache, inputPathCache); + } + + @Test + public void dontExecuteByDefault() { + report.execute(); + for (String log : logTester.logs()) { + assertThat(log).doesNotContain(ConsoleReport.HEADER); + } + } + + @Test + public void testNoFile() { + settings.setProperty(ConsoleReport.CONSOLE_REPORT_ENABLED_KEY, "true"); + when(inputPathCache.allFiles()).thenReturn(Collections.<InputFile>emptyList()); + when(issueCache.all()).thenReturn(Collections.<DefaultIssue>emptyList()); + report.execute(); + assertThat(getReportLog()).isEqualTo( + "\n\n------------- Issues Report -------------\n\n" + + " No file analyzed\n" + + "\n-------------------------------------------\n\n"); + } + + @Test + public void testNoNewIssue() { + settings.setProperty(ConsoleReport.CONSOLE_REPORT_ENABLED_KEY, "true"); + when(inputPathCache.allFiles()).thenReturn(Arrays.<InputFile>asList(new DefaultInputFile("foo", "src/Foo.php"))); + when(issueCache.all()).thenReturn(Arrays.asList(new DefaultIssue().setNew(false))); + report.execute(); + assertThat(getReportLog()).isEqualTo( + "\n\n------------- Issues Report -------------\n\n" + + " No new issue\n" + + "\n-------------------------------------------\n\n"); + } + + @Test + public void testOneNewIssue() { + settings.setProperty(ConsoleReport.CONSOLE_REPORT_ENABLED_KEY, "true"); + when(inputPathCache.allFiles()).thenReturn(Arrays.<InputFile>asList(new DefaultInputFile("foo", "src/Foo.php"))); + when(issueCache.all()).thenReturn(Arrays.asList(new DefaultIssue().setNew(true).setSeverity(Severity.BLOCKER))); + report.execute(); + assertThat(getReportLog()).isEqualTo( + "\n\n------------- Issues Report -------------\n\n" + + " +1 issue\n\n" + + " +1 blocking\n" + + "\n-------------------------------------------\n\n"); + } + + @Test + public void testOneNewIssuePerSeverity() { + settings.setProperty(ConsoleReport.CONSOLE_REPORT_ENABLED_KEY, "true"); + when(inputPathCache.allFiles()).thenReturn(Arrays.<InputFile>asList(new DefaultInputFile("foo", "src/Foo.php"))); + when(issueCache.all()).thenReturn(Arrays.asList(new DefaultIssue().setNew(true).setSeverity(Severity.BLOCKER), + new DefaultIssue().setNew(true).setSeverity(Severity.CRITICAL), + new DefaultIssue().setNew(true).setSeverity(Severity.MAJOR), + new DefaultIssue().setNew(true).setSeverity(Severity.MINOR), + new DefaultIssue().setNew(true).setSeverity(Severity.INFO))); + report.execute(); + assertThat(getReportLog()).isEqualTo( + "\n\n------------- Issues Report -------------\n\n" + + " +5 issues\n\n" + + " +1 blocking\n" + + " +1 critical\n" + + " +1 major\n" + + " +1 minor\n" + + " +1 info\n" + + "\n-------------------------------------------\n\n"); + } + + private String getReportLog() { + for (String log : logTester.logs()) { + if (log.contains(ConsoleReport.HEADER)) { + return log; + } + } + throw new IllegalStateException("No console report"); + } + +} |