diff options
author | Julien HENRY <julien.henry@sonarsource.com> | 2015-03-02 09:54:06 +0100 |
---|---|---|
committer | Simon Brandhof <simon.brandhof@sonarsource.com> | 2015-03-04 21:18:17 +0100 |
commit | 6108a50f766dac17b0bd45258c70ffd346d366c1 (patch) | |
tree | 4633e567282ffefcf8eb996aa9061e15cef338e5 /sonar-batch | |
parent | ae4801956efbc9eba1971d0b899267a3e8406c42 (diff) | |
download | sonarqube-6108a50f766dac17b0bd45258c70ffd346d366c1.tar.gz sonarqube-6108a50f766dac17b0bd45258c70ffd346d366c1.zip |
SONAR-5945 close issues on deleted components
Diffstat (limited to 'sonar-batch')
4 files changed, 220 insertions, 12 deletions
diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/IssueCache.java b/sonar-batch/src/main/java/org/sonar/batch/issue/IssueCache.java index f804fc62efd..f0c1cfb1fbd 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/issue/IssueCache.java +++ b/sonar-batch/src/main/java/org/sonar/batch/issue/IssueCache.java @@ -24,6 +24,8 @@ import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.batch.index.Cache; import org.sonar.batch.index.Caches; +import java.util.Collection; + /** * Shared issues among all project modules */ @@ -44,6 +46,10 @@ public class IssueCache implements BatchComponent { return cache.values(); } + public Collection<Object> componentKeys() { + return cache.keySet(); + } + public IssueCache put(DefaultIssue issue) { cache.put(issue.componentKey(), issue.key(), issue); return this; diff --git a/sonar-batch/src/main/java/org/sonar/batch/report/ComponentsPublisher.java b/sonar-batch/src/main/java/org/sonar/batch/report/ComponentsPublisher.java index 0a7d97d9ad6..7850f6e2db4 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/report/ComponentsPublisher.java +++ b/sonar-batch/src/main/java/org/sonar/batch/report/ComponentsPublisher.java @@ -21,7 +21,6 @@ package org.sonar.batch.report; import org.sonar.api.batch.bootstrap.ProjectReactor; import org.sonar.api.resources.Language; -import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; import org.sonar.api.resources.ResourceUtils; import org.sonar.batch.index.BatchResource; @@ -48,15 +47,6 @@ public class ComponentsPublisher implements ReportPublisher { @Override public void publish(BatchOutputWriter writer) { BatchResource rootProject = resourceCache.get(reactor.getRoot().getKeyWithBranch()); - BatchReport.Metadata.Builder builder = BatchReport.Metadata.newBuilder() - .setAnalysisDate(((Project) rootProject.resource()).getAnalysisDate().getTime()) - .setProjectKey(((Project) rootProject.resource()).key()) - .setRootComponentRef(rootProject.batchId()); - Integer sid = rootProject.snapshotId(); - if (sid != null) { - builder.setSnapshotId(sid); - } - writer.writeMetadata(builder.build()); recursiveWriteComponent(rootProject, writer); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/report/IssuesPublisher.java b/sonar-batch/src/main/java/org/sonar/batch/report/IssuesPublisher.java index 8be01240274..fd7bd13b814 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/report/IssuesPublisher.java +++ b/sonar-batch/src/main/java/org/sonar/batch/report/IssuesPublisher.java @@ -21,8 +21,10 @@ package org.sonar.batch.report; import com.google.common.base.Function; import com.google.common.collect.Iterables; +import org.sonar.api.batch.bootstrap.ProjectReactor; import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.api.issue.internal.FieldDiffs; +import org.sonar.api.resources.Project; import org.sonar.api.utils.KeyValueFormat; import org.sonar.batch.index.BatchResource; import org.sonar.batch.index.ResourceCache; @@ -33,29 +35,73 @@ import org.sonar.batch.protocol.output.BatchReport; import javax.annotation.Nullable; +import java.util.Collection; import java.util.Date; +import java.util.Iterator; public class IssuesPublisher implements ReportPublisher { private final ResourceCache resourceCache; private final IssueCache issueCache; + private final ProjectReactor reactor; - public IssuesPublisher(ResourceCache resourceCache, IssueCache issueCache) { + public IssuesPublisher(ProjectReactor reactor, ResourceCache resourceCache, IssueCache issueCache) { + this.reactor = reactor; this.resourceCache = resourceCache; this.issueCache = issueCache; } @Override public void publish(BatchOutputWriter writer) { + Collection<Object> deletedComponentKeys = issueCache.componentKeys(); for (BatchResource resource : resourceCache.all()) { - Iterable<DefaultIssue> issues = issueCache.byComponent(resource.resource().getEffectiveKey()); + String componentKey = resource.resource().getEffectiveKey(); + Iterable<DefaultIssue> issues = issueCache.byComponent(componentKey); writer.writeComponentIssues(resource.batchId(), Iterables.transform(issues, new Function<DefaultIssue, BatchReport.Issue>() { @Override public BatchReport.Issue apply(DefaultIssue input) { return toReportIssue(input); } })); + deletedComponentKeys.remove(componentKey); } + + int count = exportIssuesOfDeletedComponents(deletedComponentKeys, writer); + + exportMetadata(writer, count); + } + + private void exportMetadata(BatchOutputWriter writer, int count) { + BatchResource rootProject = resourceCache.get(reactor.getRoot().getKeyWithBranch()); + BatchReport.Metadata.Builder builder = BatchReport.Metadata.newBuilder() + .setAnalysisDate(((Project) rootProject.resource()).getAnalysisDate().getTime()) + .setProjectKey(((Project) rootProject.resource()).key()) + .setRootComponentRef(rootProject.batchId()) + .setDeletedComponentsCount(count); + Integer sid = rootProject.snapshotId(); + if (sid != null) { + builder.setSnapshotId(sid); + } + writer.writeMetadata(builder.build()); + } + + private int exportIssuesOfDeletedComponents(Collection<Object> deletedComponentKeys, BatchOutputWriter writer) { + int deletedComponentCount = 0; + for (Object componentKey : deletedComponentKeys) { + deletedComponentCount++; + Iterable<DefaultIssue> issues = issueCache.byComponent(componentKey.toString()); + Iterator<DefaultIssue> iterator = issues.iterator(); + if (iterator.hasNext()) { + String componentUuid = iterator.next().componentUuid(); + writer.writeDeletedComponentIssues(deletedComponentCount, componentUuid, Iterables.transform(issues, new Function<DefaultIssue, BatchReport.Issue>() { + @Override + public BatchReport.Issue apply(DefaultIssue input) { + return toReportIssue(input); + } + })); + } + } + return deletedComponentCount; } private BatchReport.Issue toReportIssue(DefaultIssue issue) { diff --git a/sonar-batch/src/test/java/org/sonar/batch/report/IssuesPublisherTest.java b/sonar-batch/src/test/java/org/sonar/batch/report/IssuesPublisherTest.java new file mode 100644 index 00000000000..2674e1ddc78 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/report/IssuesPublisherTest.java @@ -0,0 +1,166 @@ +/* + * 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.batch.report; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.batch.bootstrap.ProjectReactor; +import org.sonar.api.database.model.Snapshot; +import org.sonar.api.issue.internal.DefaultIssue; +import org.sonar.api.issue.internal.FieldDiffs; +import org.sonar.api.resources.Project; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.utils.Duration; +import org.sonar.batch.index.ResourceCache; +import org.sonar.batch.issue.IssueCache; +import org.sonar.batch.protocol.output.BatchOutputWriter; +import org.sonar.batch.protocol.output.BatchReport.Metadata; +import org.sonar.batch.protocol.output.BatchReportReader; + +import java.io.File; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class IssuesPublisherTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + private IssueCache issueCache; + private IssuesPublisher publisher; + + @Before + public void prepare() { + ProjectDefinition root = ProjectDefinition.create().setKey("foo"); + Project p = new Project("foo").setAnalysisDate(new Date(1234567L)); + ResourceCache resourceCache = new ResourceCache(); + org.sonar.api.resources.Resource sampleFile = org.sonar.api.resources.File.create("src/Foo.php").setEffectiveKey("foo:src/Foo.php"); + resourceCache.add(p, null).setSnapshot(new Snapshot().setId(2)); + resourceCache.add(sampleFile, null); + issueCache = mock(IssueCache.class); + when(issueCache.byComponent(anyString())).thenReturn(Collections.<DefaultIssue>emptyList()); + publisher = new IssuesPublisher(new ProjectReactor(root), resourceCache, issueCache); + } + + @Test + public void publishIssuesAndMetadata() throws Exception { + + DefaultIssue issue1 = new DefaultIssue(); + issue1.setKey("uuid"); + issue1.setSeverity("MAJOR"); + issue1.setRuleKey(RuleKey.of("repo", "rule")); + DefaultIssue issue2 = new DefaultIssue(); + issue2.setKey("uuid2"); + issue2.setSeverity("MAJOR"); + issue2.setRuleKey(RuleKey.of("repo", "rule")); + issue2.setLine(2); + issue2.setMessage("msg"); + issue2.setEffortToFix(2d); + issue2.setDebt(Duration.create(2)); + issue2.setResolution("FIXED"); + issue2.setStatus("RESOLVED"); + issue2.setChecksum("checksum"); + issue2.setReporter("reporter"); + issue2.setAssignee("assignee"); + issue2.setActionPlanKey("action"); + issue2.setAuthorLogin("author"); + issue2.setCurrentChange(new FieldDiffs().setUserLogin("foo")); + issue2.setCreationDate(new Date()); + issue2.setUpdateDate(new Date()); + issue2.setCloseDate(new Date()); + issue2.setSelectedAt(1234L); + when(issueCache.byComponent("foo:src/Foo.php")).thenReturn(Arrays.asList(issue1, issue2)); + + File outputDir = temp.newFolder(); + BatchOutputWriter writer = new BatchOutputWriter(outputDir); + + publisher.publish(writer); + + BatchReportReader reader = new BatchReportReader(outputDir); + Metadata metadata = reader.readMetadata(); + assertThat(metadata.getAnalysisDate()).isEqualTo(1234567L); + assertThat(metadata.getProjectKey()).isEqualTo("foo"); + assertThat(metadata.getDeletedComponentsCount()).isEqualTo(0); + assertThat(metadata.getSnapshotId()).isEqualTo(2); + + assertThat(reader.readComponentIssues(1)).hasSize(0); + assertThat(reader.readComponentIssues(2)).hasSize(2); + + } + + @Test + public void publishIssuesOfDeletedComponents() throws Exception { + + DefaultIssue issue1 = new DefaultIssue(); + issue1.setKey("uuid"); + issue1.setComponentUuid("deletedUuid"); + issue1.setSeverity("MAJOR"); + issue1.setRuleKey(RuleKey.of("repo", "rule")); + DefaultIssue issue2 = new DefaultIssue(); + issue2.setKey("uuid2"); + issue2.setComponentUuid("deletedUuid"); + issue2.setSeverity("MAJOR"); + issue2.setRuleKey(RuleKey.of("repo", "rule")); + issue2.setLine(2); + issue2.setMessage("msg"); + issue2.setEffortToFix(2d); + issue2.setDebt(Duration.create(2)); + issue2.setResolution("FIXED"); + issue2.setStatus("RESOLVED"); + issue2.setChecksum("checksum"); + issue2.setReporter("reporter"); + issue2.setAssignee("assignee"); + issue2.setActionPlanKey("action"); + issue2.setAuthorLogin("author"); + issue2.setCurrentChange(new FieldDiffs().setUserLogin("foo")); + issue2.setCreationDate(new Date()); + issue2.setUpdateDate(new Date()); + issue2.setCloseDate(new Date()); + issue2.setSelectedAt(1234L); + + when(issueCache.byComponent("foo:deleted.php")).thenReturn(Arrays.asList(issue1, issue2)); + + when(issueCache.componentKeys()).thenReturn(Arrays.<Object>asList("foo:deleted.php")); + + File outputDir = temp.newFolder(); + BatchOutputWriter writer = new BatchOutputWriter(outputDir); + + publisher.publish(writer); + + BatchReportReader reader = new BatchReportReader(outputDir); + Metadata metadata = reader.readMetadata(); + assertThat(metadata.getDeletedComponentsCount()).isEqualTo(1); + + assertThat(reader.readComponentIssues(1)).hasSize(0); + assertThat(reader.readComponentIssues(2)).hasSize(0); + assertThat(reader.readDeletedComponentIssues(1).getComponentUuid()).isEqualTo("deletedUuid"); + assertThat(reader.readDeletedComponentIssues(1).getListList()).hasSize(2); + + } +} |