From: Julien HENRY Date: Mon, 23 Sep 2013 15:26:16 +0000 (+0200) Subject: SONAR-4689 Batch API to get issues of the current project X-Git-Tag: 4.0~333 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=c35c6ba856dc4270e4c29233919c6bf5a06a648b;p=sonarqube.git SONAR-4689 Batch API to get issues of the current project --- diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java b/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java index ca8cf60059c..4d4a47d6df2 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java @@ -49,7 +49,7 @@ import org.sonar.batch.DefaultResourceCreationLock; import org.sonar.batch.ProjectTree; import org.sonar.batch.ResourceFilters; import org.sonar.batch.issue.DeprecatedViolations; -import org.sonar.batch.issue.DefaultModuleIssues; +import org.sonar.batch.issue.ModuleIssues; import org.sonar.core.component.ComponentKeys; import org.sonar.core.component.ScanGraph; @@ -82,7 +82,7 @@ public class DefaultIndex extends SonarIndex { private Map> incomingDependenciesByResource = Maps.newHashMap(); private ProjectTree projectTree; private final DeprecatedViolations deprecatedViolations; - private DefaultModuleIssues moduleIssues; + private ModuleIssues moduleIssues; public DefaultIndex(PersistenceManager persistence, DefaultResourceCreationLock lock, ProjectTree projectTree, MetricFinder metricFinder, ScanGraph graph, DeprecatedViolations deprecatedViolations) { @@ -124,7 +124,7 @@ public class DefaultIndex extends SonarIndex { return currentProject; } - public void setCurrentProject(Project project, ResourceFilters resourceFilters, DefaultModuleIssues moduleIssues) { + public void setCurrentProject(Project project, ResourceFilters resourceFilters, ModuleIssues moduleIssues) { this.currentProject = project; // the following components depend on the current module, so they need to be reloaded. diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/DefaultIssuable.java b/sonar-batch/src/main/java/org/sonar/batch/issue/DefaultIssuable.java index 5b7c4c8092c..98665d60bbd 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/issue/DefaultIssuable.java +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/DefaultIssuable.java @@ -33,11 +33,11 @@ import java.util.List; */ public class DefaultIssuable implements Issuable { - private final DefaultModuleIssues moduleIssues; + private final ModuleIssues moduleIssues; private final IssueCache cache; private final Component component; - DefaultIssuable(Component component, DefaultModuleIssues moduleIssues, IssueCache cache) { + DefaultIssuable(Component component, ModuleIssues moduleIssues, IssueCache cache) { this.component = component; this.moduleIssues = moduleIssues; this.cache = cache; diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/DefaultModuleIssues.java b/sonar-batch/src/main/java/org/sonar/batch/issue/DefaultModuleIssues.java deleted file mode 100644 index 1bc61bd4846..00000000000 --- a/sonar-batch/src/main/java/org/sonar/batch/issue/DefaultModuleIssues.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2013 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; - -import com.google.common.base.Predicate; -import com.google.common.collect.Iterables; -import org.sonar.api.issue.Issue; -import org.sonar.api.issue.ModuleIssues; -import org.sonar.api.issue.internal.DefaultIssue; -import org.sonar.api.profiles.RulesProfile; -import org.sonar.api.resources.Project; -import org.sonar.api.rule.RuleKey; -import org.sonar.api.rules.ActiveRule; -import org.sonar.api.rules.Violation; -import org.sonar.core.issue.DefaultIssueBuilder; - -import javax.annotation.Nullable; - -/** - * Initialize the issues raised during scan. - */ -public class DefaultModuleIssues implements ModuleIssues { - - private final RulesProfile qProfile; - private final IssueCache cache; - private final Project project; - private final IssueFilters filters; - - public DefaultModuleIssues(RulesProfile qProfile, IssueCache cache, Project project, IssueFilters filters) { - this.qProfile = qProfile; - this.cache = cache; - this.project = project; - this.filters = filters; - } - - public boolean initAndAddIssue(DefaultIssue issue) { - return initAndAddIssue(issue, null); - } - - public boolean initAndAddViolation(Violation violation) { - DefaultIssue issue = newIssue(violation); - return initAndAddIssue(issue, violation); - } - - private DefaultIssue newIssue(Violation violation) { - return (DefaultIssue) new DefaultIssueBuilder() - .componentKey(violation.getResource().getEffectiveKey()) - .ruleKey(RuleKey.of(violation.getRule().getRepositoryKey(), violation.getRule().getKey())) - .effortToFix(violation.getCost()) - .line(violation.getLineId()) - .message(violation.getMessage()) - .severity(violation.getSeverity() != null ? violation.getSeverity().name() : null) - .build(); - } - - private boolean initAndAddIssue(DefaultIssue issue, @Nullable Violation violation) { - // TODO fail fast : if rule does not exist - - ActiveRule activeRule = qProfile.getActiveRule(issue.ruleKey().repository(), issue.ruleKey().rule()); - if (activeRule == null || activeRule.getRule() == null) { - // rule does not exist or is not enabled -> ignore the issue - return false; - } - issue.setCreationDate(project.getAnalysisDate()); - issue.setUpdateDate(project.getAnalysisDate()); - if (issue.severity() == null) { - issue.setSeverity(activeRule.getSeverity().name()); - } - - if (filters.accept(issue, violation)) { - cache.put(issue); - return true; - } - return false; - } - - @Override - public Iterable issues() { - return (Iterable) Iterables.filter(cache.all(), new ModulePredicate(project, false)); - } - - @Override - public Iterable resolvedIssues() { - return (Iterable) Iterables.filter(cache.all(), new ModulePredicate(project, true)); - } - - private static class ModulePredicate implements Predicate { - private final boolean resolved; - private final String key; - private final String keyPrefix; - - private ModulePredicate(Project project, boolean resolved) { - this.resolved = resolved; - this.key = project.getEffectiveKey(); - this.keyPrefix = project.getEffectiveKey() + ":"; - } - - @Override - public boolean apply(@Nullable DefaultIssue issue) { - if (issue != null && (issue.componentKey().equals(key) || issue.componentKey().startsWith(keyPrefix))) { - return resolved ? issue.resolution() != null : issue.resolution()==null; - } - return false; - } - } -} diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/DefaultProjectIssues.java b/sonar-batch/src/main/java/org/sonar/batch/issue/DefaultProjectIssues.java new file mode 100644 index 00000000000..3e3154fc010 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/DefaultProjectIssues.java @@ -0,0 +1,67 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 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; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import org.sonar.api.issue.Issue; +import org.sonar.api.issue.ProjectIssues; +import org.sonar.api.issue.internal.DefaultIssue; + +import javax.annotation.Nullable; + +/** + * Expose list of issues for the current project + * @since 4.0 + */ +public class DefaultProjectIssues implements ProjectIssues { + + private final IssueCache cache; + + public DefaultProjectIssues(IssueCache cache) { + this.cache = cache; + } + + @Override + public Iterable issues() { + return (Iterable) Iterables.filter(cache.all(), new ResolvedPredicate(false)); + } + + @Override + public Iterable resolvedIssues() { + return (Iterable) Iterables.filter(cache.all(), new ResolvedPredicate(true)); + } + + private static class ResolvedPredicate implements Predicate { + private final boolean resolved; + + private ResolvedPredicate(boolean resolved) { + this.resolved = resolved; + } + + @Override + public boolean apply(@Nullable DefaultIssue issue) { + if (issue != null) { + return resolved ? issue.resolution() != null : issue.resolution() == null; + } + return false; + } + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/IssuableFactory.java b/sonar-batch/src/main/java/org/sonar/batch/issue/IssuableFactory.java index cd34582c24c..b1c791f4069 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/issue/IssuableFactory.java +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/IssuableFactory.java @@ -33,10 +33,10 @@ import javax.annotation.CheckForNull; */ public class IssuableFactory extends PerspectiveBuilder { - private final DefaultModuleIssues moduleIssues; + private final ModuleIssues moduleIssues; private final IssueCache cache; - public IssuableFactory(DefaultModuleIssues moduleIssues, IssueCache cache) { + public IssuableFactory(ModuleIssues moduleIssues, IssueCache cache) { super(Issuable.class); this.moduleIssues = moduleIssues; this.cache = cache; diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java b/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java new file mode 100644 index 00000000000..7bef92697a9 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java @@ -0,0 +1,89 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 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; + +import org.sonar.api.issue.internal.DefaultIssue; +import org.sonar.api.profiles.RulesProfile; +import org.sonar.api.resources.Project; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rules.ActiveRule; +import org.sonar.api.rules.Violation; +import org.sonar.core.issue.DefaultIssueBuilder; + +import javax.annotation.Nullable; + +/** + * Initialize the issues raised during scan. + */ +public class ModuleIssues { + + private final RulesProfile qProfile; + private final IssueCache cache; + private final Project project; + private final IssueFilters filters; + + public ModuleIssues(RulesProfile qProfile, IssueCache cache, Project project, IssueFilters filters) { + this.qProfile = qProfile; + this.cache = cache; + this.project = project; + this.filters = filters; + } + + public boolean initAndAddIssue(DefaultIssue issue) { + return initAndAddIssue(issue, null); + } + + public boolean initAndAddViolation(Violation violation) { + DefaultIssue issue = newIssue(violation); + return initAndAddIssue(issue, violation); + } + + private DefaultIssue newIssue(Violation violation) { + return (DefaultIssue) new DefaultIssueBuilder() + .componentKey(violation.getResource().getEffectiveKey()) + .ruleKey(RuleKey.of(violation.getRule().getRepositoryKey(), violation.getRule().getKey())) + .effortToFix(violation.getCost()) + .line(violation.getLineId()) + .message(violation.getMessage()) + .severity(violation.getSeverity() != null ? violation.getSeverity().name() : null) + .build(); + } + + private boolean initAndAddIssue(DefaultIssue issue, @Nullable Violation violation) { + // TODO fail fast : if rule does not exist + + ActiveRule activeRule = qProfile.getActiveRule(issue.ruleKey().repository(), issue.ruleKey().rule()); + if (activeRule == null || activeRule.getRule() == null) { + // rule does not exist or is not enabled -> ignore the issue + return false; + } + issue.setCreationDate(project.getAnalysisDate()); + issue.setUpdateDate(project.getAnalysisDate()); + if (issue.severity() == null) { + issue.setSeverity(activeRule.getSeverity().name()); + } + + if (filters.accept(issue, violation)) { + cache.put(issue); + return true; + } + return false; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java index 543f01e2940..b97118be253 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java @@ -19,8 +19,6 @@ */ package org.sonar.batch.scan; -import org.sonar.core.measure.MeasurementFilters; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.BatchExtension; @@ -31,7 +29,13 @@ import org.sonar.api.resources.Languages; import org.sonar.api.resources.Project; import org.sonar.api.scan.filesystem.FileExclusions; import org.sonar.api.scan.filesystem.PathResolver; -import org.sonar.batch.*; +import org.sonar.batch.DefaultProjectClasspath; +import org.sonar.batch.DefaultSensorContext; +import org.sonar.batch.DefaultTimeMachine; +import org.sonar.batch.ProfileProvider; +import org.sonar.batch.ProjectTree; +import org.sonar.batch.ResourceFilters; +import org.sonar.batch.ViolationFilters; import org.sonar.batch.bootstrap.BatchExtensionDictionnary; import org.sonar.batch.bootstrap.ExtensionInstaller; import org.sonar.batch.bootstrap.ExtensionMatcher; @@ -42,11 +46,16 @@ import org.sonar.batch.index.DefaultIndex; import org.sonar.batch.index.ResourcePersister; import org.sonar.batch.issue.IssuableFactory; import org.sonar.batch.issue.IssueFilters; -import org.sonar.batch.issue.DefaultModuleIssues; +import org.sonar.batch.issue.ModuleIssues; import org.sonar.batch.phases.PhaseExecutor; import org.sonar.batch.phases.PhasesTimeProfiler; -import org.sonar.batch.scan.filesystem.*; +import org.sonar.batch.scan.filesystem.DeprecatedFileSystemAdapter; +import org.sonar.batch.scan.filesystem.ExclusionFilters; +import org.sonar.batch.scan.filesystem.FileSystemLogger; +import org.sonar.batch.scan.filesystem.LanguageFilters; +import org.sonar.batch.scan.filesystem.ModuleFileSystemProvider; import org.sonar.core.component.ScanPerspectives; +import org.sonar.core.measure.MeasurementFilters; public class ModuleScanContainer extends ComponentContainer { private static final Logger LOG = LoggerFactory.getLogger(ModuleScanContainer.class); @@ -113,11 +122,10 @@ public class ModuleScanContainer extends ComponentContainer { new ProfileProvider(), // issues - DefaultModuleIssues.class, IssuableFactory.class, + ModuleIssues.class, - ScanPerspectives.class - ); + ScanPerspectives.class); } private void addExtensions() { @@ -140,7 +148,7 @@ public class ModuleScanContainer extends ComponentContainer { DefaultIndex index = getComponentByType(DefaultIndex.class); index.setCurrentProject(module, getComponentByType(ResourceFilters.class), - getComponentByType(DefaultModuleIssues.class)); + getComponentByType(ModuleIssues.class)); getComponentByType(PhaseExecutor.class).execute(module); } 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 9bc0c43c940..7992aab42ea 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 @@ -52,6 +52,7 @@ import org.sonar.batch.index.MemoryOptimizer; import org.sonar.batch.index.ResourceCache; import org.sonar.batch.index.SnapshotCache; import org.sonar.batch.index.SourcePersister; +import org.sonar.batch.issue.DefaultProjectIssues; import org.sonar.batch.issue.DeprecatedViolations; import org.sonar.batch.issue.IssueCache; import org.sonar.batch.issue.IssuePersister; @@ -143,6 +144,7 @@ public class ProjectScanContainer extends ComponentContainer { ScanIssueStorage.class, IssuePersister.class, IssueNotifications.class, + DefaultProjectIssues.class, // tests TestPlanPerspectiveLoader.class, diff --git a/sonar-batch/src/test/java/org/sonar/batch/index/DefaultIndexTest.java b/sonar-batch/src/test/java/org/sonar/batch/index/DefaultIndexTest.java index 2431cefbbb5..cd039e17d9a 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/index/DefaultIndexTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/index/DefaultIndexTest.java @@ -39,7 +39,7 @@ import org.sonar.batch.DefaultResourceCreationLock; import org.sonar.batch.ProjectTree; import org.sonar.batch.ResourceFilters; import org.sonar.batch.issue.DeprecatedViolations; -import org.sonar.batch.issue.DefaultModuleIssues; +import org.sonar.batch.issue.ModuleIssues; import org.sonar.core.component.ScanGraph; import static com.google.common.collect.Lists.newArrayList; @@ -77,7 +77,7 @@ public class DefaultIndexTest { rule = Rule.create("repoKey", "ruleKey", "Rule"); rule.setId(1); rulesProfile.activateRule(rule, null); - index.setCurrentProject(project, new ResourceFilters(new ResourceFilter[]{filter}), mock(DefaultModuleIssues.class)); + index.setCurrentProject(project, new ResourceFilters(new ResourceFilter[]{filter}), mock(ModuleIssues.class)); index.doStart(project); } diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/DefaultIssuableTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/DefaultIssuableTest.java index f66cc03aa16..24a12f98c0f 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/issue/DefaultIssuableTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/issue/DefaultIssuableTest.java @@ -33,7 +33,7 @@ import static org.mockito.Mockito.when; public class DefaultIssuableTest { - DefaultModuleIssues moduleIssues = mock(DefaultModuleIssues.class); + ModuleIssues moduleIssues = mock(ModuleIssues.class); IssueCache cache = mock(IssueCache.class); Component component = mock(Component.class); diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/DefaultModuleIssuesTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/DefaultModuleIssuesTest.java deleted file mode 100644 index 27f14f4bcd0..00000000000 --- a/sonar-batch/src/test/java/org/sonar/batch/issue/DefaultModuleIssuesTest.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2013 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; - -import com.google.common.collect.Lists; -import org.apache.commons.lang.time.DateUtils; -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.sonar.api.issue.Issue; -import org.sonar.api.issue.internal.DefaultIssue; -import org.sonar.api.profiles.RulesProfile; -import org.sonar.api.resources.JavaFile; -import org.sonar.api.resources.Project; -import org.sonar.api.resources.Resource; -import org.sonar.api.rule.RuleKey; -import org.sonar.api.rule.Severity; -import org.sonar.api.rules.ActiveRule; -import org.sonar.api.rules.Rule; -import org.sonar.api.rules.RulePriority; -import org.sonar.api.rules.Violation; - -import java.util.Arrays; -import java.util.Calendar; -import java.util.Date; -import java.util.List; - -import static org.fest.assertions.Assertions.assertThat; -import static org.mockito.Mockito.*; - -public class DefaultModuleIssuesTest { - - static final RuleKey SQUID_RULE_KEY = RuleKey.of("squid", "AvoidCycle"); - - IssueCache cache = mock(IssueCache.class); - RulesProfile qProfile = mock(RulesProfile.class); - Project project = mock(Project.class); - IssueFilters filters = mock(IssueFilters.class); - DefaultModuleIssues moduleIssues = new DefaultModuleIssues(qProfile, cache, project, filters); - - @Before - public void setUp() { - when(project.getAnalysisDate()).thenReturn(new Date()); - when(project.getEffectiveKey()).thenReturn("org.apache:struts-core"); - } - - @Test - public void should_ignore_null_active_rule() throws Exception { - when(qProfile.getActiveRule(anyString(), anyString())).thenReturn(null); - - DefaultIssue issue = new DefaultIssue().setRuleKey(SQUID_RULE_KEY); - boolean added = moduleIssues.initAndAddIssue(issue); - - assertThat(added).isFalse(); - verifyZeroInteractions(cache); - } - - @Test - public void should_ignore_null_rule_of_active_rule() throws Exception { - ActiveRule activeRule = mock(ActiveRule.class); - when(activeRule.getRule()).thenReturn(null); - when(qProfile.getActiveRule(anyString(), anyString())).thenReturn(activeRule); - - DefaultIssue issue = new DefaultIssue().setRuleKey(SQUID_RULE_KEY); - boolean added = moduleIssues.initAndAddIssue(issue); - - assertThat(added).isFalse(); - verifyZeroInteractions(cache); - } - - @Test - public void should_add_issue_to_cache() throws Exception { - Rule rule = Rule.create("squid", "AvoidCycle"); - ActiveRule activeRule = mock(ActiveRule.class); - when(activeRule.getRule()).thenReturn(rule); - when(activeRule.getSeverity()).thenReturn(RulePriority.INFO); - when(qProfile.getActiveRule("squid", "AvoidCycle")).thenReturn(activeRule); - - Date analysisDate = new Date(); - when(project.getAnalysisDate()).thenReturn(analysisDate); - - DefaultIssue issue = new DefaultIssue() - .setKey("ABCDE") - .setRuleKey(SQUID_RULE_KEY) - .setSeverity(Severity.CRITICAL); - when(filters.accept(issue, null)).thenReturn(true); - - boolean added = moduleIssues.initAndAddIssue(issue); - - assertThat(added).isTrue(); - ArgumentCaptor argument = ArgumentCaptor.forClass(DefaultIssue.class); - verify(cache).put(argument.capture()); - assertThat(argument.getValue().severity()).isEqualTo(Severity.CRITICAL); - assertThat(argument.getValue().creationDate()).isEqualTo(DateUtils.truncate(analysisDate, Calendar.SECOND)); - } - - @Test - public void should_use_severity_from_active_rule_if_no_severity() throws Exception { - Rule rule = Rule.create("squid", "AvoidCycle"); - ActiveRule activeRule = mock(ActiveRule.class); - when(activeRule.getRule()).thenReturn(rule); - when(activeRule.getSeverity()).thenReturn(RulePriority.INFO); - when(qProfile.getActiveRule("squid", "AvoidCycle")).thenReturn(activeRule); - - Date analysisDate = new Date(); - when(project.getAnalysisDate()).thenReturn(analysisDate); - - DefaultIssue issue = new DefaultIssue().setRuleKey(SQUID_RULE_KEY).setSeverity(null); - when(filters.accept(issue, null)).thenReturn(true); - moduleIssues.initAndAddIssue(issue); - - ArgumentCaptor argument = ArgumentCaptor.forClass(DefaultIssue.class); - verify(cache).put(argument.capture()); - assertThat(argument.getValue().severity()).isEqualTo(Severity.INFO); - assertThat(argument.getValue().creationDate()).isEqualTo(DateUtils.truncate(analysisDate, Calendar.SECOND)); - } - - @Test - public void should_add_deprecated_violation() throws Exception { - Rule rule = Rule.create("squid", "AvoidCycle"); - Resource resource = new JavaFile("org.struts.Action").setEffectiveKey("struts:org.struts.Action"); - Violation violation = new Violation(rule, resource); - violation.setLineId(42); - violation.setSeverity(RulePriority.CRITICAL); - violation.setMessage("the message"); - - ActiveRule activeRule = mock(ActiveRule.class); - when(activeRule.getRule()).thenReturn(rule); - when(activeRule.getSeverity()).thenReturn(RulePriority.INFO); - when(qProfile.getActiveRule("squid", "AvoidCycle")).thenReturn(activeRule); - when(filters.accept(any(DefaultIssue.class), eq(violation))).thenReturn(true); - - boolean added = moduleIssues.initAndAddViolation(violation); - assertThat(added).isTrue(); - - ArgumentCaptor argument = ArgumentCaptor.forClass(DefaultIssue.class); - verify(cache).put(argument.capture()); - DefaultIssue issue = argument.getValue(); - assertThat(issue.severity()).isEqualTo(Severity.CRITICAL); - assertThat(issue.line()).isEqualTo(42); - assertThat(issue.message()).isEqualTo("the message"); - assertThat(issue.key()).isNotEmpty(); - assertThat(issue.ruleKey().toString()).isEqualTo("squid:AvoidCycle"); - assertThat(issue.componentKey().toString()).isEqualTo("struts:org.struts.Action"); - } - - @Test - public void should_filter_issue() throws Exception { - Rule rule = Rule.create("squid", "AvoidCycle"); - ActiveRule activeRule = mock(ActiveRule.class); - when(activeRule.getRule()).thenReturn(rule); - when(activeRule.getSeverity()).thenReturn(RulePriority.INFO); - when(qProfile.getActiveRule("squid", "AvoidCycle")).thenReturn(activeRule); - - DefaultIssue issue = new DefaultIssue() - .setKey("ABCDE") - .setRuleKey(SQUID_RULE_KEY) - .setSeverity(Severity.CRITICAL); - - when(filters.accept(issue, null)).thenReturn(false); - - boolean added = moduleIssues.initAndAddIssue(issue); - - assertThat(added).isFalse(); - verifyZeroInteractions(cache); - } - - @Test - public void should_get_module_issues() throws Exception { - DefaultIssue issueOnModule = new DefaultIssue().setKey("1").setRuleKey(SQUID_RULE_KEY).setComponentKey("org.apache:struts-core"); - DefaultIssue issueInModule = new DefaultIssue().setKey("2").setRuleKey(SQUID_RULE_KEY).setComponentKey("org.apache:struts-core:Action"); - DefaultIssue resolvedIssueInModule = new DefaultIssue().setKey("3").setRuleKey(SQUID_RULE_KEY).setComponentKey("org.apache:struts-core:Action").setResolution(Issue.RESOLUTION_FIXED); - - when(cache.all()).thenReturn(Arrays.asList( - // issue on root module - new DefaultIssue().setKey("4").setRuleKey(SQUID_RULE_KEY).setSeverity(Severity.CRITICAL).setComponentKey("org.apache:struts"), - - // issue in root module - new DefaultIssue().setKey("5").setRuleKey(SQUID_RULE_KEY).setSeverity(Severity.CRITICAL).setComponentKey("org.apache:struts:FileInRoot"), - - issueOnModule, issueInModule, resolvedIssueInModule - )); - - // unresolved issues - List issues = Lists.newArrayList(moduleIssues.issues()); - assertThat(issues).containsOnly(issueInModule, issueOnModule); - - List resolvedIssues = Lists.newArrayList(moduleIssues.resolvedIssues()); - assertThat(resolvedIssues).containsOnly(resolvedIssueInModule); - } -} diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/DefaultProjectIssuesTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/DefaultProjectIssuesTest.java new file mode 100644 index 00000000000..17a4c4e591a --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/issue/DefaultProjectIssuesTest.java @@ -0,0 +1,64 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 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; + +import com.google.common.collect.Lists; +import org.junit.Test; +import org.sonar.api.issue.Issue; +import org.sonar.api.issue.internal.DefaultIssue; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rule.Severity; + +import java.util.Arrays; +import java.util.List; + +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class DefaultProjectIssuesTest { + + static final RuleKey SQUID_RULE_KEY = RuleKey.of("squid", "AvoidCycle"); + + IssueCache cache = mock(IssueCache.class); + DefaultProjectIssues projectIssues = new DefaultProjectIssues(cache); + + @Test + public void should_get_all_issues() throws Exception { + DefaultIssue issueOnModule = new DefaultIssue().setKey("1").setRuleKey(SQUID_RULE_KEY).setComponentKey("org.apache:struts-core"); + DefaultIssue issueInModule = new DefaultIssue().setKey("2").setRuleKey(SQUID_RULE_KEY).setComponentKey("org.apache:struts-core:Action"); + DefaultIssue resolvedIssueInModule = new DefaultIssue().setKey("3").setRuleKey(SQUID_RULE_KEY).setComponentKey("org.apache:struts-core:Action") + .setResolution(Issue.RESOLUTION_FIXED); + + DefaultIssue issueOnRoot = new DefaultIssue().setKey("4").setRuleKey(SQUID_RULE_KEY).setSeverity(Severity.CRITICAL).setComponentKey("org.apache:struts"); + DefaultIssue issueInRoot = new DefaultIssue().setKey("5").setRuleKey(SQUID_RULE_KEY).setSeverity(Severity.CRITICAL).setComponentKey("org.apache:struts:FileInRoot"); + when(cache.all()).thenReturn(Arrays. asList( + issueOnRoot, issueInRoot, + issueOnModule, issueInModule, resolvedIssueInModule + )); + + // unresolved issues + List issues = Lists.newArrayList(projectIssues.issues()); + assertThat(issues).containsOnly(issueOnRoot, issueInRoot, issueInModule, issueOnModule); + + List resolvedIssues = Lists.newArrayList(projectIssues.resolvedIssues()); + assertThat(resolvedIssues).containsOnly(resolvedIssueInModule); + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/IssuableFactoryTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/IssuableFactoryTest.java index 65e4b5d706c..1fc8e129ee3 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/issue/IssuableFactoryTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/issue/IssuableFactoryTest.java @@ -34,7 +34,7 @@ import static org.mockito.Mockito.mock; public class IssuableFactoryTest { - DefaultModuleIssues moduleIssues = mock(DefaultModuleIssues.class); + ModuleIssues moduleIssues = mock(ModuleIssues.class); IssueCache cache = mock(IssueCache.class, Mockito.RETURNS_MOCKS); @Test diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java new file mode 100644 index 00000000000..2fc2f8d2543 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java @@ -0,0 +1,187 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 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; + +import org.apache.commons.lang.time.DateUtils; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.sonar.api.issue.internal.DefaultIssue; +import org.sonar.api.profiles.RulesProfile; +import org.sonar.api.resources.JavaFile; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rule.Severity; +import org.sonar.api.rules.ActiveRule; +import org.sonar.api.rules.Rule; +import org.sonar.api.rules.RulePriority; +import org.sonar.api.rules.Violation; + +import java.util.Calendar; +import java.util.Date; + +import static org.fest.assertions.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +public class ModuleIssuesTest { + + static final RuleKey SQUID_RULE_KEY = RuleKey.of("squid", "AvoidCycle"); + + IssueCache cache = mock(IssueCache.class); + RulesProfile qProfile = mock(RulesProfile.class); + Project project = mock(Project.class); + IssueFilters filters = mock(IssueFilters.class); + ModuleIssues moduleIssues = new ModuleIssues(qProfile, cache, project, filters); + + @Before + public void setUp() { + when(project.getAnalysisDate()).thenReturn(new Date()); + when(project.getEffectiveKey()).thenReturn("org.apache:struts-core"); + } + + @Test + public void should_ignore_null_active_rule() throws Exception { + when(qProfile.getActiveRule(anyString(), anyString())).thenReturn(null); + + DefaultIssue issue = new DefaultIssue().setRuleKey(SQUID_RULE_KEY); + boolean added = moduleIssues.initAndAddIssue(issue); + + assertThat(added).isFalse(); + verifyZeroInteractions(cache); + } + + @Test + public void should_ignore_null_rule_of_active_rule() throws Exception { + ActiveRule activeRule = mock(ActiveRule.class); + when(activeRule.getRule()).thenReturn(null); + when(qProfile.getActiveRule(anyString(), anyString())).thenReturn(activeRule); + + DefaultIssue issue = new DefaultIssue().setRuleKey(SQUID_RULE_KEY); + boolean added = moduleIssues.initAndAddIssue(issue); + + assertThat(added).isFalse(); + verifyZeroInteractions(cache); + } + + @Test + public void should_add_issue_to_cache() throws Exception { + Rule rule = Rule.create("squid", "AvoidCycle"); + ActiveRule activeRule = mock(ActiveRule.class); + when(activeRule.getRule()).thenReturn(rule); + when(activeRule.getSeverity()).thenReturn(RulePriority.INFO); + when(qProfile.getActiveRule("squid", "AvoidCycle")).thenReturn(activeRule); + + Date analysisDate = new Date(); + when(project.getAnalysisDate()).thenReturn(analysisDate); + + DefaultIssue issue = new DefaultIssue() + .setKey("ABCDE") + .setRuleKey(SQUID_RULE_KEY) + .setSeverity(Severity.CRITICAL); + when(filters.accept(issue, null)).thenReturn(true); + + boolean added = moduleIssues.initAndAddIssue(issue); + + assertThat(added).isTrue(); + ArgumentCaptor argument = ArgumentCaptor.forClass(DefaultIssue.class); + verify(cache).put(argument.capture()); + assertThat(argument.getValue().severity()).isEqualTo(Severity.CRITICAL); + assertThat(argument.getValue().creationDate()).isEqualTo(DateUtils.truncate(analysisDate, Calendar.SECOND)); + } + + @Test + public void should_use_severity_from_active_rule_if_no_severity() throws Exception { + Rule rule = Rule.create("squid", "AvoidCycle"); + ActiveRule activeRule = mock(ActiveRule.class); + when(activeRule.getRule()).thenReturn(rule); + when(activeRule.getSeverity()).thenReturn(RulePriority.INFO); + when(qProfile.getActiveRule("squid", "AvoidCycle")).thenReturn(activeRule); + + Date analysisDate = new Date(); + when(project.getAnalysisDate()).thenReturn(analysisDate); + + DefaultIssue issue = new DefaultIssue().setRuleKey(SQUID_RULE_KEY).setSeverity(null); + when(filters.accept(issue, null)).thenReturn(true); + moduleIssues.initAndAddIssue(issue); + + ArgumentCaptor argument = ArgumentCaptor.forClass(DefaultIssue.class); + verify(cache).put(argument.capture()); + assertThat(argument.getValue().severity()).isEqualTo(Severity.INFO); + assertThat(argument.getValue().creationDate()).isEqualTo(DateUtils.truncate(analysisDate, Calendar.SECOND)); + } + + @Test + public void should_add_deprecated_violation() throws Exception { + Rule rule = Rule.create("squid", "AvoidCycle"); + Resource resource = new JavaFile("org.struts.Action").setEffectiveKey("struts:org.struts.Action"); + Violation violation = new Violation(rule, resource); + violation.setLineId(42); + violation.setSeverity(RulePriority.CRITICAL); + violation.setMessage("the message"); + + ActiveRule activeRule = mock(ActiveRule.class); + when(activeRule.getRule()).thenReturn(rule); + when(activeRule.getSeverity()).thenReturn(RulePriority.INFO); + when(qProfile.getActiveRule("squid", "AvoidCycle")).thenReturn(activeRule); + when(filters.accept(any(DefaultIssue.class), eq(violation))).thenReturn(true); + + boolean added = moduleIssues.initAndAddViolation(violation); + assertThat(added).isTrue(); + + ArgumentCaptor argument = ArgumentCaptor.forClass(DefaultIssue.class); + verify(cache).put(argument.capture()); + DefaultIssue issue = argument.getValue(); + assertThat(issue.severity()).isEqualTo(Severity.CRITICAL); + assertThat(issue.line()).isEqualTo(42); + assertThat(issue.message()).isEqualTo("the message"); + assertThat(issue.key()).isNotEmpty(); + assertThat(issue.ruleKey().toString()).isEqualTo("squid:AvoidCycle"); + assertThat(issue.componentKey().toString()).isEqualTo("struts:org.struts.Action"); + } + + @Test + public void should_filter_issue() throws Exception { + Rule rule = Rule.create("squid", "AvoidCycle"); + ActiveRule activeRule = mock(ActiveRule.class); + when(activeRule.getRule()).thenReturn(rule); + when(activeRule.getSeverity()).thenReturn(RulePriority.INFO); + when(qProfile.getActiveRule("squid", "AvoidCycle")).thenReturn(activeRule); + + DefaultIssue issue = new DefaultIssue() + .setKey("ABCDE") + .setRuleKey(SQUID_RULE_KEY) + .setSeverity(Severity.CRITICAL); + + when(filters.accept(issue, null)).thenReturn(false); + + boolean added = moduleIssues.initAndAddIssue(issue); + + assertThat(added).isFalse(); + verifyZeroInteractions(cache); + } + +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/ModuleIssues.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/ModuleIssues.java deleted file mode 100644 index 2f1a974f51c..00000000000 --- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/ModuleIssues.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2013 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.api.issue; - -import org.sonar.api.BatchComponent; - -/** - * Used by batch components to get the issues of the current module. It does not allow - * to get issues of all project modules. - * - * @since 4.0 - */ -public interface ModuleIssues extends BatchComponent { - - /** - * All the unresolved issues of the current module, including the issues reported by end-users. - */ - Iterable issues(); - - /** - * All the issues of this module that have been marked as resolved during this scan - */ - Iterable resolvedIssues(); -} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/ProjectIssues.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/ProjectIssues.java new file mode 100644 index 00000000000..36659896d68 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/ProjectIssues.java @@ -0,0 +1,47 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2013 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.api.issue; + +import org.sonar.api.BatchComponent; +import org.sonar.api.batch.InstantiationStrategy; +import org.sonar.api.batch.PostJob; + +import static org.sonar.api.batch.InstantiationStrategy.PER_BATCH; + +/** + * Used by batch components to get the issues of the project. You have to wait for all + * issues to have been computed (for example in a {@link PostJob}) to be sure all issues have + * been computed. + * + * @since 4.0 + */ +@InstantiationStrategy(PER_BATCH) +public interface ProjectIssues extends BatchComponent { + + /** + * All the unresolved issues of the project, including the issues reported by end-users. + */ + Iterable issues(); + + /** + * All the issues of this project that have been marked as resolved during this scan + */ + Iterable resolvedIssues(); +}