From fd90efb85f075b9f048a9ebbc6f2438d6c153dd5 Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Wed, 26 Aug 2015 12:25:31 +0200 Subject: [PATCH] SONAR-6730 Add issues in Measure API --- .../issue/ComponentIssuesRepository.java | 4 +- .../issue/ComponentIssuesRepositoryImpl.java | 8 +- .../issue/IntegrateIssuesVisitor.java | 3 +- .../MutableComponentIssuesRepository.java | 4 +- .../measure/MeasureComputersVisitor.java | 8 +- .../MeasureComputerImplementationContext.java | 22 ++- .../ComponentIssuesRepositoryImplTest.java | 5 +- .../issue/ComponentIssuesRepositoryRule.java | 12 +- .../measure/MeasureComputersVisitorTest.java | 12 +- ...sureComputerImplementationContextTest.java | 58 ++++++- .../org/sonar/core/issue/DefaultIssue.java | 7 +- .../java/org/sonar/api/ce/measure/Issue.java | 52 ++++++ .../sonar/api/ce/measure/MeasureComputer.java | 6 + .../sonar/api/test/ce/measure/IssueImpl.java | 157 ++++++++++++++++++ .../MeasureComputerImplementationContext.java | 12 ++ .../api/test/ce/measure/package-info.java | 24 +++ 16 files changed, 351 insertions(+), 43 deletions(-) create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/ce/measure/Issue.java create mode 100644 sonar-plugin-api/src/test/java/org/sonar/api/test/ce/measure/IssueImpl.java create mode 100644 sonar-plugin-api/src/test/java/org/sonar/api/test/ce/measure/package-info.java diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/ComponentIssuesRepository.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/ComponentIssuesRepository.java index 51a87d20910..fd8900ce56a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/ComponentIssuesRepository.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/ComponentIssuesRepository.java @@ -22,7 +22,7 @@ package org.sonar.server.computation.issue; import java.util.List; import org.sonar.api.ce.measure.MeasureComputer; -import org.sonar.api.issue.Issue; +import org.sonar.core.issue.DefaultIssue; import org.sonar.server.computation.component.Component; /** @@ -40,6 +40,6 @@ public interface ComponentIssuesRepository { * @throws IllegalStateException if no issues have been set * @throws IllegalArgumentException if the component is not the component that contains current issues. */ - List getIssues(Component component); + List getIssues(Component component); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/ComponentIssuesRepositoryImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/ComponentIssuesRepositoryImpl.java index 16dfd4c9242..b4248320df8 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/ComponentIssuesRepositoryImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/ComponentIssuesRepositoryImpl.java @@ -22,7 +22,7 @@ package org.sonar.server.computation.issue; import java.util.List; import javax.annotation.CheckForNull; -import org.sonar.api.issue.Issue; +import org.sonar.core.issue.DefaultIssue; import org.sonar.server.computation.component.Component; import static com.google.common.base.Preconditions.checkArgument; @@ -32,19 +32,19 @@ import static java.util.Objects.requireNonNull; public class ComponentIssuesRepositoryImpl implements MutableComponentIssuesRepository { @CheckForNull - private List issues; + private List issues; @CheckForNull private Component component; @Override - public void setIssues(Component component, List issues) { + public void setIssues(Component component, List issues) { this.issues = requireNonNull(issues, "issues cannot be null"); this.component = requireNonNull(component, "component cannot be null"); } @Override - public List getIssues(Component component) { + public List getIssues(Component component) { checkState(this.component != null && this.issues != null, "Issues have not been initialized"); checkArgument(component.equals(this.component), String.format("Only issues from component '%s' are available, but wanted component is '%s'.", diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IntegrateIssuesVisitor.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IntegrateIssuesVisitor.java index 595b51160ee..db9cfb79529 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IntegrateIssuesVisitor.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IntegrateIssuesVisitor.java @@ -22,7 +22,6 @@ package org.sonar.server.computation.issue; import java.util.ArrayList; import java.util.List; import java.util.Map; -import org.sonar.api.issue.Issue; import org.sonar.api.utils.log.Loggers; import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.tracking.Tracking; @@ -42,7 +41,7 @@ public class IntegrateIssuesVisitor extends TypeAwareVisitorAdapter { private final MutableComponentIssuesRepository componentIssuesRepository; private final ComponentsWithUnprocessedIssues componentsWithUnprocessedIssues; - private final List componentIssues = new ArrayList<>(); + private final List componentIssues = new ArrayList<>(); public IntegrateIssuesVisitor(TrackerExecution tracker, IssueCache issueCache, IssueLifecycle issueLifecycle, IssueVisitors issueVisitors, ComponentsWithUnprocessedIssues componentsWithUnprocessedIssues, MutableComponentIssuesRepository componentIssuesRepository) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/MutableComponentIssuesRepository.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/MutableComponentIssuesRepository.java index 6086381ee18..a860d460a39 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/MutableComponentIssuesRepository.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/MutableComponentIssuesRepository.java @@ -21,7 +21,7 @@ package org.sonar.server.computation.issue; import java.util.List; -import org.sonar.api.issue.Issue; +import org.sonar.core.issue.DefaultIssue; import org.sonar.server.computation.component.Component; public interface MutableComponentIssuesRepository extends ComponentIssuesRepository { @@ -32,6 +32,6 @@ public interface MutableComponentIssuesRepository extends ComponentIssuesReposit * @throws NullPointerException if {@code component} is {@code null} * @throws NullPointerException if {@code issues} is {@code null} */ - void setIssues(Component component, List issues); + void setIssues(Component component, List issues); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/measure/MeasureComputersVisitor.java b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/MeasureComputersVisitor.java index b47f793a702..43ad33fdf05 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/measure/MeasureComputersVisitor.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/MeasureComputersVisitor.java @@ -24,6 +24,7 @@ import org.sonar.api.ce.measure.MeasureComputer; import org.sonar.server.computation.component.CrawlerDepthLimit; import org.sonar.server.computation.component.SettingsRepository; import org.sonar.server.computation.component.TypeAwareVisitorAdapter; +import org.sonar.server.computation.issue.ComponentIssuesRepository; import org.sonar.server.computation.measure.api.MeasureComputerImplementationContext; import org.sonar.server.computation.metric.MetricRepository; @@ -36,20 +37,23 @@ public class MeasureComputersVisitor extends TypeAwareVisitorAdapter { private final SettingsRepository settings; private final MeasureComputersHolder measureComputersHolder; + private final ComponentIssuesRepository componentIssuesRepository; public MeasureComputersVisitor(MetricRepository metricRepository, MeasureRepository measureRepository, SettingsRepository settings, - MeasureComputersHolder measureComputersHolder) { + MeasureComputersHolder measureComputersHolder, ComponentIssuesRepository componentIssuesRepository) { super(CrawlerDepthLimit.FILE, PRE_ORDER); this.metricRepository = metricRepository; this.measureRepository = measureRepository; this.settings = settings; this.measureComputersHolder = measureComputersHolder; + this.componentIssuesRepository = componentIssuesRepository; } @Override public void visitAny(org.sonar.server.computation.component.Component component) { for (MeasureComputer computer : measureComputersHolder.getMeasureComputers()) { - MeasureComputerImplementationContext measureComputerContext = new MeasureComputerImplementationContext(component, computer, settings, measureRepository, metricRepository); + MeasureComputerImplementationContext measureComputerContext = new MeasureComputerImplementationContext(component, computer, + settings, measureRepository, metricRepository, componentIssuesRepository); computer.getImplementation().compute(measureComputerContext); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/measure/api/MeasureComputerImplementationContext.java b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/api/MeasureComputerImplementationContext.java index e8f74f449a9..44516db373a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/measure/api/MeasureComputerImplementationContext.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/api/MeasureComputerImplementationContext.java @@ -25,15 +25,19 @@ import com.google.common.base.Optional; import com.google.common.base.Predicates; import com.google.common.collect.FluentIterable; import java.util.HashSet; +import java.util.List; import java.util.Set; import javax.annotation.CheckForNull; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.sonar.api.ce.measure.Component; +import org.sonar.api.ce.measure.Issue; import org.sonar.api.ce.measure.Measure; import org.sonar.api.ce.measure.MeasureComputer; import org.sonar.api.ce.measure.Settings; +import org.sonar.core.issue.DefaultIssue; import org.sonar.server.computation.component.SettingsRepository; +import org.sonar.server.computation.issue.ComponentIssuesRepository; import org.sonar.server.computation.measure.MeasureRepository; import org.sonar.server.computation.metric.Metric; import org.sonar.server.computation.metric.MetricRepository; @@ -50,17 +54,19 @@ public class MeasureComputerImplementationContext implements MeasureComputer.Imp private final org.sonar.server.computation.component.Component internalComponent; private final Component component; + private final List componentIssues; private final Set allowedMetrics; public MeasureComputerImplementationContext(org.sonar.server.computation.component.Component component, MeasureComputer measureComputer, - SettingsRepository settings, MeasureRepository measureRepository, MetricRepository metricRepository) { + SettingsRepository settings, MeasureRepository measureRepository, MetricRepository metricRepository, ComponentIssuesRepository componentIssuesRepository) { this.measureComputer = measureComputer; this.settings = settings; this.internalComponent = component; this.measureRepository = measureRepository; this.metricRepository = metricRepository; this.component = newComponent(component); + this.componentIssues = componentIssuesRepository.getIssues(component); this.allowedMetrics = allowedMetric(measureComputer); } @@ -70,11 +76,10 @@ public class MeasureComputerImplementationContext implements MeasureComputer.Imp Component.Type.valueOf(component.getType().name()), component.getType() == org.sonar.server.computation.component.Component.Type.FILE ? new ComponentImpl.FileAttributesImpl(component.getFileAttributes().getLanguageKey(), component.getFileAttributes().isUnitTest()) : - null - ); + null); } - private static Set allowedMetric(MeasureComputer measureComputer){ + private static Set allowedMetric(MeasureComputer measureComputer) { Set allowedMetrics = new HashSet<>(); allowedMetrics.addAll(measureComputer.getInputMetrics()); allowedMetrics.addAll(measureComputer.getOutputMetrics()); @@ -102,7 +107,7 @@ public class MeasureComputerImplementationContext implements MeasureComputer.Imp }; } - private org.sonar.api.config.Settings getComponentSettings(){ + private org.sonar.api.config.Settings getComponentSettings() { return settings.getSettings(internalComponent); } @@ -165,6 +170,11 @@ public class MeasureComputerImplementationContext implements MeasureComputer.Imp } } + @Override + public List getIssues() { + return componentIssues; + } + private class ComponentToMeasure implements Function> { private final Metric metric; @@ -173,7 +183,6 @@ public class MeasureComputerImplementationContext implements MeasureComputer.Imp this.metric = metric; } - @Nullable @Override public Optional apply(@Nonnull org.sonar.server.computation.component.Component input) { return measureRepository.getRawMeasure(input, metric); @@ -189,4 +198,5 @@ public class MeasureComputerImplementationContext implements MeasureComputer.Imp return input.isPresent() ? new MeasureImpl(input.get()) : null; } } + } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/ComponentIssuesRepositoryImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/ComponentIssuesRepositoryImplTest.java index 1ec999ae1db..4188f835808 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/ComponentIssuesRepositoryImplTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/ComponentIssuesRepositoryImplTest.java @@ -24,7 +24,6 @@ import java.util.Arrays; import java.util.Collections; import org.junit.Test; import org.junit.rules.ExpectedException; -import org.sonar.api.issue.Issue; import org.sonar.core.issue.DefaultIssue; import org.sonar.server.computation.component.Component; @@ -39,7 +38,7 @@ public class ComponentIssuesRepositoryImplTest { static final Component FILE_1 = builder(Component.Type.FILE, 1).build(); static final Component FILE_2 = builder(Component.Type.FILE, 2).build(); - static final Issue DUMB_ISSUE = new DefaultIssue().setKey("ISSUE"); + static final DefaultIssue DUMB_ISSUE = new DefaultIssue().setKey("ISSUE"); ComponentIssuesRepositoryImpl sut = new ComponentIssuesRepositoryImpl(); @@ -52,7 +51,7 @@ public class ComponentIssuesRepositoryImplTest { @Test public void set_empty_issues() throws Exception { - sut.setIssues(FILE_1, Collections.emptyList()); + sut.setIssues(FILE_1, Collections.emptyList()); assertThat(sut.getIssues(FILE_1)).isEmpty(); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/ComponentIssuesRepositoryRule.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/ComponentIssuesRepositoryRule.java index ab7995b07e3..dd1950d026f 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/ComponentIssuesRepositoryRule.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/ComponentIssuesRepositoryRule.java @@ -23,7 +23,7 @@ package org.sonar.server.computation.issue; import java.util.List; import javax.annotation.CheckForNull; import org.junit.rules.ExternalResource; -import org.sonar.api.issue.Issue; +import org.sonar.core.issue.DefaultIssue; import org.sonar.server.computation.component.Component; import org.sonar.server.computation.component.ReportTreeRootHolder; @@ -37,7 +37,7 @@ public class ComponentIssuesRepositoryRule extends ExternalResource implements M private final ReportTreeRootHolder reportTreeRootHolder; @CheckForNull - private List issues; + private List issues; @CheckForNull private Component component; @@ -47,12 +47,12 @@ public class ComponentIssuesRepositoryRule extends ExternalResource implements M } @Override - public void setIssues(Component component, List issues) { + public void setIssues(Component component, List issues) { checkNotNull(component, "component cannot be null"); setIssues(component.getReportAttributes().getRef(), issues); } - public void setIssues(int componentRef, List issues) { + public void setIssues(int componentRef, List issues) { this.issues = requireNonNull(issues, "issues cannot be null"); Component component = reportTreeRootHolder.getComponentByRef(componentRef); checkArgument(component != null, String.format("Component '%s' does not exists in the report ", componentRef)); @@ -60,12 +60,12 @@ public class ComponentIssuesRepositoryRule extends ExternalResource implements M } @Override - public List getIssues(Component component) { + public List getIssues(Component component) { checkNotNull(component, "component cannot be null"); return getIssues(component.getReportAttributes().getRef()); } - public List getIssues(int componentRef) { + public List getIssues(int componentRef) { checkState(this.component != null && this.issues != null, "Issues have not been initialized"); Component component = reportTreeRootHolder.getComponentByRef(componentRef); checkArgument(component != null, String.format("Component '%s' does not exists in the report ", componentRef)); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureComputersVisitorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureComputersVisitorTest.java index c43e70139ec..9aa1cac9d2e 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureComputersVisitorTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureComputersVisitorTest.java @@ -25,14 +25,17 @@ import java.util.Collections; import org.junit.Rule; import org.junit.Test; import org.sonar.api.ce.measure.MeasureComputer; +import org.sonar.server.computation.batch.TreeRootHolderRule; import org.sonar.server.computation.component.Component; import org.sonar.server.computation.component.ComponentVisitor; import org.sonar.server.computation.component.VisitorsCrawler; +import org.sonar.server.computation.issue.ComponentIssuesRepository; import org.sonar.server.computation.measure.api.MeasureComputerImpl; import org.sonar.server.computation.metric.MetricRepositoryRule; import static com.google.common.collect.Lists.newArrayList; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; import static org.sonar.api.measures.CoreMetrics.COMMENT_LINES; import static org.sonar.api.measures.CoreMetrics.COMMENT_LINES_KEY; import static org.sonar.api.measures.CoreMetrics.NCLOC; @@ -73,6 +76,9 @@ public class MeasureComputersVisitorTest { ).build() ).build(); + @Rule + public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule().setRoot(ROOT); + @Rule public MetricRepositoryRule metricRepository = new MetricRepositoryRule() .add(NCLOC) @@ -82,6 +88,8 @@ public class MeasureComputersVisitorTest { @Rule public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(ROOT, metricRepository); + ComponentIssuesRepository componentIssuesRepository = mock(ComponentIssuesRepository.class); + MeasureComputersHolderImpl measureComputersHolder = new MeasureComputersHolderImpl(); @Test @@ -116,7 +124,7 @@ public class MeasureComputersVisitorTest { .build() )); - VisitorsCrawler visitorsCrawler = new VisitorsCrawler(Arrays.asList(new MeasureComputersVisitor(metricRepository, measureRepository, null, measureComputersHolder))); + VisitorsCrawler visitorsCrawler = new VisitorsCrawler(Arrays.asList(new MeasureComputersVisitor(metricRepository, measureRepository, null, measureComputersHolder, componentIssuesRepository))); visitorsCrawler.visit(ROOT); assertThat(toEntries(measureRepository.getAddedRawMeasures(FILE_1_REF))).containsOnly(entryOf(NEW_METRIC_KEY, newMeasureBuilder().create(12))); @@ -140,7 +148,7 @@ public class MeasureComputersVisitorTest { measureRepository.addRawMeasure(ROOT_REF, COMMENT_LINES_KEY, newMeasureBuilder().create(7)); measureComputersHolder.setMeasureComputers(Collections.emptyList()); - VisitorsCrawler visitorsCrawler = new VisitorsCrawler(Arrays.asList(new MeasureComputersVisitor(metricRepository, measureRepository, null, measureComputersHolder))); + VisitorsCrawler visitorsCrawler = new VisitorsCrawler(Arrays.asList(new MeasureComputersVisitor(metricRepository, measureRepository, null, measureComputersHolder, componentIssuesRepository))); visitorsCrawler.visit(ROOT); assertThat(toEntries(measureRepository.getAddedRawMeasures(FILE_1_REF))).isEmpty(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/measure/api/MeasureComputerImplementationContextTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/measure/api/MeasureComputerImplementationContextTest.java index 3bb8df5ba43..aa8beac7e55 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/measure/api/MeasureComputerImplementationContextTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/measure/api/MeasureComputerImplementationContextTest.java @@ -21,7 +21,9 @@ package org.sonar.server.computation.measure.api; import com.google.common.base.Optional; +import java.util.Arrays; import java.util.Collections; +import java.util.List; import java.util.Set; import org.junit.Rule; import org.junit.Test; @@ -29,8 +31,12 @@ import org.junit.rules.ExpectedException; import org.sonar.api.ce.measure.Component; import org.sonar.api.ce.measure.MeasureComputer; import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.utils.Duration; +import org.sonar.core.issue.DefaultIssue; import org.sonar.server.computation.batch.TreeRootHolderRule; import org.sonar.server.computation.component.SettingsRepository; +import org.sonar.server.computation.issue.ComponentIssuesRepositoryRule; import org.sonar.server.computation.measure.Measure; import org.sonar.server.computation.measure.MeasureRepositoryRule; import org.sonar.server.computation.metric.Metric; @@ -62,7 +68,9 @@ public class MeasureComputerImplementationContextTest { private static final String FILE_1_KEY = "fileKey"; private static final int FILE_2_REF = 12342; - private static final org.sonar.server.computation.component.Component FILE_1 = builder(org.sonar.server.computation.component.Component.Type.FILE, FILE_1_REF).setKey(FILE_1_KEY).build(); + private static final org.sonar.server.computation.component.Component FILE_1 = builder(org.sonar.server.computation.component.Component.Type.FILE, FILE_1_REF) + .setKey(FILE_1_KEY) + .build(); @Rule public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule() @@ -78,12 +86,14 @@ public class MeasureComputerImplementationContextTest { .add(new MetricImpl(2, INT_METRIC_KEY, "int metric", Metric.MetricType.INT)) .add(new MetricImpl(3, DOUBLE_METRIC_KEY, "double metric", Metric.MetricType.FLOAT)) .add(new MetricImpl(4, LONG_METRIC_KEY, "long metric", Metric.MetricType.MILLISEC)) - .add(new MetricImpl(5, STRING_METRIC_KEY, "string metric", Metric.MetricType.STRING)) - ; + .add(new MetricImpl(5, STRING_METRIC_KEY, "string metric", Metric.MetricType.STRING)); @Rule public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository); + @Rule + public ComponentIssuesRepositoryRule componentIssuesRepository = new ComponentIssuesRepositoryRule(treeRootHolder); + SettingsRepository settingsRepository = mock(SettingsRepository.class); @Test @@ -173,7 +183,7 @@ public class MeasureComputerImplementationContextTest { MeasureComputer.Implementation.Context underTest = newContext(PROJECT_REF, of(NCLOC_KEY), of(INT_METRIC_KEY)); underTest.addMeasure(INT_METRIC_KEY, 10); - Optional measure = measureRepository.getAddedRawMeasure(PROJECT_REF, INT_METRIC_KEY); + Optional measure = measureRepository.getAddedRawMeasure(PROJECT_REF, INT_METRIC_KEY); assertThat(measure).isPresent(); assertThat(measure.get().getIntValue()).isEqualTo(10); } @@ -183,7 +193,7 @@ public class MeasureComputerImplementationContextTest { MeasureComputer.Implementation.Context underTest = newContext(PROJECT_REF, of(NCLOC_KEY), of(DOUBLE_METRIC_KEY)); underTest.addMeasure(DOUBLE_METRIC_KEY, 10d); - Optional measure = measureRepository.getAddedRawMeasure(PROJECT_REF, DOUBLE_METRIC_KEY); + Optional measure = measureRepository.getAddedRawMeasure(PROJECT_REF, DOUBLE_METRIC_KEY); assertThat(measure).isPresent(); assertThat(measure.get().getDoubleValue()).isEqualTo(10d); } @@ -193,7 +203,7 @@ public class MeasureComputerImplementationContextTest { MeasureComputer.Implementation.Context underTest = newContext(PROJECT_REF, of(NCLOC_KEY), of(LONG_METRIC_KEY)); underTest.addMeasure(LONG_METRIC_KEY, 10L); - Optional measure = measureRepository.getAddedRawMeasure(PROJECT_REF, LONG_METRIC_KEY); + Optional measure = measureRepository.getAddedRawMeasure(PROJECT_REF, LONG_METRIC_KEY); assertThat(measure).isPresent(); assertThat(measure.get().getLongValue()).isEqualTo(10L); } @@ -203,7 +213,7 @@ public class MeasureComputerImplementationContextTest { MeasureComputer.Implementation.Context underTest = newContext(PROJECT_REF, of(NCLOC_KEY), of(STRING_METRIC_KEY)); underTest.addMeasure(STRING_METRIC_KEY, "data"); - Optional measure = measureRepository.getAddedRawMeasure(PROJECT_REF, STRING_METRIC_KEY); + Optional measure = measureRepository.getAddedRawMeasure(PROJECT_REF, STRING_METRIC_KEY); assertThat(measure).isPresent(); assertThat(measure.get().getStringValue()).isEqualTo("data"); } @@ -228,11 +238,42 @@ public class MeasureComputerImplementationContextTest { underTest.addMeasure(INT_METRIC_KEY, 10); } + @Test + public void get_issues() throws Exception { + DefaultIssue issue = new DefaultIssue() + .setKey("KEY") + .setRuleKey(RuleKey.of("xoo", "S01")) + .setSeverity("MAJOR") + .setStatus("CLOSED") + .setResolution("FIXED") + .setDebt(Duration.create(10l)); + + MeasureComputer.Implementation.Context underTest = newContext(PROJECT_REF, Arrays.asList(issue)); + + assertThat(underTest.getIssues()).hasSize(1); + org.sonar.api.ce.measure.Issue result = underTest.getIssues().get(0); + assertThat(result.key()).isEqualTo("KEY"); + assertThat(result.ruleKey()).isEqualTo(RuleKey.of("xoo", "S01")); + assertThat(result.severity()).isEqualTo("MAJOR"); + assertThat(result.status()).isEqualTo("CLOSED"); + assertThat(result.resolution()).isEqualTo("FIXED"); + assertThat(result.debt()).isEqualTo(Duration.create(10l)); + } + private MeasureComputer.Implementation.Context newContext(int componentRef) { return newContext(componentRef, Collections.emptySet(), Collections.emptySet()); } + private MeasureComputer.Implementation.Context newContext(int componentRef, List issues) { + return newContext(componentRef, Collections.emptySet(), Collections.emptySet(), issues); + } + private MeasureComputer.Implementation.Context newContext(int componentRef, final Set inputMetrics, final Set outputMetrics) { + return newContext(componentRef, inputMetrics, outputMetrics, Collections.emptyList()); + } + + private MeasureComputer.Implementation.Context newContext(int componentRef, final Set inputMetrics, final Set outputMetrics, List issues) { + componentIssuesRepository.setIssues(componentRef, issues); MeasureComputer measureComputer = new MeasureComputer() { @Override public Set getInputMetrics() { @@ -249,6 +290,7 @@ public class MeasureComputerImplementationContextTest { return null; } }; - return new MeasureComputerImplementationContext(treeRootHolder.getComponentByRef(componentRef), measureComputer, settingsRepository, measureRepository, metricRepository); + return new MeasureComputerImplementationContext(treeRootHolder.getComponentByRef(componentRef), measureComputer, + settingsRepository, measureRepository, metricRepository, componentIssuesRepository); } } diff --git a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java b/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java index 7c2fd24f6cc..1b405d50294 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java @@ -51,12 +51,7 @@ import org.sonar.core.issue.tracking.Trackable; import static java.lang.String.format; -/** - * PLUGINS MUST NOT BE USED THIS CLASS, EXCEPT FOR UNIT TESTING. - * - * @since 3.6 - */ -public class DefaultIssue implements Issue, Trackable { +public class DefaultIssue implements Issue, Trackable, org.sonar.api.ce.measure.Issue { private String key; diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/ce/measure/Issue.java b/sonar-plugin-api/src/main/java/org/sonar/api/ce/measure/Issue.java new file mode 100644 index 00000000000..e4233c2a6a5 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/ce/measure/Issue.java @@ -0,0 +1,52 @@ +/* + * 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.api.ce.measure; + +import javax.annotation.CheckForNull; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.utils.Duration; + +public interface Issue { + + String key(); + + RuleKey ruleKey(); + + /** + * Available list of status can be found in {@link org.sonar.api.issue.Issue#STATUSES} + */ + String status(); + + /** + * Available list of resolutions can be found in {@link org.sonar.api.issue.Issue#RESOLUTIONS} + */ + @CheckForNull + String resolution(); + + /** + * See constants in {@link org.sonar.api.rule.Severity}. + */ + String severity(); + + @CheckForNull + Duration debt(); + +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/ce/measure/MeasureComputer.java b/sonar-plugin-api/src/main/java/org/sonar/api/ce/measure/MeasureComputer.java index 0d1e2c866f5..32d5d90e469 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/ce/measure/MeasureComputer.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/ce/measure/MeasureComputer.java @@ -20,6 +20,7 @@ package org.sonar.api.ce.measure; +import java.util.List; import java.util.Set; import javax.annotation.CheckForNull; @@ -142,6 +143,11 @@ public interface MeasureComputer { */ void addMeasure(String metric, String value); + /** + * Return list of issues of current component. + */ + List getIssues(); + } } } diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/test/ce/measure/IssueImpl.java b/sonar-plugin-api/src/test/java/org/sonar/api/test/ce/measure/IssueImpl.java new file mode 100644 index 00000000000..0f47f4ff133 --- /dev/null +++ b/sonar-plugin-api/src/test/java/org/sonar/api/test/ce/measure/IssueImpl.java @@ -0,0 +1,157 @@ +/* + * 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.api.test.ce.measure; + +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import org.sonar.api.ce.measure.Issue; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rule.Severity; +import org.sonar.api.utils.Duration; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +public class IssueImpl implements Issue { + + private String key; + private String status; + private String resolution; + private String severity; + private RuleKey ruleKey; + private Duration debt; + + private IssueImpl(Builder builder) { + this.key = builder.key; + this.status = builder.status; + this.resolution = builder.resolution; + this.severity = builder.severity; + this.ruleKey = builder.ruleKey; + this.debt = builder.debt; + } + + @Override + public String key() { + return key; + } + + @Override + public RuleKey ruleKey() { + return ruleKey; + } + + @Override + public String status() { + return status; + } + + @Override + @CheckForNull + public String resolution() { + return resolution; + } + + @Override + public String severity() { + return severity; + } + + @Override + @CheckForNull + public Duration debt() { + return debt; + } + + public static class Builder { + private String key; + private String status; + private String resolution; + private String severity; + private RuleKey ruleKey; + private Duration debt; + + public Builder setKey(String key) { + this.key = validateKey(key); + return this; + } + + public Builder setResolution(@Nullable String resolution) { + this.resolution = validateResolution(resolution); + return this; + } + + public Builder setSeverity(String severity) { + this.severity = validateSeverity(severity); + return this; + } + + public Builder setStatus(String status) { + this.status = validateStatus(status); + return this; + } + + public Builder setRuleKey(RuleKey ruleKey) { + this.ruleKey = validateRuleKey(ruleKey); + return this; + } + + public Builder setDebt(@Nullable Duration debt) { + this.debt = debt; + return this; + } + + private static String validateKey(String key){ + checkNotNull(key, "key cannot be null"); + return key; + } + + private static RuleKey validateRuleKey(RuleKey ruleKey){ + checkNotNull(ruleKey, "ruleKey cannot be null"); + return ruleKey; + } + + private static String validateResolution(@Nullable String resolution){ + checkArgument(resolution == null || org.sonar.api.issue.Issue.RESOLUTIONS.contains(resolution), String.format("resolution '%s' is invalid", resolution)); + return resolution; + } + + private static String validateSeverity(String severity){ + checkNotNull(severity, "severity cannot be null"); + checkArgument(Severity.ALL.contains(severity), String.format("severity '%s' is invalid", severity)); + return severity; + } + + private static String validateStatus(String status){ + checkNotNull(status, "status cannot be null"); + checkArgument(org.sonar.api.issue.Issue.STATUSES.contains(status), String.format("status '%s' is invalid", status)); + return status; + } + + public Issue build(){ + validateKey(key); + validateResolution(resolution); + validateSeverity(severity); + validateStatus(status); + validateRuleKey(ruleKey); + return new IssueImpl(this); + } + } +} diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/test/ce/measure/MeasureComputerImplementationContext.java b/sonar-plugin-api/src/test/java/org/sonar/api/test/ce/measure/MeasureComputerImplementationContext.java index f8182f5483f..a49b2d6611e 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/test/ce/measure/MeasureComputerImplementationContext.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/test/ce/measure/MeasureComputerImplementationContext.java @@ -22,6 +22,7 @@ package org.sonar.api.test.ce.measure; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -29,6 +30,7 @@ import java.util.Map; import java.util.Set; import javax.annotation.CheckForNull; import org.sonar.api.ce.measure.Component; +import org.sonar.api.ce.measure.Issue; import org.sonar.api.ce.measure.Measure; import org.sonar.api.ce.measure.MeasureComputer; import org.sonar.api.ce.measure.Settings; @@ -43,6 +45,7 @@ public class MeasureComputerImplementationContext implements MeasureComputer.Imp private Map componentMeasureByMetricKey = new HashMap<>(); private Multimap childrenComponentMeasureByMetricKey = ArrayListMultimap.create(); + private List issues = new ArrayList<>(); public MeasureComputerImplementationContext(Component component, Settings settings, MeasureComputer measureComputer) { this.measureComputer = measureComputer; @@ -137,6 +140,15 @@ public class MeasureComputerImplementationContext implements MeasureComputer.Imp } } + @Override + public List getIssues() { + return issues; + } + + public void setIssues(List issues){ + this.issues = issues; + } + private void validateInputMetric(String metric) { Set allowedMetrics = new HashSet<>(); allowedMetrics.addAll(measureComputer.getInputMetrics()); diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/test/ce/measure/package-info.java b/sonar-plugin-api/src/test/java/org/sonar/api/test/ce/measure/package-info.java new file mode 100644 index 00000000000..aaa0831b888 --- /dev/null +++ b/sonar-plugin-api/src/test/java/org/sonar/api/test/ce/measure/package-info.java @@ -0,0 +1,24 @@ +/* + * 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. + */ + +@ParametersAreNonnullByDefault +package org.sonar.api.test.ce.measure; + +import javax.annotation.ParametersAreNonnullByDefault; -- 2.39.5