]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6730 Add issues in Measure API
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 26 Aug 2015 10:25:31 +0000 (12:25 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Mon, 31 Aug 2015 07:49:14 +0000 (09:49 +0200)
16 files changed:
server/sonar-server/src/main/java/org/sonar/server/computation/issue/ComponentIssuesRepository.java
server/sonar-server/src/main/java/org/sonar/server/computation/issue/ComponentIssuesRepositoryImpl.java
server/sonar-server/src/main/java/org/sonar/server/computation/issue/IntegrateIssuesVisitor.java
server/sonar-server/src/main/java/org/sonar/server/computation/issue/MutableComponentIssuesRepository.java
server/sonar-server/src/main/java/org/sonar/server/computation/measure/MeasureComputersVisitor.java
server/sonar-server/src/main/java/org/sonar/server/computation/measure/api/MeasureComputerImplementationContext.java
server/sonar-server/src/test/java/org/sonar/server/computation/issue/ComponentIssuesRepositoryImplTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/issue/ComponentIssuesRepositoryRule.java
server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureComputersVisitorTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/measure/api/MeasureComputerImplementationContextTest.java
sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java
sonar-plugin-api/src/main/java/org/sonar/api/ce/measure/Issue.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/ce/measure/MeasureComputer.java
sonar-plugin-api/src/test/java/org/sonar/api/test/ce/measure/IssueImpl.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/test/ce/measure/MeasureComputerImplementationContext.java
sonar-plugin-api/src/test/java/org/sonar/api/test/ce/measure/package-info.java [new file with mode: 0644]

index 51a87d20910ddc1777b4b422263392cd9f8e6a84..fd8900ce56a8153deb1773a969e9c2a5ac5f79d8 100644 (file)
@@ -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<Issue> getIssues(Component component);
+  List<DefaultIssue> getIssues(Component component);
 
 }
index 16dfd4c9242955f97bbf29084e915c82bf1db669..b4248320df8994b3a5b70a644f98ecfb2ed0adfa 100644 (file)
@@ -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<Issue> issues;
+  private List<DefaultIssue> issues;
 
   @CheckForNull
   private Component component;
 
   @Override
-  public void setIssues(Component component, List<Issue> issues) {
+  public void setIssues(Component component, List<DefaultIssue> issues) {
     this.issues = requireNonNull(issues, "issues cannot be null");
     this.component = requireNonNull(component, "component cannot be null");
   }
 
   @Override
-  public List<Issue> getIssues(Component component) {
+  public List<DefaultIssue> 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'.",
index 595b51160eed0d68846d1c203f66beaa3a1b03ac..db9cfb7952971cd505b906049afef0cb35a9bfbc 100644 (file)
@@ -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<Issue> componentIssues = new ArrayList<>();
+  private final List<DefaultIssue> componentIssues = new ArrayList<>();
 
   public IntegrateIssuesVisitor(TrackerExecution tracker, IssueCache issueCache, IssueLifecycle issueLifecycle, IssueVisitors issueVisitors,
                                 ComponentsWithUnprocessedIssues componentsWithUnprocessedIssues, MutableComponentIssuesRepository componentIssuesRepository) {
index 6086381ee185cbc44e06b1ec16e3929bfd430ccf..a860d460a393479f3205d9e1fcf240caa89e4aa3 100644 (file)
@@ -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<Issue> issues);
+  void setIssues(Component component, List<DefaultIssue> issues);
 
 }
index b47f793a702ab5fe1789ec00bf5260d7941c5e39..43ad33fdf0542935c2074ebe1dceb4e166b68b47 100644 (file)
@@ -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);
     }
   }
index e8f74f449a92e6f30eab94c0c7f76d639d9ecb4e..44516db373a0a62a602debace43de539ca9ae2ec 100644 (file)
@@ -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<DefaultIssue> componentIssues;
 
   private final Set<String> 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<String> allowedMetric(MeasureComputer measureComputer){
+  private static Set<String> allowedMetric(MeasureComputer measureComputer) {
     Set<String> 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<? extends Issue> getIssues() {
+    return componentIssues;
+  }
+
   private class ComponentToMeasure implements Function<org.sonar.server.computation.component.Component, Optional<org.sonar.server.computation.measure.Measure>> {
 
     private final Metric metric;
@@ -173,7 +183,6 @@ public class MeasureComputerImplementationContext implements MeasureComputer.Imp
       this.metric = metric;
     }
 
-    @Nullable
     @Override
     public Optional<org.sonar.server.computation.measure.Measure> 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;
     }
   }
+
 }
index 1ec999ae1dbc396b97d1fb540d442024ede7a130..4188f835808c06038e9c8851769cad962cc588d0 100644 (file)
@@ -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.<Issue>emptyList());
+    sut.setIssues(FILE_1, Collections.<DefaultIssue>emptyList());
 
     assertThat(sut.getIssues(FILE_1)).isEmpty();
   }
index ab7995b07e30fa8159a9827310d379c4cb757f51..dd1950d026feb45ade80707657a5138bc9a775cb 100644 (file)
@@ -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<Issue> issues;
+  private List<DefaultIssue> issues;
 
   @CheckForNull
   private Component component;
@@ -47,12 +47,12 @@ public class ComponentIssuesRepositoryRule extends ExternalResource implements M
   }
 
   @Override
-  public void setIssues(Component component, List<Issue> issues) {
+  public void setIssues(Component component, List<DefaultIssue> issues) {
     checkNotNull(component, "component cannot be null");
     setIssues(component.getReportAttributes().getRef(), issues);
   }
 
-  public void setIssues(int componentRef, List<Issue> issues) {
+  public void setIssues(int componentRef, List<DefaultIssue> 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<Issue> getIssues(Component component) {
+  public List<DefaultIssue> getIssues(Component component) {
     checkNotNull(component, "component cannot be null");
     return getIssues(component.getReportAttributes().getRef());
   }
 
-  public List<Issue> getIssues(int componentRef) {
+  public List<DefaultIssue> 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));
index c43e70139ec41b5bdb4e6e0c6a23242bc3834a66..9aa1cac9d2e23f55e0bbfef03714bfd70b5b035a 100644 (file)
@@ -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.<ComponentVisitor>asList(new MeasureComputersVisitor(metricRepository, measureRepository, null, measureComputersHolder)));
+    VisitorsCrawler visitorsCrawler = new VisitorsCrawler(Arrays.<ComponentVisitor>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.<MeasureComputer>emptyList());
-    VisitorsCrawler visitorsCrawler = new VisitorsCrawler(Arrays.<ComponentVisitor>asList(new MeasureComputersVisitor(metricRepository, measureRepository, null, measureComputersHolder)));
+    VisitorsCrawler visitorsCrawler = new VisitorsCrawler(Arrays.<ComponentVisitor>asList(new MeasureComputersVisitor(metricRepository, measureRepository, null, measureComputersHolder, componentIssuesRepository)));
     visitorsCrawler.visit(ROOT);
 
     assertThat(toEntries(measureRepository.getAddedRawMeasures(FILE_1_REF))).isEmpty();
index 3bb8df5ba4391c3a461c9e3002bb460921e3aced..aa8beac7e557a1459cd274cac098d29d64499c93 100644 (file)
@@ -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> measure =  measureRepository.getAddedRawMeasure(PROJECT_REF, INT_METRIC_KEY);
+    Optional<Measure> 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> measure =  measureRepository.getAddedRawMeasure(PROJECT_REF, DOUBLE_METRIC_KEY);
+    Optional<Measure> 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> measure =  measureRepository.getAddedRawMeasure(PROJECT_REF, LONG_METRIC_KEY);
+    Optional<Measure> 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> measure =  measureRepository.getAddedRawMeasure(PROJECT_REF, STRING_METRIC_KEY);
+    Optional<Measure> 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.<String>emptySet(), Collections.<String>emptySet());
   }
 
+  private MeasureComputer.Implementation.Context newContext(int componentRef, List<DefaultIssue> issues) {
+    return newContext(componentRef, Collections.<String>emptySet(), Collections.<String>emptySet(), issues);
+  }
+
   private MeasureComputer.Implementation.Context newContext(int componentRef, final Set<String> inputMetrics, final Set<String> outputMetrics) {
+    return newContext(componentRef, inputMetrics, outputMetrics, Collections.<DefaultIssue>emptyList());
+  }
+
+  private MeasureComputer.Implementation.Context newContext(int componentRef, final Set<String> inputMetrics, final Set<String> outputMetrics, List<DefaultIssue> issues) {
+    componentIssuesRepository.setIssues(componentRef, issues);
     MeasureComputer measureComputer = new MeasureComputer() {
       @Override
       public Set<String> 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);
   }
 }
index 7c2fd24f6cc761d17cde7db13d3dadd4ba720895..1b405d5029443834a4ae2019b054340047c9456d 100644 (file)
@@ -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 (file)
index 0000000..e4233c2
--- /dev/null
@@ -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();
+
+}
index 0d1e2c866f5d5ef683d29eef43ccf4d445ddde91..32d5d90e469a5e02268e14b94c2783713ae18c35 100644 (file)
@@ -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<? extends Issue> 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 (file)
index 0000000..0f47f4f
--- /dev/null
@@ -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);
+    }
+  }
+}
index f8182f5483f2eb75cf5ec53d9c0974c36f594347..a49b2d6611eb6eb035c896ee0bac3f2ab7ee6da1 100644 (file)
@@ -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<String, Measure> componentMeasureByMetricKey = new HashMap<>();
   private Multimap<String, Measure> childrenComponentMeasureByMetricKey = ArrayListMultimap.create();
+  private List<Issue> 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<Issue> getIssues() {
+    return issues;
+  }
+
+  public void setIssues(List<Issue> issues){
+    this.issues = issues;
+  }
+
   private void validateInputMetric(String metric) {
     Set<String> 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 (file)
index 0000000..aaa0831
--- /dev/null
@@ -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;