diff options
author | Julien Lancelot <julien.lancelot@sonarsource.com> | 2015-08-25 18:10:39 +0200 |
---|---|---|
committer | Julien Lancelot <julien.lancelot@sonarsource.com> | 2015-08-31 09:49:14 +0200 |
commit | 27ad46fd2b4f9bcd3becdf0c68b38200e6f6b5b8 (patch) | |
tree | 686012aef81b8289a4ccd9fe25a1c272d90e64aa /server | |
parent | 62def73057ecf234e2e066231364e57193b005f0 (diff) | |
download | sonarqube-27ad46fd2b4f9bcd3becdf0c68b38200e6f6b5b8.tar.gz sonarqube-27ad46fd2b4f9bcd3becdf0c68b38200e6f6b5b8.zip |
SONAR-6730 Create a repository of issues for a given component
Diffstat (limited to 'server')
8 files changed, 356 insertions, 3 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ReportComputeEngineContainerPopulator.java b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ReportComputeEngineContainerPopulator.java index 4c16f1ed92e..664a04ace82 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ReportComputeEngineContainerPopulator.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ReportComputeEngineContainerPopulator.java @@ -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 index 00000000000..51a87d20910 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/ComponentIssuesRepository.java @@ -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 index 00000000000..16dfd4c9242 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/ComponentIssuesRepositoryImpl.java @@ -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; + } +} 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 4cbcf06ed33..595b51160ee 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,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 index 00000000000..6086381ee18 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/MutableComponentIssuesRepository.java @@ -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 index 00000000000..1ec999ae1db --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/ComponentIssuesRepositoryImplTest.java @@ -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 index 00000000000..ab7995b07e3 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/ComponentIssuesRepositoryRule.java @@ -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; + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IntegrateIssuesVisitorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IntegrateIssuesVisitorTest.java index 7a810775bda..d0c04e06a3b 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IntegrateIssuesVisitorTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IntegrateIssuesVisitorTest.java @@ -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); |