]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6730 Create a repository of issues for a given component
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Tue, 25 Aug 2015 16:10:39 +0000 (18:10 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Mon, 31 Aug 2015 07:49:14 +0000 (09:49 +0200)
server/sonar-server/src/main/java/org/sonar/server/computation/container/ReportComputeEngineContainerPopulator.java
server/sonar-server/src/main/java/org/sonar/server/computation/issue/ComponentIssuesRepository.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/issue/ComponentIssuesRepositoryImpl.java [new file with mode: 0644]
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 [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/issue/ComponentIssuesRepositoryImplTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/issue/ComponentIssuesRepositoryRule.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/issue/IntegrateIssuesVisitorTest.java

index 4c16f1ed92e2cb4a2d7b74c1a4217530203a2875..664a04ace8260fa7eec7049c45f64c8afcda7772 100644 (file)
@@ -36,6 +36,7 @@ import org.sonar.server.computation.component.SettingsRepositoryImpl;
 import org.sonar.server.computation.debt.DebtModelHolderImpl;
 import org.sonar.server.computation.event.EventRepositoryImpl;
 import org.sonar.server.computation.issue.BaseIssuesLoader;
+import org.sonar.server.computation.issue.ComponentIssuesRepositoryImpl;
 import org.sonar.server.computation.issue.ComponentsWithUnprocessedIssues;
 import org.sonar.server.computation.issue.DebtAggregator;
 import org.sonar.server.computation.issue.DebtCalculator;
@@ -141,6 +142,7 @@ public final class ReportComputeEngineContainerPopulator implements ContainerPop
         IssueVisitors.class,
         IssueLifecycle.class,
         ComponentsWithUnprocessedIssues.class,
+        ComponentIssuesRepositoryImpl.class,
 
         // common rules
         CommonRuleEngineImpl.class,
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
new file mode 100644 (file)
index 0000000..51a87d2
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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.server.computation.issue;
+
+import java.util.List;
+import org.sonar.api.ce.measure.MeasureComputer;
+import org.sonar.api.issue.Issue;
+import org.sonar.server.computation.component.Component;
+
+/**
+ * This repository contains issues for only one component at a time. It's populated by {@link IntegrateIssuesVisitor} and
+ * it's mainly used by {@link org.sonar.server.computation.measure.api.MeasureComputerImplementationContext} in order for a {@link MeasureComputer}
+ * to access to the issues of a component.
+ *
+ * This repository must NEVER contains more issues than in issues from one component order to not consume to much memory.
+ */
+public interface ComponentIssuesRepository {
+
+  /**
+   * Return issues from the component
+   *
+   * @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);
+
+}
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
new file mode 100644 (file)
index 0000000..16dfd4c
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * 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.server.computation.issue;
+
+import java.util.List;
+import javax.annotation.CheckForNull;
+import org.sonar.api.issue.Issue;
+import org.sonar.server.computation.component.Component;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+public class ComponentIssuesRepositoryImpl implements MutableComponentIssuesRepository {
+
+  @CheckForNull
+  private List<Issue> issues;
+
+  @CheckForNull
+  private Component component;
+
+  @Override
+  public void setIssues(Component component, List<Issue> issues) {
+    this.issues = requireNonNull(issues, "issues cannot be null");
+    this.component = requireNonNull(component, "component cannot be null");
+  }
+
+  @Override
+  public List<Issue> 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'.",
+        this.component.getReportAttributes().getRef(), component.getReportAttributes().getRef()));
+    return issues;
+  }
+}
index 4cbcf06ed330a5ecb22cc8f3f6b709e32346488e..595b51160eed0d68846d1c203f66beaa3a1b03ac 100644 (file)
@@ -22,6 +22,7 @@ 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;
@@ -38,18 +39,20 @@ public class IntegrateIssuesVisitor extends TypeAwareVisitorAdapter {
   private final IssueCache issueCache;
   private final IssueLifecycle issueLifecycle;
   private final IssueVisitors issueVisitors;
+  private final MutableComponentIssuesRepository componentIssuesRepository;
   private final ComponentsWithUnprocessedIssues componentsWithUnprocessedIssues;
 
-  private final List<DefaultIssue> componentIssues = new ArrayList<>();
+  private final List<Issue> componentIssues = new ArrayList<>();
 
   public IntegrateIssuesVisitor(TrackerExecution tracker, IssueCache issueCache, IssueLifecycle issueLifecycle, IssueVisitors issueVisitors,
-                                ComponentsWithUnprocessedIssues componentsWithUnprocessedIssues) {
+                                ComponentsWithUnprocessedIssues componentsWithUnprocessedIssues, MutableComponentIssuesRepository componentIssuesRepository) {
     super(CrawlerDepthLimit.FILE, POST_ORDER);
     this.tracker = tracker;
     this.issueCache = issueCache;
     this.issueLifecycle = issueLifecycle;
     this.issueVisitors = issueVisitors;
     this.componentsWithUnprocessedIssues = componentsWithUnprocessedIssues;
+    this.componentIssuesRepository = componentIssuesRepository;
   }
 
   @Override
@@ -57,6 +60,7 @@ public class IntegrateIssuesVisitor extends TypeAwareVisitorAdapter {
     componentIssues.clear();
     processIssues(component);
     componentsWithUnprocessedIssues.remove(component.getUuid());
+    componentIssuesRepository.setIssues(component, componentIssues);
   }
 
   private void processIssues(Component component) {
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
new file mode 100644 (file)
index 0000000..6086381
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * 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.server.computation.issue;
+
+import java.util.List;
+import org.sonar.api.issue.Issue;
+import org.sonar.server.computation.component.Component;
+
+public interface MutableComponentIssuesRepository extends ComponentIssuesRepository {
+
+  /**
+   * Add issues of the component.
+   *    *
+   * @throws NullPointerException if {@code component} is {@code null}
+   * @throws NullPointerException if {@code issues} is {@code null}
+   */
+  void setIssues(Component component, List<Issue> issues);
+
+}
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
new file mode 100644 (file)
index 0000000..1ec999a
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * 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.server.computation.issue;
+
+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;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.server.computation.component.ReportComponent.builder;
+
+public class ComponentIssuesRepositoryImplTest {
+
+  @org.junit.Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  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");
+
+  ComponentIssuesRepositoryImpl sut = new ComponentIssuesRepositoryImpl();
+
+  @Test
+  public void get_issues() throws Exception {
+    sut.setIssues(FILE_1, Arrays.asList(DUMB_ISSUE));
+
+    assertThat(sut.getIssues(FILE_1)).containsOnly(DUMB_ISSUE);
+  }
+
+  @Test
+  public void set_empty_issues() throws Exception {
+    sut.setIssues(FILE_1, Collections.<Issue>emptyList());
+
+    assertThat(sut.getIssues(FILE_1)).isEmpty();
+  }
+
+  @Test
+  public void fail_with_NPE_when_setting_issues_with_null_component() throws Exception {
+    thrown.expect(NullPointerException.class);
+    thrown.expectMessage("component cannot be null");
+
+    sut.setIssues(null, Arrays.asList(DUMB_ISSUE));
+  }
+
+  @Test
+  public void fail_with_NPE_when_setting_issues_with_null_issues() throws Exception {
+    thrown.expect(NullPointerException.class);
+    thrown.expectMessage("issues cannot be null");
+
+    sut.setIssues(FILE_1, null);
+  }
+
+  @Test
+  public void fail_with_IAE_when_getting_issues_on_different_component() throws Exception {
+    thrown.expect(IllegalArgumentException.class);
+    thrown.expectMessage("Only issues from component '1' are available, but wanted component is '2'.");
+
+    sut.setIssues(FILE_1, Arrays.asList(DUMB_ISSUE));
+    sut.getIssues(FILE_2);
+  }
+
+  @Test
+  public void fail_with_ISE_when_getting_issues_but_issues_are_null() throws Exception {
+    thrown.expect(IllegalStateException.class);
+    thrown.expectMessage("Issues have not been initialized");
+
+    sut.getIssues(FILE_1);
+  }
+}
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
new file mode 100644 (file)
index 0000000..ab7995b
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * 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.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.server.computation.component.Component;
+import org.sonar.server.computation.component.ReportTreeRootHolder;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+public class ComponentIssuesRepositoryRule extends ExternalResource implements MutableComponentIssuesRepository, ComponentIssuesRepository {
+
+  private final ReportTreeRootHolder reportTreeRootHolder;
+
+  @CheckForNull
+  private List<Issue> issues;
+
+  @CheckForNull
+  private Component component;
+
+  public ComponentIssuesRepositoryRule(ReportTreeRootHolder reportTreeRootHolder) {
+    this.reportTreeRootHolder = reportTreeRootHolder;
+  }
+
+  @Override
+  public void setIssues(Component component, List<Issue> issues) {
+    checkNotNull(component, "component cannot be null");
+    setIssues(component.getReportAttributes().getRef(), issues);
+  }
+
+  public void setIssues(int componentRef, List<Issue> 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));
+    this.component = component;
+  }
+
+  @Override
+  public List<Issue> getIssues(Component component) {
+    checkNotNull(component, "component cannot be null");
+    return getIssues(component.getReportAttributes().getRef());
+  }
+
+  public List<Issue> 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));
+    checkArgument(component == this.component,
+      String.format("Only issues from component '%s' are available, but wanted component is '%s'.",
+        this.component.getReportAttributes().getRef(), component.getReportAttributes().getRef()));
+    return issues;
+  }
+
+}
index 7a810775bdaabaf8d184e675516b19d6ce448c26..d0c04e06a3b6f1419505e9202da129758d5580f3 100644 (file)
@@ -95,6 +95,9 @@ public class IntegrateIssuesVisitorTest {
   @Rule
   public RuleRepositoryRule ruleRepositoryRule = new RuleRepositoryRule();
 
+  @Rule
+  public ComponentIssuesRepositoryRule componentIssuesRepository = new ComponentIssuesRepositoryRule(treeRootHolder);
+
   ArgumentCaptor<DefaultIssue> defaultIssueCaptor = ArgumentCaptor.forClass(DefaultIssue.class);
 
   BaseIssuesLoader baseIssuesLoader = new BaseIssuesLoader(treeRootHolder, dbTester.getDbClient(), ruleRepositoryRule, activeRulesHolderRule);
@@ -113,7 +116,7 @@ public class IntegrateIssuesVisitorTest {
   public void setUp() throws Exception {
     treeRootHolder.setRoot(PROJECT);
     issueCache = new IssueCache(temp.newFile(), System2.INSTANCE);
-    underTest = new IntegrateIssuesVisitor(tracker, issueCache, issueLifecycle, issueVisitors, componentsWithUnprocessedIssues);
+    underTest = new IntegrateIssuesVisitor(tracker, issueCache, issueLifecycle, issueVisitors, componentsWithUnprocessedIssues, componentIssuesRepository);
   }
 
   @Test
@@ -234,6 +237,44 @@ public class IntegrateIssuesVisitorTest {
     assertThat(componentsWithUnprocessedIssues.getUuids()).isEmpty();
   }
 
+  @Test
+  public void feed_component_issues_repo() throws Exception {
+    componentsWithUnprocessedIssues.setUuids(Collections.<String>emptySet());
+
+    BatchReport.Issue reportIssue = BatchReport.Issue.newBuilder()
+      .setMsg("the message")
+      .setRuleRepository("xoo")
+      .setRuleKey("S001")
+      .setSeverity(Constants.Severity.BLOCKER)
+      .build();
+    reportReader.putIssues(FILE_REF, asList(reportIssue));
+    reportReader.putFileSourceLines(FILE_REF, "line1");
+
+    underTest.visitAny(FILE);
+
+    assertThat(componentIssuesRepository.getIssues(FILE_REF)).hasSize(1);
+  }
+
+  @Test
+  public void empty_component_issues_repo_when_no_issue() throws Exception {
+    componentsWithUnprocessedIssues.setUuids(Collections.<String>emptySet());
+
+    BatchReport.Issue reportIssue = BatchReport.Issue.newBuilder()
+      .setMsg("the message")
+      .setRuleRepository("xoo")
+      .setRuleKey("S001")
+      .setSeverity(Constants.Severity.BLOCKER)
+      .build();
+    reportReader.putIssues(FILE_REF, asList(reportIssue));
+    reportReader.putFileSourceLines(FILE_REF, "line1");
+
+    underTest.visitAny(FILE);
+    assertThat(componentIssuesRepository.getIssues(FILE_REF)).hasSize(1);
+
+    underTest.visitAny(PROJECT);
+    assertThat(componentIssuesRepository.getIssues(PROJECT)).isEmpty();
+  }
+
   private void addBaseIssue(RuleKey ruleKey) {
     ComponentDto project = ComponentTesting.newProjectDto(PROJECT_UUID).setKey(PROJECT_KEY);
     ComponentDto file = ComponentTesting.newFileDto(project, FILE_UUID).setKey(FILE_KEY);