Browse Source

Use protocol buffers format for analysis reports

tags/5.1-RC1
Simon Brandhof 9 years ago
parent
commit
35f1487144
46 changed files with 7800 additions and 1733 deletions
  1. 5
    0
      pom.xml
  2. 0
    152
      server/sonar-server/src/main/java/org/sonar/server/computation/AnalysisReportService.java
  3. 0
    1
      server/sonar-server/src/main/java/org/sonar/server/computation/ComputationComponents.java
  4. 14
    39
      server/sonar-server/src/main/java/org/sonar/server/computation/ComputationContext.java
  5. 4
    3
      server/sonar-server/src/main/java/org/sonar/server/computation/ComputationService.java
  6. 53
    7
      server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueComputation.java
  7. 1
    1
      server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java
  8. 20
    6
      server/sonar-server/src/main/java/org/sonar/server/computation/step/ParseReportStep.java
  9. 0
    135
      server/sonar-server/src/test/java/org/sonar/server/computation/AnalysisReportServiceTest.java
  10. 61
    82
      server/sonar-server/src/test/java/org/sonar/server/computation/ComputationThreadLauncherTest.java
  11. 40
    34
      server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueComputationTest.java
  12. 3
    3
      server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputationStepsTest.java
  13. 0
    64
      server/sonar-server/src/test/java/org/sonar/server/computation/step/DigestReportStepTest.java
  14. 2
    6
      server/sonar-server/src/test/java/org/sonar/server/computation/step/IndexComponentsStepTest.java
  15. 2
    2
      server/sonar-server/src/test/java/org/sonar/server/computation/step/PurgeDatastoresStepTest.java
  16. 4
    7
      server/sonar-server/src/test/java/org/sonar/server/computation/step/SwitchSnapshotStepTest.java
  17. 6
    0
      sonar-batch-protocol/compile_protobuf.sh
  18. 22
    0
      sonar-batch-protocol/pom.xml
  19. 269
    0
      sonar-batch-protocol/src/main/gen-java/org/sonar/batch/protocol/Constants.java
  20. 6470
    0
      sonar-batch-protocol/src/main/gen-java/org/sonar/batch/protocol/output/BatchOutput.java
  21. 54
    0
      sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/ProtobufUtil.java
  22. 62
    0
      sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/BatchOutputReader.java
  23. 65
    0
      sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/BatchOutputWriter.java
  24. 22
    23
      sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/FileStructure.java
  25. 0
    170
      sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/ReportHelper.java
  26. 0
    154
      sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/component/ReportComponent.java
  27. 0
    298
      sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/issue/ReportIssue.java
  28. 0
    24
      sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/issue/package-info.java
  29. 95
    0
      sonar-batch-protocol/src/main/protobuf/batch_output.proto
  30. 39
    0
      sonar-batch-protocol/src/main/protobuf/constants.proto
  31. 13
    3
      sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/ProtobufUtilTest.java
  32. 77
    0
      sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/output/BatchOutputReaderTest.java
  33. 120
    0
      sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/output/BatchOutputWriterTest.java
  34. 74
    0
      sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/output/FileStructureTest.java
  35. 0
    55
      sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/output/ReportHelperTest.java
  36. 0
    98
      sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/output/component/ReportComponentsTest.java
  37. 0
    86
      sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/output/issue/ReportIssueTest.java
  38. 3
    3
      sonar-batch/src/main/java/org/sonar/batch/index/BatchResource.java
  39. 53
    37
      sonar-batch/src/main/java/org/sonar/batch/report/ComponentsPublisher.java
  40. 80
    37
      sonar-batch/src/main/java/org/sonar/batch/report/IssuesPublisher.java
  41. 6
    6
      sonar-batch/src/main/java/org/sonar/batch/report/PublishReportJob.java
  42. 5
    4
      sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisher.java
  43. 41
    109
      sonar-batch/src/test/java/org/sonar/batch/report/ComponentsPublisherTest.java
  44. 0
    59
      sonar-batch/src/test/resources/org/sonar/batch/report/ComponentsPublisherTest/expected.json
  45. 0
    21
      sonar-batch/src/test/resources/org/sonar/batch/report/ComponentsPublisherTest/testComponentPublisher_containing_file_without_language.json
  46. 15
    4
      sonar-plugin-api/src/test/java/org/sonar/api/utils/ZipUtilsTest.java

+ 5
- 0
pom.xml View File

@@ -1083,6 +1083,11 @@
<artifactId>freemarker</artifactId>
<version>2.3.20</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>2.6.1</version>
</dependency>

<!-- tomcat -->
<dependency>

+ 0
- 152
server/sonar-server/src/main/java/org/sonar/server/computation/AnalysisReportService.java View File

@@ -1,152 +0,0 @@
/*
* 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;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import org.apache.commons.io.IOUtils;
import org.sonar.api.issue.internal.DefaultIssue;
import org.sonar.api.issue.internal.FieldDiffs;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.utils.Duration;
import org.sonar.api.utils.KeyValueFormat;
import org.sonar.batch.protocol.output.ReportHelper;
import org.sonar.batch.protocol.output.component.ReportComponent;
import org.sonar.batch.protocol.output.component.ReportComponents;
import org.sonar.batch.protocol.output.issue.ReportIssue;
import org.sonar.server.computation.issue.IssueComputation;

import javax.annotation.Nullable;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;

public class AnalysisReportService {

private final IssueComputation issueComputation;

public AnalysisReportService(IssueComputation issueComputation) {
this.issueComputation = issueComputation;
}

public void digest(ComputationContext context) {
initComponents(context);
parseReport(context);
}

@VisibleForTesting
void initComponents(ComputationContext context) {
File file = new File(context.getReportDirectory(), "components.json");

try (InputStream resourcesStream = new FileInputStream(file)) {
String json = IOUtils.toString(resourcesStream);
ReportComponents reportComponents = ReportComponents.fromJson(json);
context.addResources(reportComponents);
} catch (IOException e) {
throw new IllegalStateException("Failed to read issues", e);
}
}

private void parseReport(ComputationContext context) {
ReportHelper helper = ReportHelper.create(context.getReportDirectory());
ReportComponent root = helper.getComponents().root();
browseComponent(context, helper, root);
issueComputation.afterReportProcessing();
}

private void browseComponent(ComputationContext context, ReportHelper helper, ReportComponent component) {
Iterable<ReportIssue> reportIssues = helper.getIssues(component.batchId());
browseComponentIssues(context, component, reportIssues);
for (ReportComponent child : component.children()) {
browseComponent(context, helper, child);
}
}

private void browseComponentIssues(final ComputationContext context, ReportComponent component, Iterable<ReportIssue> reportIssues) {
issueComputation.processComponentIssues(component.uuid(), Iterables.transform(reportIssues, new Function<ReportIssue, DefaultIssue>() {
@Override
public DefaultIssue apply(ReportIssue input) {
return toIssue(context, input);
}
}));
}

private DefaultIssue toIssue(ComputationContext context, ReportIssue issue) {
DefaultIssue defaultIssue = new DefaultIssue();
defaultIssue.setKey(issue.key());
ReportComponent component = context.getComponentByBatchId(issue.componentBatchId());
setComponent(defaultIssue, component);
defaultIssue.setRuleKey(RuleKey.of(issue.ruleRepo(), issue.ruleKey()));
defaultIssue.setSeverity(issue.severity());
defaultIssue.setManualSeverity(issue.isManualSeverity());
defaultIssue.setMessage(issue.message());
defaultIssue.setLine(issue.line());
defaultIssue.setProjectUuid(context.getProject().uuid());
defaultIssue.setProjectKey(context.getProject().key());
defaultIssue.setEffortToFix(issue.effortToFix());
setDebt(defaultIssue, issue.debt());
setFieldDiffs(defaultIssue, issue.diffFields(), context.getAnalysisDate());
defaultIssue.setStatus(issue.status());
defaultIssue.setTags(issue.tags());
defaultIssue.setResolution(issue.resolution());
defaultIssue.setReporter(issue.reporter());
defaultIssue.setAssignee(issue.assignee());
defaultIssue.setChecksum(issue.checksum());
defaultIssue.setAttributes(KeyValueFormat.parse(issue.issueAttributes()));
defaultIssue.setAuthorLogin(issue.authorLogin());
defaultIssue.setActionPlanKey(issue.actionPlanKey());
defaultIssue.setCreationDate(issue.creationDate());
defaultIssue.setUpdateDate(issue.updateDate());
defaultIssue.setCloseDate(issue.closeDate());
defaultIssue.setChanged(issue.isChanged());
defaultIssue.setNew(issue.isNew());
defaultIssue.setSelectedAt(issue.selectedAt());
defaultIssue.setSendNotifications(issue.mustSendNotifications());
return defaultIssue;
}

private DefaultIssue setFieldDiffs(DefaultIssue issue, String diffFields, Date analysisDate) {
FieldDiffs fieldDiffs = FieldDiffs.parse(diffFields);
fieldDiffs.setCreationDate(analysisDate);
issue.setCurrentChange(fieldDiffs);

return issue;
}

private DefaultIssue setComponent(DefaultIssue issue, @Nullable ReportComponent component) {
if (component != null) {
issue.setComponentUuid(component.uuid());
}
return issue;
}

private DefaultIssue setDebt(DefaultIssue issue, @Nullable Long debt) {
if (debt != null) {
issue.setDebt(Duration.create(debt));
}

return issue;
}
}

+ 0
- 1
server/sonar-server/src/main/java/org/sonar/server/computation/ComputationComponents.java View File

@@ -41,7 +41,6 @@ public class ComputationComponents {
return Arrays.asList(
ComputationService.class,
ComputationSteps.class,
AnalysisReportService.class,

// issues
ScmAccountCacheLoader.class,

+ 14
- 39
server/sonar-server/src/main/java/org/sonar/server/computation/ComputationContext.java View File

@@ -20,31 +20,27 @@

package org.sonar.server.computation;

import com.google.common.annotations.VisibleForTesting;
import org.sonar.batch.protocol.output.component.ReportComponent;
import org.sonar.batch.protocol.output.component.ReportComponents;
import org.sonar.batch.protocol.output.BatchOutputReader;
import org.sonar.core.component.ComponentDto;
import org.sonar.core.computation.db.AnalysisReportDto;

import javax.annotation.CheckForNull;

import java.io.File;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class ComputationContext {

private final AnalysisReportDto reportDto;
private final ComponentDto project;
private final File reportDirectory;
private Map<Long, ReportComponent> components = new HashMap<>();
private Date analysisDate;
private final BatchOutputReader reportReader;

/**
* Cache of analysis date as it can be accessed several times
*/
private Date analysisDate = null;

public ComputationContext(AnalysisReportDto reportDto, ComponentDto project, File reportDir) {
public ComputationContext(AnalysisReportDto reportDto, ComponentDto project, BatchOutputReader reportReader) {
this.reportDto = reportDto;
this.project = project;
this.reportDirectory = reportDir;
this.reportReader = reportReader;
}

public AnalysisReportDto getReportDto() {
@@ -55,35 +51,14 @@ public class ComputationContext {
return project;
}

public File getReportDirectory() {
return reportDirectory;
}

public void addResources(ReportComponents reportComponents) {
analysisDate = reportComponents.analysisDate();
addResource(reportComponents.root());
public BatchOutputReader getReportReader() {
return reportReader;
}

@CheckForNull
public ReportComponent getComponentByBatchId(Long batchId) {
return components.get(batchId);
}

@VisibleForTesting
Map<Long, ReportComponent> getComponents() {
return components;
}

private void addResource(ReportComponent resource) {
this.components.put(resource.batchId(), resource);
for (ReportComponent childResource : resource.children()) {
addResource(childResource);
}
}

@CheckForNull
public Date getAnalysisDate() {
if (analysisDate == null) {
analysisDate = new Date(reportReader.readMetadata().getAnalysisDate());
}
return analysisDate;
}

}

+ 4
- 3
server/sonar-server/src/main/java/org/sonar/server/computation/ComputationService.java View File

@@ -28,6 +28,7 @@ import org.sonar.api.ServerComponent;
import org.sonar.api.utils.System2;
import org.sonar.api.utils.TempFolder;
import org.sonar.api.utils.TimeProfiler;
import org.sonar.batch.protocol.output.BatchOutputReader;
import org.sonar.core.activity.Activity;
import org.sonar.core.component.ComponentDto;
import org.sonar.core.computation.db.AnalysisReportDto;
@@ -64,8 +65,8 @@ public class ComputationService implements ServerComponent {
ComponentDto project = loadProject(report);
File reportDir = tempFolder.newDir();
try {
ComputationContext context = new ComputationContext(report, project, reportDir);
decompressReport(report, reportDir);
ComputationContext context = new ComputationContext(report, project, new BatchOutputReader(reportDir));
for (ComputationStep step : steps.orderedSteps()) {
TimeProfiler stepProfiler = new TimeProfiler(LOG).start(step.getDescription());
step.execute(context);
@@ -112,7 +113,7 @@ public class ComputationService implements ServerComponent {
} finally {
MyBatis.closeQuietly(session);
}
long stoptTime = System.currentTimeMillis();
LOG.info("Analysis report loaded and uncompressed in " + (stoptTime-startTime) + "ms (project=" + report.getProjectKey() +")");
long stopTime = System.currentTimeMillis();
LOG.info("Analysis report loaded and uncompressed in " + (stopTime - startTime) + "ms (project=" + report.getProjectKey() + ")");
}
}

+ 53
- 7
server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueComputation.java View File

@@ -21,40 +21,86 @@ package org.sonar.server.computation.issue;

import com.google.common.collect.Sets;
import org.sonar.api.issue.internal.DefaultIssue;
import org.sonar.api.issue.internal.FieldDiffs;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.utils.Duration;
import org.sonar.api.utils.KeyValueFormat;
import org.sonar.batch.protocol.output.BatchOutput;
import org.sonar.core.rule.RuleDto;
import org.sonar.server.computation.ComputationContext;
import org.sonar.server.util.cache.DiskCache;

import java.util.Date;

public class IssueComputation {

private final RuleCache ruleCache;
private final ScmAccountCache scmAccountCache;
private final SourceLinesCache linesCache;
private final DiskCache<DefaultIssue>.DiskAppender finalIssuesAppender;
private final DiskCache<DefaultIssue>.DiskAppender diskIssuesAppender;

public IssueComputation(RuleCache ruleCache, SourceLinesCache linesCache, ScmAccountCache scmAccountCache,
IssueCache issueCache) {
IssueCache issueCache) {
this.ruleCache = ruleCache;
this.linesCache = linesCache;
this.scmAccountCache = scmAccountCache;
this.finalIssuesAppender = issueCache.newAppender();
this.diskIssuesAppender = issueCache.newAppender();
}

public void processComponentIssues(String componentUuid, Iterable<DefaultIssue> issues) {
public void processComponentIssues(ComputationContext context, String componentUuid, Iterable<BatchOutput.ReportIssue> issues) {
linesCache.init(componentUuid);
for (DefaultIssue issue : issues) {
for (BatchOutput.ReportIssue reportIssue : issues) {
DefaultIssue issue = toDefaultIssue(context, componentUuid, reportIssue);
if (issue.isNew()) {
guessAuthor(issue);
autoAssign(issue);
copyRuleTags(issue);
// TODO execute extension points
}
finalIssuesAppender.append(issue);
diskIssuesAppender.append(issue);
}
linesCache.clear();
}

private DefaultIssue toDefaultIssue(ComputationContext context, String componentUuid, BatchOutput.ReportIssue issue) {
DefaultIssue target = new DefaultIssue();
target.setKey(issue.getUuid());
target.setComponentUuid(componentUuid);
target.setRuleKey(RuleKey.of(issue.getRuleRepository(), issue.getRuleKey()));
target.setSeverity(issue.getSeverity().name());
target.setManualSeverity(issue.getManualSeverity());
target.setMessage(issue.hasMsg() ? issue.getMsg() : null);
target.setLine(issue.hasLine() ? issue.getLine() : null);
target.setProjectUuid(context.getProject().uuid());
target.setProjectKey(context.getProject().key());
target.setEffortToFix(issue.hasEffortToFix() ? issue.getEffortToFix() : null);
target.setDebt(issue.hasDebtInMinutes() ? Duration.create(issue.getDebtInMinutes()) : null);
if (issue.hasDiffFields()) {
FieldDiffs fieldDiffs = FieldDiffs.parse(issue.getDiffFields());
fieldDiffs.setCreationDate(context.getAnalysisDate());
target.setCurrentChange(fieldDiffs);
}
target.setStatus(issue.getStatus());
target.setTags(issue.getTagsList());
target.setResolution(issue.hasResolution() ? issue.getResolution() : null);
target.setReporter(issue.hasReporter() ? issue.getReporter() : null);
target.setAssignee(issue.hasAssignee() ? issue.getAssignee() : null);
target.setChecksum(issue.hasChecksum() ? issue.getChecksum() : null);
target.setAttributes(issue.hasAttributes() ? KeyValueFormat.parse(issue.getAttributes()) : null);
target.setAuthorLogin(issue.hasAuthorLogin() ? issue.getAuthorLogin() : null);
target.setActionPlanKey(issue.hasActionPlanKey() ? issue.getActionPlanKey() : null);
target.setCreationDate(issue.hasCreationDate() ? new Date(issue.getCreationDate()) : null);
target.setUpdateDate(issue.hasUpdateDate() ? new Date(issue.getUpdateDate()) : null);
target.setCloseDate(issue.hasCloseDate() ? new Date(issue.getCloseDate()) : null);
target.setChanged(issue.getIsChanged());
target.setNew(issue.getIsNew());
target.setSelectedAt(issue.hasSelectedAt() ? issue.getSelectedAt() : null);
target.setSendNotifications(issue.getMustSendNotification());
return target;
}

public void afterReportProcessing() {
finalIssuesAppender.close();
diskIssuesAppender.close();
}

private void guessAuthor(DefaultIssue issue) {

+ 1
- 1
server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java View File

@@ -34,7 +34,7 @@ public class ComputationSteps {
*/
public static List<Class<? extends ComputationStep>> orderedStepClasses() {
return Arrays.asList(
DigestReportStep.class,
ParseReportStep.class,
PersistIssuesStep.class,
SwitchSnapshotStep.class,
IndexComponentsStep.class,

server/sonar-server/src/main/java/org/sonar/server/computation/step/DigestReportStep.java → server/sonar-server/src/main/java/org/sonar/server/computation/step/ParseReportStep.java View File

@@ -20,20 +20,34 @@

package org.sonar.server.computation.step;

import org.sonar.server.computation.AnalysisReportService;
import org.sonar.batch.protocol.output.BatchOutputReader;
import org.sonar.batch.protocol.output.BatchOutput;
import org.sonar.server.computation.ComputationContext;
import org.sonar.server.computation.issue.IssueComputation;

public class DigestReportStep implements ComputationStep {
public class ParseReportStep implements ComputationStep {

private final AnalysisReportService reportService;
private final IssueComputation issueComputation;

public DigestReportStep(AnalysisReportService reportService) {
this.reportService = reportService;
public ParseReportStep(IssueComputation issueComputation) {
this.issueComputation = issueComputation;
}

@Override
public void execute(ComputationContext context) {
reportService.digest(context);
int rootComponentRef = context.getReportReader().readMetadata().getRootComponentRef();
processComponent(context, rootComponentRef);
issueComputation.afterReportProcessing();
}

private void processComponent(ComputationContext context, int componentRef) {
BatchOutputReader reader = context.getReportReader();
BatchOutput.ReportComponent component = reader.readComponent(componentRef);
issueComputation.processComponentIssues(context, component.getUuid(), reader.readComponentIssues(componentRef));

for (Integer childRef : component.getChildRefsList()) {
processComponent(context, childRef);
}
}

@Override

+ 0
- 135
server/sonar-server/src/test/java/org/sonar/server/computation/AnalysisReportServiceTest.java View File

@@ -1,135 +0,0 @@
/*
* 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.
*/
///*
// * 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;
//
//import com.google.common.collect.Lists;
//import org.junit.Before;
//import org.junit.Ignore;
//import org.junit.Test;
//import org.sonar.api.issue.internal.DefaultIssue;
//import org.sonar.api.rules.RuleFinder;
//import org.sonar.batch.protocol.output.component.ReportComponent;
//import org.sonar.core.component.ComponentDto;
//import org.sonar.core.computation.db.AnalysisReportDto;
//import org.sonar.core.issue.db.IssueStorage;
//import org.sonar.core.persistence.DbSession;
//import org.sonar.core.persistence.MyBatis;
//
//import java.io.File;
//import java.util.List;
//
//import static org.assertj.core.api.Assertions.assertThat;
//import static org.mockito.Matchers.any;
//import static org.mockito.Mockito.mock;
//import static org.mockito.Mockito.when;
//
//public class AnalysisReportServiceTest {
// private AnalysisReportService sut;
//
// private IssueStorage issueStorage;
//
// @Before
// public void before() throws Exception {
// issueStorage = new FakeIssueStorage();
// ComputeEngineIssueStorageFactory issueStorageFactory = mock(ComputeEngineIssueStorageFactory.class);
// when(issueStorageFactory.newComputeEngineIssueStorage(any(ComponentDto.class))).thenReturn(issueStorage);
// sut = new AnalysisReportService(issueStorageFactory);
// }
//
// @Test
// public void load_resources() throws Exception {
// File dir = new File(getClass().getResource("/org/sonar/server/computation/AnalysisReportServiceTest/report-folder").getFile());
// ComputationContext context = new ComputationContext(mock(AnalysisReportDto.class), mock(ComponentDto.class), dir);
//
// sut.initComponents(context);
//
// assertThat(context.getComponents()).hasSize(4);
// }
//
// @Test
// @Ignore("Temporarily ignored")
// public void save_issues() throws Exception {
// File dir = new File(getClass().getResource("/org/sonar/server/computation/AnalysisReportServiceTest/report-folder").getFile());
// ComputationContext context = new FakeComputationContext(dir);
//
// sut.saveIssues(context);
//
// assertThat(((FakeIssueStorage) issueStorage).issues).hasSize(6);
// }
//
// private static class FakeIssueStorage extends IssueStorage {
//
// public List<DefaultIssue> issues = null;
//
// protected FakeIssueStorage() {
// super(mock(MyBatis.class), mock(RuleFinder.class));
// }
//
// @Override
// public void save(Iterable<DefaultIssue> issues) {
// this.issues = Lists.newArrayList(issues);
// }
//
// @Override
// protected void doInsert(DbSession batchSession, long now, DefaultIssue issue) {
//
// }
//
// @Override
// protected void doUpdate(DbSession batchSession, long now, DefaultIssue issue) {
//
// }
// }
//
// private static class FakeComputationContext extends ComputationContext {
//
// public FakeComputationContext(File reportDir) {
// super(mock(AnalysisReportDto.class), mock(ComponentDto.class), reportDir);
// }
//
// @Override
// public ReportComponent getComponentByBatchId(Long batchId) {
// return new ReportComponent()
// .setBatchId(123)
// .setId(456);
// }
// }
//
//}

+ 61
- 82
server/sonar-server/src/test/java/org/sonar/server/computation/ComputationThreadLauncherTest.java View File

@@ -17,85 +17,64 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
///*
// * 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;
//
//import org.junit.After;
//import org.junit.Before;
//import org.junit.Rule;
//import org.junit.Test;
//import org.junit.rules.DisableOnDebug;
//import org.junit.rules.TestRule;
//import org.junit.rules.Timeout;
//import org.sonar.api.platform.Server;
//
//import java.util.concurrent.TimeUnit;
//
//import static org.mockito.Mockito.*;
//
//public class ComputationThreadLauncherTest {
//
// @Rule
// public TestRule timeout = new DisableOnDebug(Timeout.seconds(5));
//
// private ComputationThreadLauncher sut;
// private ComputationService service;
// private AnalysisReportQueue queue;
//
// @Before
// public void before() {
// this.service = mock(ComputationService.class);
// this.queue = mock(AnalysisReportQueue.class);
// }
//
// @After
// public void after() {
// sut.stop();
// }
//
// @Test
// public void call_findAndBook_when_launching_a_recurrent_task() throws Exception {
// sut = new ComputationThreadLauncher(service, queue, 0, 1, TimeUnit.MILLISECONDS);
//
// sut.onServerStart(mock(Server.class));
//
// sleep();
//
// verify(queue, atLeastOnce()).pop();
// }
//
// @Test
// public void call_findAndBook_when_executing_task_immediately() throws Exception {
// sut = new ComputationThreadLauncher(service, queue, 1, 1, TimeUnit.HOURS);
// sut.start();
//
// sut.startAnalysisTaskNow();
//
// sleep();
//
// verify(queue, atLeastOnce()).pop();
// }
//
// private void sleep() throws InterruptedException {
// TimeUnit.MILLISECONDS.sleep(500L);
// }
//}

package org.sonar.server.computation;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.DisableOnDebug;
import org.junit.rules.TestRule;
import org.junit.rules.Timeout;
import org.sonar.api.platform.Server;

import java.util.concurrent.TimeUnit;

import static org.mockito.Mockito.*;

public class ComputationThreadLauncherTest {

@Rule
public TestRule timeout = new DisableOnDebug(Timeout.seconds(5));

ComputationThreadLauncher sut;
AnalysisReportQueue queue;

@Before
public void before() {
this.queue = mock(AnalysisReportQueue.class);
}

@After
public void after() {
sut.stop();
}

@Test
public void call_findAndBook_when_launching_a_recurrent_task() throws Exception {
sut = new ComputationThreadLauncher(queue, 0, 1, TimeUnit.MILLISECONDS);

sut.onServerStart(mock(Server.class));

sleep();

verify(queue, atLeastOnce()).pop();
}

@Test
public void call_findAndBook_when_executing_task_immediately() throws Exception {
sut = new ComputationThreadLauncher(queue, 1, 1, TimeUnit.HOURS);
sut.start();

sut.startAnalysisTaskNow();

sleep();

verify(queue, atLeastOnce()).pop();
}

private void sleep() throws InterruptedException {
TimeUnit.MILLISECONDS.sleep(500L);
}
}

+ 40
- 34
server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueComputationTest.java View File

@@ -25,18 +25,20 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.Mockito;
import org.sonar.api.issue.Issue;
import org.sonar.api.issue.internal.DefaultIssue;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.utils.System2;
import org.sonar.batch.protocol.output.BatchOutput;
import org.sonar.core.rule.RuleDto;
import org.sonar.server.computation.ComputationContext;

import java.io.IOException;
import java.util.Arrays;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.*;

public class IssueComputationTest {

@@ -45,129 +47,133 @@ public class IssueComputationTest {
@Rule
public TemporaryFolder temp = new TemporaryFolder();

IssueComputation sut;

// inputs
RuleCache ruleCache = mock(RuleCache.class);
SourceLinesCache lineCache = mock(SourceLinesCache.class);
ScmAccountCache scmAccountCache = mock(ScmAccountCache.class);
DefaultIssue issue = new DefaultIssue().setRuleKey(RULE_KEY).setKey("ISSUE_A");
RuleDto rule = new RuleDto().setRepositoryKey(RULE_KEY.repository()).setRuleKey(RULE_KEY.rule());
BatchOutput.ReportIssue.Builder inputIssue = BatchOutput.ReportIssue.newBuilder()
.setUuid("ISSUE_A")
.setRuleRepository(RULE_KEY.repository())
.setRuleKey(RULE_KEY.rule())
.setStatus(Issue.STATUS_OPEN);

// output
IssueCache issueCache;

IssueComputation sut;
IssueCache outputIssues;

@Before
public void setUp() throws IOException {
when(ruleCache.get(RULE_KEY)).thenReturn(rule);
issueCache = new IssueCache(temp.newFile(), System2.INSTANCE);
sut = new IssueComputation(ruleCache, lineCache, scmAccountCache, issueCache);
outputIssues = new IssueCache(temp.newFile(), System2.INSTANCE);
sut = new IssueComputation(ruleCache, lineCache, scmAccountCache, outputIssues);
}

@Test
public void store_issues_on_disk() throws Exception {
process();

assertThat(Iterators.getOnlyElement(issueCache.traverse()).key()).isEqualTo("ISSUE_A");
assertThat(Iterators.getOnlyElement(outputIssues.traverse()).key()).isEqualTo("ISSUE_A");
}

@Test
public void copy_rule_tags_on_new_issues() throws Exception {
issue.setNew(true);
inputIssue.setIsNew(true);
rule.setTags(ImmutableSet.of("bug", "performance"));
rule.setSystemTags(ImmutableSet.of("blocker"));

process();

assertThat(Iterators.getOnlyElement(issueCache.traverse()).tags()).containsOnly("blocker", "bug", "performance");
assertThat(Iterators.getOnlyElement(outputIssues.traverse()).tags()).containsOnly("blocker", "bug", "performance");
}

@Test
public void do_not_copy_rule_tags_on_existing_issues() throws Exception {
issue.setNew(false);
inputIssue.setIsNew(false);
rule.setTags(ImmutableSet.of("bug", "performance"));
rule.setSystemTags(ImmutableSet.of("blocker"));

process();

assertThat(Iterators.getOnlyElement(issueCache.traverse()).tags()).isEmpty();
assertThat(Iterators.getOnlyElement(outputIssues.traverse()).tags()).isEmpty();
}

@Test
public void guess_author_of_new_issues() throws Exception {
issue.setNew(true);
issue.setLine(3);
inputIssue.setIsNew(true);
inputIssue.setLine(3);
when(lineCache.lineAuthor(3)).thenReturn("charlie");

process();

assertThat(Iterators.getOnlyElement(issueCache.traverse()).authorLogin()).isEqualTo("charlie");
assertThat(Iterators.getOnlyElement(outputIssues.traverse()).authorLogin()).isEqualTo("charlie");
}

@Test
public void do_not_fail_if_missing_author_for_new_issues() throws Exception {
issue.setNew(true);
issue.setLine(3);
inputIssue.setIsNew(true);
inputIssue.setLine(3);
when(lineCache.lineAuthor(3)).thenReturn(null);

process();

assertThat(Iterators.getOnlyElement(issueCache.traverse()).authorLogin()).isNull();
assertThat(Iterators.getOnlyElement(outputIssues.traverse()).authorLogin()).isNull();
}

@Test
public void do_not_guess_author_of_existing_issues() throws Exception {
issue.setNew(false);
issue.setLine(3);
inputIssue.setIsNew(false);
inputIssue.setLine(3);
when(lineCache.lineAuthor(3)).thenReturn("charlie");

process();

assertThat(Iterators.getOnlyElement(issueCache.traverse()).authorLogin()).isNull();
assertThat(Iterators.getOnlyElement(outputIssues.traverse()).authorLogin()).isNull();
}

@Test
public void auto_assign_new_issues() throws Exception {
issue.setNew(true);
issue.setAuthorLogin("charlie");
inputIssue.setIsNew(true);
inputIssue.setAuthorLogin("charlie");
when(scmAccountCache.getNullable("charlie")).thenReturn("char.lie");

process();

assertThat(Iterators.getOnlyElement(issueCache.traverse()).assignee()).isEqualTo("char.lie");
assertThat(Iterators.getOnlyElement(outputIssues.traverse()).assignee()).isEqualTo("char.lie");
}

@Test
public void do_not_auto_assign_existing_issues() throws Exception {
issue.setNew(false);
issue.setAuthorLogin("charlie");
inputIssue.setIsNew(false);
inputIssue.setAuthorLogin("charlie");
when(scmAccountCache.getNullable("charlie")).thenReturn("char.lie");

process();

assertThat(Iterators.getOnlyElement(issueCache.traverse()).assignee()).isNull();
assertThat(Iterators.getOnlyElement(outputIssues.traverse()).assignee()).isNull();
}

@Test
public void do_not_override_author_and_assignee_set_by_old_batch_plugins() throws Exception {
issue.setNew(true);
inputIssue.setIsNew(true);

// these fields were provided during project analysis, for instance
// by developer cockpit or issue-assign plugins
issue.setAuthorLogin("charlie");
issue.setAssignee("cabu");
inputIssue.setAuthorLogin("charlie");
inputIssue.setAssignee("cabu");

process();

// keep the values, without trying to update them
DefaultIssue cachedIssue = Iterators.getOnlyElement(issueCache.traverse());
DefaultIssue cachedIssue = Iterators.getOnlyElement(outputIssues.traverse());
assertThat(cachedIssue.assignee()).isEqualTo("cabu");
assertThat(cachedIssue.authorLogin()).isEqualTo("charlie");
verifyZeroInteractions(scmAccountCache);
}

private void process() {
sut.processComponentIssues("FILE_A", Arrays.asList(issue));
sut.processComponentIssues(mock(ComputationContext.class, Mockito.RETURNS_DEEP_STUBS), "FILE_A", Arrays.asList(inputIssue.build()));
sut.afterReportProcessing();
}
}

+ 3
- 3
server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputationStepsTest.java View File

@@ -33,7 +33,7 @@ public class ComputationStepsTest {
ComputationSteps registry = new ComputationSteps(
// unordered
mock(ApplyPermissionsStep.class),
mock(DigestReportStep.class),
mock(ParseReportStep.class),
mock(IndexSourceLinesStep.class),
mock(IndexViewsStep.class),
mock(PurgeRemovedViewsStep.class),
@@ -45,14 +45,14 @@ public class ComputationStepsTest {
mock(IndexComponentsStep.class));

assertThat(registry.orderedSteps()).hasSize(11);
assertThat(registry.orderedSteps().get(0)).isInstanceOf(DigestReportStep.class);
assertThat(registry.orderedSteps().get(0)).isInstanceOf(ParseReportStep.class);
assertThat(registry.orderedSteps().get(10)).isInstanceOf(SendIssueNotificationsStep.class);
}

@Test
public void fail_if_a_step_is_not_registered_in_picocontainer() throws Exception {
try {
new ComputationSteps(mock(DigestReportStep.class));
new ComputationSteps(mock(ParseReportStep.class));
fail();
} catch (IllegalStateException e) {
assertThat(e).hasMessageContaining("Component not found");

+ 0
- 64
server/sonar-server/src/test/java/org/sonar/server/computation/step/DigestReportStepTest.java View File

@@ -1,64 +0,0 @@
/*
* 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.
*/
/*
* 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.step;

import org.junit.Test;
import org.sonar.core.component.ComponentDto;
import org.sonar.core.computation.db.AnalysisReportDto;
import org.sonar.server.computation.AnalysisReportService;
import org.sonar.server.computation.ComputationContext;

import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

public class DigestReportStepTest {

@Test
public void call_service_method() throws Exception {
AnalysisReportService service = mock(AnalysisReportService.class);
DigestReportStep sut = new DigestReportStep(service);
ComputationContext context = new ComputationContext(mock(AnalysisReportDto.class), mock(ComponentDto.class), null);

sut.execute(context);

verify(service).digest(eq(context));
}
}

+ 2
- 6
server/sonar-server/src/test/java/org/sonar/server/computation/step/IndexComponentsStepTest.java View File

@@ -21,9 +21,8 @@
package org.sonar.server.computation.step;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.sonar.batch.protocol.output.BatchOutputReader;
import org.sonar.core.component.ComponentDto;
import org.sonar.core.computation.db.AnalysisReportDto;
import org.sonar.core.resource.ResourceIndexerDao;
@@ -35,9 +34,6 @@ import static org.mockito.Mockito.*;

public class IndexComponentsStepTest {

@Rule
public TemporaryFolder temp = new TemporaryFolder();

IndexComponentsStep sut;
ResourceIndexerDao resourceIndexerDao;

@@ -51,7 +47,7 @@ public class IndexComponentsStepTest {
public void call_indexProject_of_dao() throws IOException {
ComponentDto project = mock(ComponentDto.class);
when(project.getId()).thenReturn(123L);
ComputationContext context = new ComputationContext(mock(AnalysisReportDto.class), project, temp.newFolder());
ComputationContext context = new ComputationContext(mock(AnalysisReportDto.class), project, mock(BatchOutputReader.class));

sut.execute(context);


+ 2
- 2
server/sonar-server/src/test/java/org/sonar/server/computation/step/PurgeDatastoresStepTest.java View File

@@ -25,6 +25,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.Mockito;
import org.sonar.batch.protocol.output.BatchOutputReader;
import org.sonar.core.component.ComponentDto;
import org.sonar.core.computation.db.AnalysisReportDto;
import org.sonar.core.computation.dbcleaner.ProjectCleaner;
@@ -58,8 +59,7 @@ public class PurgeDatastoresStepTest {
ComponentDto project = mock(ComponentDto.class);
when(project.getId()).thenReturn(123L);
when(project.uuid()).thenReturn("UUID-1234");
ComputationContext context = new ComputationContext(mock(AnalysisReportDto.class), project,
temp.newFolder());
ComputationContext context = new ComputationContext(mock(AnalysisReportDto.class), project, mock(BatchOutputReader.class));

sut.execute(context);


+ 4
- 7
server/sonar-server/src/test/java/org/sonar/server/computation/step/SwitchSnapshotStepTest.java View File

@@ -24,9 +24,9 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.rules.TemporaryFolder;
import org.sonar.api.utils.DateUtils;
import org.sonar.api.utils.System2;
import org.sonar.batch.protocol.output.BatchOutputReader;
import org.sonar.core.computation.db.AnalysisReportDto;
import org.sonar.core.persistence.DbTester;
import org.sonar.server.component.ComponentTesting;
@@ -46,10 +46,7 @@ public class SwitchSnapshotStepTest {
@Rule
public DbTester db = new DbTester();

@Rule
public TemporaryFolder temp = new TemporaryFolder();

private SwitchSnapshotStep sut;
SwitchSnapshotStep sut;

@Before
public void before() {
@@ -62,7 +59,7 @@ public class SwitchSnapshotStepTest {
public void one_switch_with_a_snapshot_and_his_children() throws IOException {
db.prepareDbUnit(getClass(), "snapshots.xml");
ComputationContext context = new ComputationContext(AnalysisReportDto.newForTests(1L).setSnapshotId(1L),
ComponentTesting.newProjectDto(), temp.newFolder());
ComponentTesting.newProjectDto(), mock(BatchOutputReader.class));

sut.execute(context);

@@ -73,7 +70,7 @@ public class SwitchSnapshotStepTest {
public void throw_IllegalStateException_when_not_finding_snapshot() throws IOException {
db.prepareDbUnit(getClass(), "empty.xml");
ComputationContext context = new ComputationContext(AnalysisReportDto.newForTests(1L).setSnapshotId(1L),
ComponentTesting.newProjectDto(), temp.newFolder());
ComponentTesting.newProjectDto(), mock(BatchOutputReader.class));

sut.execute(context);
}

+ 6
- 0
sonar-batch-protocol/compile_protobuf.sh View File

@@ -0,0 +1,6 @@
#!/bin/sh

OUTPUT_DIR="src/main/gen-java"

mkdir -p ${OUTPUT_DIR}
protoc --proto_path=src/main/protobuf --java_out=${OUTPUT_DIR} src/main/protobuf/*.proto

+ 22
- 0
sonar-batch-protocol/pom.xml View File

@@ -13,6 +13,10 @@
<description>Classes used for communication between batch and server</description>

<dependencies>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
@@ -42,6 +46,24 @@

<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>add-source</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>src/main/gen-java</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>

+ 269
- 0
sonar-batch-protocol/src/main/gen-java/org/sonar/batch/protocol/Constants.java View File

@@ -0,0 +1,269 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: constants.proto

package org.sonar.batch.protocol;

public final class Constants {
private Constants() {}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistry registry) {
}
/**
* Protobuf enum {@code Severity}
*/
public enum Severity
implements com.google.protobuf.ProtocolMessageEnum {
/**
* <code>INFO = 0;</code>
*/
INFO(0, 0),
/**
* <code>MINOR = 1;</code>
*/
MINOR(1, 1),
/**
* <code>MAJOR = 2;</code>
*/
MAJOR(2, 2),
/**
* <code>CRITICAL = 3;</code>
*/
CRITICAL(3, 3),
/**
* <code>BLOCKER = 4;</code>
*/
BLOCKER(4, 4),
;

/**
* <code>INFO = 0;</code>
*/
public static final int INFO_VALUE = 0;
/**
* <code>MINOR = 1;</code>
*/
public static final int MINOR_VALUE = 1;
/**
* <code>MAJOR = 2;</code>
*/
public static final int MAJOR_VALUE = 2;
/**
* <code>CRITICAL = 3;</code>
*/
public static final int CRITICAL_VALUE = 3;
/**
* <code>BLOCKER = 4;</code>
*/
public static final int BLOCKER_VALUE = 4;


public final int getNumber() { return value; }

public static Severity valueOf(int value) {
switch (value) {
case 0: return INFO;
case 1: return MINOR;
case 2: return MAJOR;
case 3: return CRITICAL;
case 4: return BLOCKER;
default: return null;
}
}

public static com.google.protobuf.Internal.EnumLiteMap<Severity>
internalGetValueMap() {
return internalValueMap;
}
private static com.google.protobuf.Internal.EnumLiteMap<Severity>
internalValueMap =
new com.google.protobuf.Internal.EnumLiteMap<Severity>() {
public Severity findValueByNumber(int number) {
return Severity.valueOf(number);
}
};

public final com.google.protobuf.Descriptors.EnumValueDescriptor
getValueDescriptor() {
return getDescriptor().getValues().get(index);
}
public final com.google.protobuf.Descriptors.EnumDescriptor
getDescriptorForType() {
return getDescriptor();
}
public static final com.google.protobuf.Descriptors.EnumDescriptor
getDescriptor() {
return org.sonar.batch.protocol.Constants.getDescriptor().getEnumTypes().get(0);
}

private static final Severity[] VALUES = values();

public static Severity valueOf(
com.google.protobuf.Descriptors.EnumValueDescriptor desc) {
if (desc.getType() != getDescriptor()) {
throw new java.lang.IllegalArgumentException(
"EnumValueDescriptor is not for this type.");
}
return VALUES[desc.getIndex()];
}

private final int index;
private final int value;

private Severity(int index, int value) {
this.index = index;
this.value = value;
}

// @@protoc_insertion_point(enum_scope:Severity)
}

/**
* Protobuf enum {@code ComponentType}
*/
public enum ComponentType
implements com.google.protobuf.ProtocolMessageEnum {
/**
* <code>PROJECT = 0;</code>
*/
PROJECT(0, 0),
/**
* <code>MODULE = 1;</code>
*/
MODULE(1, 1),
/**
* <code>DIRECTORY = 2;</code>
*/
DIRECTORY(2, 2),
/**
* <code>FILE = 3;</code>
*/
FILE(3, 3),
/**
* <code>VIEW = 4;</code>
*/
VIEW(4, 4),
/**
* <code>SUBVIEW = 5;</code>
*/
SUBVIEW(5, 5),
;

/**
* <code>PROJECT = 0;</code>
*/
public static final int PROJECT_VALUE = 0;
/**
* <code>MODULE = 1;</code>
*/
public static final int MODULE_VALUE = 1;
/**
* <code>DIRECTORY = 2;</code>
*/
public static final int DIRECTORY_VALUE = 2;
/**
* <code>FILE = 3;</code>
*/
public static final int FILE_VALUE = 3;
/**
* <code>VIEW = 4;</code>
*/
public static final int VIEW_VALUE = 4;
/**
* <code>SUBVIEW = 5;</code>
*/
public static final int SUBVIEW_VALUE = 5;


public final int getNumber() { return value; }

public static ComponentType valueOf(int value) {
switch (value) {
case 0: return PROJECT;
case 1: return MODULE;
case 2: return DIRECTORY;
case 3: return FILE;
case 4: return VIEW;
case 5: return SUBVIEW;
default: return null;
}
}

public static com.google.protobuf.Internal.EnumLiteMap<ComponentType>
internalGetValueMap() {
return internalValueMap;
}
private static com.google.protobuf.Internal.EnumLiteMap<ComponentType>
internalValueMap =
new com.google.protobuf.Internal.EnumLiteMap<ComponentType>() {
public ComponentType findValueByNumber(int number) {
return ComponentType.valueOf(number);
}
};

public final com.google.protobuf.Descriptors.EnumValueDescriptor
getValueDescriptor() {
return getDescriptor().getValues().get(index);
}
public final com.google.protobuf.Descriptors.EnumDescriptor
getDescriptorForType() {
return getDescriptor();
}
public static final com.google.protobuf.Descriptors.EnumDescriptor
getDescriptor() {
return org.sonar.batch.protocol.Constants.getDescriptor().getEnumTypes().get(1);
}

private static final ComponentType[] VALUES = values();

public static ComponentType valueOf(
com.google.protobuf.Descriptors.EnumValueDescriptor desc) {
if (desc.getType() != getDescriptor()) {
throw new java.lang.IllegalArgumentException(
"EnumValueDescriptor is not for this type.");
}
return VALUES[desc.getIndex()];
}

private final int index;
private final int value;

private ComponentType(int index, int value) {
this.index = index;
this.value = value;
}

// @@protoc_insertion_point(enum_scope:ComponentType)
}


public static com.google.protobuf.Descriptors.FileDescriptor
getDescriptor() {
return descriptor;
}
private static com.google.protobuf.Descriptors.FileDescriptor
descriptor;
static {
java.lang.String[] descriptorData = {
"\n\017constants.proto*E\n\010Severity\022\010\n\004INFO\020\000\022" +
"\t\n\005MINOR\020\001\022\t\n\005MAJOR\020\002\022\014\n\010CRITICAL\020\003\022\013\n\007B" +
"LOCKER\020\004*X\n\rComponentType\022\013\n\007PROJECT\020\000\022\n" +
"\n\006MODULE\020\001\022\r\n\tDIRECTORY\020\002\022\010\n\004FILE\020\003\022\010\n\004V" +
"IEW\020\004\022\013\n\007SUBVIEW\020\005B\034\n\030org.sonar.batch.pr" +
"otocolH\001"
};
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
new com.google.protobuf.Descriptors.FileDescriptor. InternalDescriptorAssigner() {
public com.google.protobuf.ExtensionRegistry assignDescriptors(
com.google.protobuf.Descriptors.FileDescriptor root) {
descriptor = root;
return null;
}
};
com.google.protobuf.Descriptors.FileDescriptor
.internalBuildGeneratedFileFrom(descriptorData,
new com.google.protobuf.Descriptors.FileDescriptor[] {
}, assigner);
}

// @@protoc_insertion_point(outer_class_scope)
}

+ 6470
- 0
sonar-batch-protocol/src/main/gen-java/org/sonar/batch/protocol/output/BatchOutput.java
File diff suppressed because it is too large
View File


+ 54
- 0
sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/ProtobufUtil.java View File

@@ -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.batch.protocol;

import com.google.protobuf.Message;
import com.google.protobuf.Parser;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class ProtobufUtil {
private ProtobufUtil() {
// only static stuff
}

public static <T extends Message> T readFile(File file, Parser<T> parser) {
try (InputStream input = new BufferedInputStream(new FileInputStream(file))) {
return parser.parseFrom(input);
} catch (IOException e) {
throw new IllegalStateException("Failed to read file: " + file, e);
}
}

public static void writeToFile(Message message, File toFile) {
try (OutputStream out = new BufferedOutputStream(new FileOutputStream(toFile, false))) {
message.writeTo(out);
} catch (IOException e) {
throw new IllegalStateException("Unable to write protocol buffer data to file " + toFile, e);
}
}
}

+ 62
- 0
sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/BatchOutputReader.java View File

@@ -0,0 +1,62 @@
/*
* 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.protocol.output;

import org.sonar.batch.protocol.ProtobufUtil;

import javax.annotation.CheckForNull;
import java.io.File;
import java.util.Collections;

public class BatchOutputReader {

private final FileStructure fileStructure;

public BatchOutputReader(File dir) {
this.fileStructure = new FileStructure(dir);
}

public BatchOutput.ReportMetadata readMetadata() {
File file = fileStructure.metadataFile();
if (!file.exists() || !file.isFile()) {
throw new IllegalStateException("Metadata file is missing in analysis report: " + file);
}
return ProtobufUtil.readFile(file, BatchOutput.ReportMetadata.PARSER);
}

@CheckForNull
public BatchOutput.ReportComponent readComponent(int componentRef) {
File file = fileStructure.fileFor(FileStructure.Domain.COMPONENT, componentRef);
if (file.exists() && file.isFile()) {
return ProtobufUtil.readFile(file, BatchOutput.ReportComponent.PARSER);
}
return null;
}

public Iterable<BatchOutput.ReportIssue> readComponentIssues(int componentRef) {
File file = fileStructure.fileFor(FileStructure.Domain.ISSUES, componentRef);
if (file.exists() && file.isFile()) {
// all the issues are loaded in memory
BatchOutput.ReportIssues issues = ProtobufUtil.readFile(file, BatchOutput.ReportIssues.PARSER);
return issues.getListList();
}
return Collections.emptyList();
}
}

+ 65
- 0
sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/BatchOutputWriter.java View File

@@ -0,0 +1,65 @@
/*
* 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.protocol.output;

import org.sonar.batch.protocol.ProtobufUtil;

import java.io.File;

public class BatchOutputWriter {

private final FileStructure fileStructure;

public BatchOutputWriter(File dir) {
if (!dir.exists() && !dir.mkdirs()) {
throw new IllegalStateException("Unable to create directory: " + dir);
}
this.fileStructure = new FileStructure(dir);
}

FileStructure getFileStructure() {
return fileStructure;
}

public boolean hasComponentData(FileStructure.Domain domain, int componentRef) {
File file = fileStructure.fileFor(domain, componentRef);
return file.exists() && file.isFile();
}

/**
* Metadata is mandatory
*/
public void writeMetadata(BatchOutput.ReportMetadata metadata) {
ProtobufUtil.writeToFile(metadata, fileStructure.metadataFile());
}

public void writeComponent(BatchOutput.ReportComponent component) {
File file = fileStructure.fileFor(FileStructure.Domain.COMPONENT, component.getRef());
ProtobufUtil.writeToFile(component, file);
}

public void writeComponentIssues(int componentRef, Iterable<BatchOutput.ReportIssue> issues) {
BatchOutput.ReportIssues.Builder issuesBuilder = BatchOutput.ReportIssues.newBuilder();
issuesBuilder.setComponentRef(componentRef);
issuesBuilder.addAllList(issues);
File file = fileStructure.fileFor(FileStructure.Domain.ISSUES, componentRef);
ProtobufUtil.writeToFile(issuesBuilder.build(), file);
}
}

sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/component/ReportComponents.java → sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/FileStructure.java View File

@@ -17,41 +17,40 @@
* 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.protocol.output.component;
package org.sonar.batch.protocol.output;

import org.sonar.batch.protocol.GsonHelper;
import java.io.File;

import java.util.Date;

public class ReportComponents {
/**
* Structure of files in the zipped report
*/
public class FileStructure {

private Date analysisDate;
public static enum Domain {
ISSUES("issues-"), COMPONENT("component-");

private ReportComponent root;
private final String filePrefix;

public void setAnalysisDate(Date analysisDate) {
this.analysisDate = analysisDate;
Domain(String filePrefix) {
this.filePrefix = filePrefix;
}
}

public Date analysisDate() {
return analysisDate;
}

public ReportComponents setRoot(ReportComponent r) {
this.root = r;
return this;
}
private final File dir;

public ReportComponent root() {
return root;
FileStructure(File dir) {
if (!dir.exists() || !dir.isDirectory()) {
throw new IllegalArgumentException("Directory of analysis report does not exist: " + dir);
}
this.dir = dir;
}

public String toJson() {
return GsonHelper.create().toJson(this);
public File metadataFile() {
return new File(dir, "metadata.pb");
}

public static ReportComponents fromJson(String json) {
return GsonHelper.create().fromJson(json, ReportComponents.class);
public File fileFor(Domain domain, int componentRef) {
return new File(dir, domain.filePrefix + componentRef + ".pb");
}

}

+ 0
- 170
sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/ReportHelper.java View File

@@ -1,170 +0,0 @@
/*
* 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.protocol.output;

import com.google.gson.Gson;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import org.apache.commons.io.Charsets;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.sonar.batch.protocol.GsonHelper;
import org.sonar.batch.protocol.output.component.ReportComponents;
import org.sonar.batch.protocol.output.issue.ReportIssue;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Iterator;
import java.util.NoSuchElementException;

public class ReportHelper {

private static final String COMPONENTS_JSON = "components.json";
private final File reportRootDir;
private final Gson gson = GsonHelper.create();

private ReportHelper(File reportRootDir) {
this.reportRootDir = reportRootDir;
}

public static ReportHelper create(File workDirectory) {
if (!workDirectory.exists() && !workDirectory.mkdirs()) {
throw new IllegalStateException("Unable to create directory " + workDirectory);
}
return new ReportHelper(workDirectory);
}

public File reportRootDir() {
return reportRootDir;
}

public void saveComponents(ReportComponents components) {
File resourcesFile = new File(reportRootDir, COMPONENTS_JSON);
try {
FileUtils.write(resourcesFile, components.toJson());
} catch (IOException e) {
throw new IllegalStateException("Unable to write components", e);
}
}

public void saveIssues(long componentBatchId, Iterable<ReportIssue> issues) {
File issuesFile = getIssuesFile(componentBatchId);
try (OutputStreamWriter out = new OutputStreamWriter(new BufferedOutputStream(new FileOutputStream(issuesFile)), "UTF-8")) {

JsonWriter writer = new JsonWriter(out);
writer.setIndent(" ");
writer.beginArray();
for (ReportIssue reportIssue : issues) {
gson.toJson(reportIssue, ReportIssue.class, writer);
}
writer.endArray();
writer.close();
} catch (IOException e) {
throw new IllegalStateException("Unable to save issues", e);
}
}

private File getIssuesFile(long componentBatchId) {
return new File(getComponentFolder(componentBatchId), "issues-" + componentBatchId + ".json");
}

private File getComponentFolder(long componentBatchId) {
File folder = new File(reportRootDir, Long.toString(componentBatchId));
if (!folder.exists() && !folder.mkdir()) {
throw new IllegalStateException("Unable to create directory " + folder);
}
return folder;
}

public ReportComponents getComponents() {
File file = new File(reportRootDir, COMPONENTS_JSON);

try (InputStream resourcesStream = new FileInputStream(file)) {
String json = IOUtils.toString(resourcesStream);
return ReportComponents.fromJson(json);
} catch (IOException e) {
throw new IllegalStateException("Failed to read issues", e);
}
}

public Iterable<ReportIssue> getIssues(final long componentBatchId) {

return new Iterable<ReportIssue>() {
@Override
public Iterator<ReportIssue> iterator() {
return new ReportIssueIterator(getIssuesFile(componentBatchId));
}
};
}

private final class ReportIssueIterator implements Iterator<ReportIssue> {

private JsonReader reader;

public ReportIssueIterator(File issuesFile) {
try {
reader = new JsonReader(new InputStreamReader(new BufferedInputStream(new FileInputStream(issuesFile)), Charsets.UTF_8));
reader.beginArray();
} catch (IOException e) {
throw new IllegalStateException("Unable to read " + issuesFile, e);
}
}

@Override
public boolean hasNext() {
try {
if (reader.hasNext()) {
return true;
}
reader.endArray();
reader.close();
return false;
} catch (IOException e) {
IOUtils.closeQuietly(reader);
throw new IllegalStateException("Unable to iterate over JSON file ", e);
}
}

@Override
public ReportIssue next() {
try {
if (!reader.hasNext()) {
throw new NoSuchElementException();
}
} catch (IOException e) {
throw new IllegalStateException("Unable to iterate over JSON file ", e);
}
return gson.fromJson(reader, ReportIssue.class);
}

@Override
public void remove() {
throw new UnsupportedOperationException("remove");
}
}

}

+ 0
- 154
sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/component/ReportComponent.java View File

@@ -1,154 +0,0 @@
/*
* 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.protocol.output.component;

import javax.annotation.CheckForNull;
import javax.annotation.Nullable;

import java.util.ArrayList;
import java.util.Collection;

public class ReportComponent {

public enum Type {
PRJ,
MOD,
DIR,
FIL,
VIEW,
SUBVIEW
}

private long batchId;
private int id;
private int snapshotId;
private String path;
private String name;
private String uuid;
private Type type;
// Only for files
private Boolean isTest;
private String languageKey;

private Collection<ReportComponent> children = new ArrayList<ReportComponent>();

public ReportComponent setBatchId(long batchId) {
this.batchId = batchId;
return this;
}

public long batchId() {
return batchId;
}

public ReportComponent setId(int id) {
this.id = id;
return this;
}

public int id() {
return id;
}

public ReportComponent setSnapshotId(int snapshotId) {
this.snapshotId = snapshotId;
return this;
}

public int snapshotId() {
return snapshotId;
}

public ReportComponent setPath(String path) {
this.path = path;
return this;
}

public String path() {
return path;
}

public ReportComponent setUuid(String s) {
this.uuid = s;
return this;
}

public String uuid() {
return uuid;
}

public ReportComponent setName(@Nullable String name) {
this.name = name;
return this;
}

/**
* @return null for files and directories since it is the same as the path
*/
@CheckForNull
public String name() {
return name;
}

public ReportComponent setType(Type type) {
this.type = type;
return this;
}

public Type type() {
return type;
}

public ReportComponent setTest(@Nullable Boolean isTest) {
this.isTest = isTest;
return this;
}

/**
* @return null when not a file
*/
@CheckForNull
public Boolean isTest() {
return isTest;
}

public ReportComponent setLanguageKey(@Nullable String languageKey) {
this.languageKey = languageKey;
return this;
}

/**
* @return null when not a file
*/
@CheckForNull
public String languageKey() {
return languageKey;
}

public ReportComponent addChild(ReportComponent child) {
this.children.add(child);
return this;
}

public Collection<ReportComponent> children() {
return children;
}

}

+ 0
- 298
sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/issue/ReportIssue.java View File

@@ -1,298 +0,0 @@
/*
* 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.protocol.output.issue;

import javax.annotation.Nullable;

import java.util.Collection;
import java.util.Date;

public class ReportIssue {

private Long componentBatchId;
private String ruleKey;
private String ruleRepo;
private Integer line;
private String message;
private Double effortToFix;
private String severity;
private Collection<String> tags;

// Temporary fields that should be removed when aggregation/issue tracking is done by computation
private boolean isNew;
private String key;
private Long debtInMinutes;
private String resolution;
private String status;
private String checksum;
private boolean manualSeverity;
private String reporter;
private String assignee;
private String actionPlanKey;
private String attributes;
private String authorLogin;
private Date creationDate;
private Date closeDate;
private Date updateDate;
private Long selectedAt;
private String diffFields;
private boolean isChanged;
private boolean mustSendNotification;


public ReportIssue setKey(String key) {
this.key = key;
return this;
}

public String key() {
return key;
}

public ReportIssue setComponentBatchId(@Nullable Long resourceBatchId) {
this.componentBatchId = resourceBatchId;
return this;
}

public Long componentBatchId() {
return componentBatchId;
}

public ReportIssue setNew(boolean isNew) {
this.isNew = isNew;
return this;
}

public boolean isNew() {
return isNew;
}

public ReportIssue setLine(Integer line) {
this.line = line;
return this;
}

public Integer line() {
return line;
}

public ReportIssue setMessage(String message) {
this.message = message;
return this;
}

public String message() {
return message;
}

public ReportIssue setEffortToFix(Double effortToFix) {
this.effortToFix = effortToFix;
return this;
}

public Double effortToFix() {
return effortToFix;
}

public ReportIssue setDebt(Long debtInMinutes) {
this.debtInMinutes = debtInMinutes;
return this;
}

public Long debt() {
return debtInMinutes;
}

public ReportIssue setResolution(String resolution) {
this.resolution = resolution;
return this;
}

public String resolution() {
return resolution;
}

public ReportIssue setStatus(String status) {
this.status = status;
return this;
}

public String status() {
return status;
}

public ReportIssue setSeverity(String severity) {
this.severity = severity;
return this;
}

public String severity() {
return severity;
}

public ReportIssue setChecksum(String checksum) {
this.checksum = checksum;
return this;
}

public String checksum() {
return checksum;
}

public ReportIssue setManualSeverity(boolean manualSeverity) {
this.manualSeverity = manualSeverity;
return this;
}

public boolean isManualSeverity() {
return manualSeverity;
}

public ReportIssue setReporter(String reporter) {
this.reporter = reporter;
return this;
}

public String reporter() {
return reporter;
}

public ReportIssue setAssignee(String assignee) {
this.assignee = assignee;
return this;
}

public String assignee() {
return assignee;
}

public Collection<String> tags() {
return tags;
}

public ReportIssue setTags(Collection<String> s) {
this.tags = s;
return this;
}

public ReportIssue setRuleKey(String ruleRepo, String ruleKey) {
this.ruleRepo = ruleRepo;
this.ruleKey = ruleKey;
return this;
}

public String ruleRepo() {
return ruleRepo;
}

public String ruleKey() {
return ruleKey;
}

public ReportIssue setActionPlanKey(String actionPlanKey) {
this.actionPlanKey = actionPlanKey;
return this;
}

public String actionPlanKey() {
return actionPlanKey;
}

public ReportIssue setAttributes(String attributes) {
this.attributes = attributes;
return this;
}

public String issueAttributes() {
return attributes;
}

public ReportIssue setAuthorLogin(String authorLogin) {
this.authorLogin = authorLogin;
return this;
}

public String authorLogin() {
return authorLogin;
}

public ReportIssue setCreationDate(Date creationDate) {
this.creationDate = creationDate;
return this;
}

public Date creationDate() {
return creationDate;
}

public ReportIssue setCloseDate(Date closeDate) {
this.closeDate = closeDate;
return this;
}

public Date closeDate() {
return closeDate;
}

public ReportIssue setUpdateDate(Date updateDate) {
this.updateDate = updateDate;
return this;
}

public Date updateDate() {
return updateDate;
}

public ReportIssue setSelectedAt(Long selectedAt) {
this.selectedAt = selectedAt;
return this;
}

public Long selectedAt() {
return selectedAt;
}

public ReportIssue setDiffFields(@Nullable String diffFields) {
this.diffFields = diffFields;
return this;
}

public String diffFields() {
return diffFields;
}

public ReportIssue setChanged(boolean isChanged) {
this.isChanged = isChanged;
return this;
}

public boolean isChanged() {
return isChanged;
}

public ReportIssue setMustSendNotification(boolean mustSendNotification) {
this.mustSendNotification = mustSendNotification;
return this;
}

public boolean mustSendNotifications() {
return mustSendNotification;
}
}

+ 0
- 24
sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/issue/package-info.java View File

@@ -1,24 +0,0 @@
/*
* 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.batch.protocol.output.issue;

import javax.annotation.ParametersAreNonnullByDefault;


+ 95
- 0
sonar-batch-protocol/src/main/protobuf/batch_output.proto View File

@@ -0,0 +1,95 @@
/*
SonarQube, open source software quality management tool.
Copyright (C) 2008-2015 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.
*/

/*
Notes

- "required" fields are not used as recommended by Google to keep forward-compatibility:
https://developers.google.com/protocol-buffers/docs/proto#simple

- this is beta version of specification. It will evolve during next releases and is
not forward-compatible yet.

- the related Java files are not generated during build. Indeed the existing protoc maven
plugins require protobuf to be installed on boxes. That means that generated Java files
are updated and committed for each change (see src/main/gen-java).
*/


import "constants.proto";

option java_package = "org.sonar.batch.protocol.output";
option optimize_for = SPEED;

message ReportMetadata {
optional int64 analysis_date = 1;
optional string project_key = 2;
optional int32 root_component_ref = 3;
}

message ReportComponent {
optional int32 ref = 1;
optional string path = 2;
optional string name = 3;
optional ComponentType type = 4;
optional bool is_test = 5;
optional string language = 6;
repeated int32 child_refs = 7;

// temporary fields during development of computation stack
optional int32 snapshot_id = 8;
optional string uuid = 9;
}

message ReportIssue {
optional string rule_repository = 1;
optional string rule_key = 2;
optional int32 line = 3;
optional string msg = 4;
optional Severity severity = 5;
repeated string tags = 6;

// temporary fields during development of computation stack
optional double effort_to_fix = 7;
optional bool is_new = 8;
optional string uuid = 9;
optional int64 debt_in_minutes = 10;
optional string resolution = 11;
optional string status = 12;
optional string checksum = 13;
optional bool manual_severity = 14;
optional string reporter = 15;
optional string assignee = 16;
optional string action_plan_key = 17;
optional string attributes = 18;
optional string author_login = 19;
optional int64 creation_date = 20;
optional int64 close_date = 21;
optional int64 update_date = 22;
optional int64 selected_at = 23;
optional string diff_fields = 24;
optional bool is_changed = 25;
optional bool must_send_notification = 26;
}

message ReportIssues {
optional int32 component_ref = 1;
repeated ReportIssue list = 2;
}

+ 39
- 0
sonar-batch-protocol/src/main/protobuf/constants.proto View File

@@ -0,0 +1,39 @@
/*
SonarQube, open source software quality management tool.
Copyright (C) 2008-2015 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.
*/

option java_package = "org.sonar.batch.protocol";
option optimize_for = SPEED;

enum Severity {
INFO = 0;
MINOR = 1;
MAJOR = 2;
CRITICAL = 3;
BLOCKER = 4;
}

enum ComponentType {
PROJECT = 0;
MODULE = 1;
DIRECTORY = 2;
FILE = 3;
VIEW = 4;
SUBVIEW = 5;
}

sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/component/package-info.java → sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/ProtobufUtilTest.java View File

@@ -17,8 +17,18 @@
* 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.batch.protocol.output.component;
package org.sonar.batch.protocol;

import javax.annotation.ParametersAreNonnullByDefault;
import org.junit.Test;
import org.sonar.test.TestUtils;

import static org.assertj.core.api.Assertions.assertThat;

public class ProtobufUtilTest {

@Test
public void only_utils() throws Exception {
assertThat(TestUtils.hasOnlyPrivateConstructors(ProtobufUtil.class));
}

}

+ 77
- 0
sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/output/BatchOutputReaderTest.java View File

@@ -0,0 +1,77 @@
/*
* 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.protocol.output;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

import java.io.File;
import java.util.Arrays;

import static org.assertj.core.api.Assertions.assertThat;

public class BatchOutputReaderTest {

@Rule
public TemporaryFolder temp = new TemporaryFolder();

@Test
public void create_dir_if_does_not_exist() throws Exception {
File dir = temp.newFolder();

initFiles(dir);

BatchOutputReader reader = new BatchOutputReader(dir);
assertThat(reader.readMetadata().getAnalysisDate()).isEqualTo(15000000L);
assertThat(reader.readComponentIssues(1)).hasSize(1);
assertThat(reader.readComponentIssues(200)).isEmpty();
assertThat(reader.readComponent(1).getUuid()).isEqualTo("UUID_A");
assertThat(reader.readComponent(200)).isNull();

}

private void initFiles(File dir) {
BatchOutputWriter writer = new BatchOutputWriter(dir);

BatchOutput.ReportMetadata.Builder metadata = BatchOutput.ReportMetadata.newBuilder()
.setAnalysisDate(15000000L)
.setProjectKey("PROJECT_A")
.setRootComponentRef(1);
writer.writeMetadata(metadata.build());

BatchOutput.ReportComponent.Builder component = BatchOutput.ReportComponent.newBuilder()
.setRef(1)
.setUuid("UUID_A");
writer.writeComponent(component.build());

BatchOutput.ReportIssue issue = BatchOutput.ReportIssue.newBuilder()
.setUuid("ISSUE_A")
.setLine(50)
.build();

writer.writeComponentIssues(1, Arrays.asList(issue));
}

@Test
public void readMetadata() throws Exception {

}
}

+ 120
- 0
sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/output/BatchOutputWriterTest.java View File

@@ -0,0 +1,120 @@
/*
* 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.protocol.output;

import org.apache.commons.io.FileUtils;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.sonar.batch.protocol.Constants;
import org.sonar.batch.protocol.ProtobufUtil;

import java.io.File;
import java.util.Arrays;

import static org.assertj.core.api.Assertions.assertThat;

public class BatchOutputWriterTest {

@Rule
public TemporaryFolder temp = new TemporaryFolder();

@Test
public void create_dir_if_does_not_exist() throws Exception {
File dir = temp.newFolder();
FileUtils.deleteQuietly(dir);

new BatchOutputWriter(dir);

assertThat(dir).isDirectory().exists();
}

@Test
public void write_metadata() throws Exception {
File dir = temp.newFolder();
BatchOutputWriter writer = new BatchOutputWriter(dir);
BatchOutput.ReportMetadata.Builder metadata = BatchOutput.ReportMetadata.newBuilder()
.setAnalysisDate(15000000L)
.setProjectKey("PROJECT_A")
.setRootComponentRef(1);
writer.writeMetadata(metadata.build());

BatchOutput.ReportMetadata read = ProtobufUtil.readFile(writer.getFileStructure().metadataFile(), BatchOutput.ReportMetadata.PARSER);
assertThat(read.getAnalysisDate()).isEqualTo(15000000L);
assertThat(read.getProjectKey()).isEqualTo("PROJECT_A");
assertThat(read.getRootComponentRef()).isEqualTo(1);
}

@Test
public void write_component() throws Exception {
File dir = temp.newFolder();
BatchOutputWriter writer = new BatchOutputWriter(dir);

// no data yet
assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 1)).isFalse();

// write data
BatchOutput.ReportComponent.Builder component = BatchOutput.ReportComponent.newBuilder()
.setRef(1)
.setLanguage("java")
.setPath("src/Foo.java")
.setUuid("UUID_A")
.setType(Constants.ComponentType.FILE)
.setIsTest(false)
.addChildRefs(5)
.addChildRefs(42);
writer.writeComponent(component.build());

assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 1)).isTrue();
File file = writer.getFileStructure().fileFor(FileStructure.Domain.COMPONENT, 1);
assertThat(file).exists().isFile();
BatchOutput.ReportComponent read = ProtobufUtil.readFile(file, BatchOutput.ReportComponent.PARSER);
assertThat(read.getRef()).isEqualTo(1);
assertThat(read.getChildRefsList()).containsOnly(5, 42);
assertThat(read.hasName()).isFalse();
assertThat(read.getIsTest()).isFalse();
assertThat(read.getUuid()).isEqualTo("UUID_A");
}

@Test
public void write_issues() throws Exception {
File dir = temp.newFolder();
BatchOutputWriter writer = new BatchOutputWriter(dir);

// no data yet
assertThat(writer.hasComponentData(FileStructure.Domain.ISSUES, 1)).isFalse();

// write data
BatchOutput.ReportIssue issue = BatchOutput.ReportIssue.newBuilder()
.setUuid("ISSUE_A")
.setLine(50)
.setMsg("the message")
.build();

writer.writeComponentIssues(1, Arrays.asList(issue));

assertThat(writer.hasComponentData(FileStructure.Domain.ISSUES, 1)).isTrue();
File file = writer.getFileStructure().fileFor(FileStructure.Domain.ISSUES, 1);
assertThat(file).exists().isFile();
BatchOutput.ReportIssues read = ProtobufUtil.readFile(file, BatchOutput.ReportIssues.PARSER);
assertThat(read.getComponentRef()).isEqualTo(1);
assertThat(read.getListCount()).isEqualTo(1);
}
}

+ 74
- 0
sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/output/FileStructureTest.java View File

@@ -0,0 +1,74 @@
/*
* 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.protocol.output;

import org.apache.commons.io.FileUtils;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

import java.io.File;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;

public class FileStructureTest {

@Rule
public TemporaryFolder temp = new TemporaryFolder();

@Test
public void fail_if_dir_does_not_exist() throws Exception {
File dir = temp.newFolder();
FileUtils.deleteQuietly(dir);
try {
new FileStructure(dir);
fail();
} catch (IllegalArgumentException e) {
assertThat(e).hasMessageContaining("Directory of analysis report does not exist");
}
}

@Test
public void fail_if_invalid_dir() throws Exception {
// not a dir but a file
File dir = temp.newFile();
try {
new FileStructure(dir);
fail();
} catch (IllegalArgumentException e) {
assertThat(e).hasMessageContaining("Directory of analysis report does not exist");
}
}

@Test
public void locate_files() throws Exception {
File dir = temp.newFolder();
FileUtils.write(new File(dir, "metadata.pb"), "metadata content");
FileUtils.write(new File(dir, "issues-3.pb"), "issues of component 3");
FileUtils.write(new File(dir, "component-42.pb"), "details of component 42");

FileStructure structure = new FileStructure(dir);
assertThat(structure.metadataFile()).exists().isFile();
assertThat(structure.fileFor(FileStructure.Domain.COMPONENT, 42)).exists().isFile();
assertThat(structure.fileFor(FileStructure.Domain.ISSUES, 3)).exists().isFile();
assertThat(structure.fileFor(FileStructure.Domain.ISSUES, 42)).doesNotExist();
}
}

+ 0
- 55
sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/output/ReportHelperTest.java View File

@@ -1,55 +0,0 @@
/*
* 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.protocol.output;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.sonar.batch.protocol.output.component.ReportComponent;
import org.sonar.batch.protocol.output.component.ReportComponents;
import org.sonar.batch.protocol.output.issue.ReportIssue;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;

import static org.assertj.core.api.Assertions.assertThat;

public class ReportHelperTest {

@Rule
public TemporaryFolder temp = new TemporaryFolder();

@Test
public void createAndRead() throws IOException {
ReportHelper helper = ReportHelper.create(temp.newFolder());

helper.saveComponents(new ReportComponents().setRoot(new ReportComponent().setBatchId(1L)));

helper.saveIssues(1L, Arrays.asList(new ReportIssue().setRuleKey("foo", "bar")));

assertThat(new File(helper.reportRootDir(), "components.json")).exists();
assertThat(new File(helper.reportRootDir(), "1/issues-1.json")).exists();

assertThat(helper.getComponents().root().batchId()).isEqualTo(1L);
assertThat(helper.getIssues(1L)).hasSize(1);
}

}

+ 0
- 98
sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/output/component/ReportComponentsTest.java View File

@@ -1,98 +0,0 @@
/*
* 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.protocol.output.component;

import org.apache.commons.io.IOUtils;
import org.junit.Test;
import org.skyscreamer.jsonassert.JSONAssert;
import org.sonar.batch.protocol.output.component.ReportComponent.Type;

import java.text.SimpleDateFormat;
import java.util.Date;

import static org.assertj.core.api.Assertions.assertThat;

public class ReportComponentsTest {

@Test
public void to_json() throws Exception {
ReportComponents res = new ReportComponents();
Date d = new SimpleDateFormat("dd/MM/yyyy").parse("12/12/2012");
res.setAnalysisDate(d);
ReportComponent root = new ReportComponent()
.setBatchId(1)
.setId(11)
.setName("Root project")
.setSnapshotId(111)
.setType(Type.PRJ);
ReportComponent module = new ReportComponent()
.setBatchId(2)
.setId(22)
.setName("Module")
.setSnapshotId(222)
.setPath("module1")
.setType(Type.MOD);
root.addChild(module);
ReportComponent dir = new ReportComponent()
.setBatchId(3)
.setId(33)
.setName("src")
.setSnapshotId(333)
.setPath("src")
.setType(Type.DIR);
module.addChild(dir);
ReportComponent file = new ReportComponent()
.setBatchId(4)
.setId(44)
.setName("Foo.java")
.setSnapshotId(444)
.setPath("Foo.java")
.setType(Type.FIL)
.setTest(true)
.setLanguageKey("java");
dir.addChild(file);
res.setRoot(root);

JSONAssert
.assertEquals(
IOUtils.toString(this.getClass().getResourceAsStream("ReportComponentsTest/expected.json"), "UTF-8"),
res.toJson(), true);
}

@Test
public void from_json() throws Exception {
ReportComponents res = ReportComponents
.fromJson(
IOUtils.toString(this.getClass().getResourceAsStream("ReportComponentsTest/expected.json"), "UTF-8"));

assertThat(res.analysisDate()).isEqualTo(new SimpleDateFormat("dd/MM/yyyy").parse("12/12/2012"));
ReportComponent root = res.root();
assertThat(root.batchId()).isEqualTo(1);
assertThat(root.id()).isEqualTo(11);
assertThat(root.name()).isEqualTo("Root project");
assertThat(root.snapshotId()).isEqualTo(111);
assertThat(root.path()).isNull();
assertThat(root.type()).isEqualTo(Type.PRJ);
assertThat(root.children()).hasSize(1);
assertThat(root.isTest()).isNull();
assertThat(root.languageKey()).isNull();

}
}

+ 0
- 86
sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/output/issue/ReportIssueTest.java View File

@@ -1,86 +0,0 @@
/*
* 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.protocol.output.issue;

import org.junit.Test;

import java.text.SimpleDateFormat;

import static org.assertj.core.api.Assertions.assertThat;

public class ReportIssueTest {

@Test
public void testGetterSetter() throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
ReportIssue issue = new ReportIssue()
.setActionPlanKey("plan")
.setAssignee("assignee")
.setAuthorLogin("author")
.setChanged(true)
.setChecksum("checksum")
.setDebt(3L)
.setDiffFields("diff")
.setEffortToFix(2.0)
.setAttributes("attributes")
.setCloseDate(sdf.parse("11/12/2012"))
.setCreationDate(sdf.parse("12/12/2012"))
.setUpdateDate(sdf.parse("13/12/2012"))
.setKey("key")
.setLine(3)
.setManualSeverity(true)
.setMessage("message")
.setNew(true)
.setReporter("reporter")
.setResolution("resolution")
.setComponentBatchId(4L)
.setRuleKey("repo", "rule")
.setSelectedAt(234L)
.setSeverity("severity")
.setStatus("status");

assertThat(issue.actionPlanKey()).isEqualTo("plan");
assertThat(issue.assignee()).isEqualTo("assignee");
assertThat(issue.authorLogin()).isEqualTo("author");
assertThat(issue.isChanged()).isTrue();
assertThat(issue.checksum()).isEqualTo("checksum");
assertThat(issue.debt()).isEqualTo(3L);
assertThat(issue.diffFields()).isEqualTo("diff");
assertThat(issue.effortToFix()).isEqualTo(2.0);
assertThat(issue.issueAttributes()).isEqualTo("attributes");
assertThat(issue.closeDate()).isEqualTo(sdf.parse("11/12/2012"));
assertThat(issue.creationDate()).isEqualTo(sdf.parse("12/12/2012"));
assertThat(issue.updateDate()).isEqualTo(sdf.parse("13/12/2012"));
assertThat(issue.key()).isEqualTo("key");
assertThat(issue.line()).isEqualTo(3);
assertThat(issue.isManualSeverity()).isTrue();
assertThat(issue.message()).isEqualTo("message");
assertThat(issue.isNew()).isTrue();
assertThat(issue.reporter()).isEqualTo("reporter");
assertThat(issue.resolution()).isEqualTo("resolution");
assertThat(issue.componentBatchId()).isEqualTo(4L);
assertThat(issue.ruleRepo()).isEqualTo("repo");
assertThat(issue.ruleKey()).isEqualTo("rule");
assertThat(issue.selectedAt()).isEqualTo(234L);
assertThat(issue.severity()).isEqualTo("severity");
assertThat(issue.status()).isEqualTo("status");
}

}

+ 3
- 3
sonar-batch/src/main/java/org/sonar/batch/index/BatchResource.java View File

@@ -31,13 +31,13 @@ import java.util.Collection;

public class BatchResource {

private final long batchId;
private final int batchId;
private final Resource r;
private Snapshot s;
private final BatchResource parent;
private final Collection<BatchResource> children = new ArrayList<BatchResource>();

public BatchResource(long batchId, Resource r, @Nullable BatchResource parent) {
public BatchResource(int batchId, Resource r, @Nullable BatchResource parent) {
this.batchId = batchId;
this.r = r;
this.parent = parent;
@@ -46,7 +46,7 @@ public class BatchResource {
}
}

public long batchId() {
public int batchId() {
return batchId;
}


+ 53
- 37
sonar-batch/src/main/java/org/sonar/batch/report/ComponentsPublisher.java View File

@@ -26,14 +26,15 @@ import org.sonar.api.resources.Resource;
import org.sonar.api.resources.ResourceUtils;
import org.sonar.batch.index.BatchResource;
import org.sonar.batch.index.ResourceCache;
import org.sonar.batch.protocol.output.ReportHelper;
import org.sonar.batch.protocol.output.component.ReportComponent;
import org.sonar.batch.protocol.output.component.ReportComponents;
import org.sonar.batch.protocol.Constants;
import org.sonar.batch.protocol.output.BatchOutput;
import org.sonar.batch.protocol.output.BatchOutputWriter;

import javax.annotation.CheckForNull;

import java.io.IOException;

/**
* Adds components and analysis metadata to output report
*/
public class ComponentsPublisher implements ReportPublisher {

private final ResourceCache resourceCache;
@@ -45,37 +46,52 @@ public class ComponentsPublisher implements ReportPublisher {
}

@Override
public void export(ReportHelper reportHelper) throws IOException {
ReportComponents components = new ReportComponents();
public void publish(BatchOutputWriter writer) {
BatchResource rootProject = resourceCache.get(reactor.getRoot().getKeyWithBranch());
components.setRoot(buildResourceForReport(rootProject));
components.setAnalysisDate(((Project) rootProject.resource()).getAnalysisDate());
reportHelper.saveComponents(components);
BatchOutput.ReportMetadata metadata = BatchOutput.ReportMetadata.newBuilder()
.setAnalysisDate(((Project) rootProject.resource()).getAnalysisDate().getTime())
.setProjectKey(((Project) rootProject.resource()).key())
.setRootComponentRef(rootProject.batchId())
.build();
writer.writeMetadata(metadata);
recursiveWriteComponent(rootProject, writer);
}

private ReportComponent buildResourceForReport(BatchResource batchResource) {
private void recursiveWriteComponent(BatchResource batchResource, BatchOutputWriter writer) {
Resource r = batchResource.resource();
Integer snapshotId = batchResource.snapshotId();
Integer id = r.getId();
ReportComponent result = new ReportComponent()
.setBatchId(batchResource.batchId())
.setSnapshotId(snapshotId != null ? snapshotId.intValue() : -1)
.setId(id != null ? id : -1)
.setName(getName(r))
.setPath(r.getPath())
.setUuid(r.getUuid())
.setType(getType(r))
.setLanguageKey(getLanguageKey(r))
.setTest(isTest(r));
BatchOutput.ReportComponent.Builder builder = BatchOutput.ReportComponent.newBuilder();

// non-null fields
builder.setRef(batchResource.batchId());
builder.setSnapshotId(batchResource.snapshotId());
builder.setUuid(r.getUuid());
builder.setType(getType(r));

// protocol buffers does not accept null values

if (ResourceUtils.isFile(r)) {
builder.setIsTest(ResourceUtils.isUnitTestClass(r));
}
String name = getName(r);
if (name != null) {
builder.setName(name);
}
String path = r.getPath();
if (path != null) {
builder.setPath(path);
}
String lang = getLanguageKey(r);
if (lang != null) {
builder.setLanguage(lang);
}
for (BatchResource child : batchResource.children()) {
result.addChild(buildResourceForReport(child));
builder.addChildRefs(child.batchId());
}
return result;
}
writer.writeComponent(builder.build());

@CheckForNull
private Boolean isTest(Resource r) {
return ResourceUtils.isFile(r) ? ResourceUtils.isUnitTestClass(r) : null;
for (BatchResource child : batchResource.children()) {
recursiveWriteComponent(child, writer);
}
}

@CheckForNull
@@ -90,21 +106,21 @@ public class ComponentsPublisher implements ReportPublisher {
return (ResourceUtils.isFile(r) || ResourceUtils.isDirectory(r)) ? null : r.getName();
}

private ReportComponent.Type getType(Resource r) {
private Constants.ComponentType getType(Resource r) {
if (ResourceUtils.isFile(r)) {
return ReportComponent.Type.FIL;
return Constants.ComponentType.FILE;
} else if (ResourceUtils.isDirectory(r)) {
return ReportComponent.Type.DIR;
return Constants.ComponentType.DIRECTORY;
} else if (ResourceUtils.isModuleProject(r)) {
return ReportComponent.Type.MOD;
return Constants.ComponentType.MODULE;
} else if (ResourceUtils.isRootProject(r)) {
return ReportComponent.Type.PRJ;
return Constants.ComponentType.PROJECT;
} else if (ResourceUtils.isView(r)) {
return ReportComponent.Type.VIEW;
return Constants.ComponentType.VIEW;
} else if (ResourceUtils.isSubview(r)) {
return ReportComponent.Type.SUBVIEW;
return Constants.ComponentType.SUBVIEW;
}
throw new IllegalArgumentException("Unknow resource type: " + r);
throw new IllegalArgumentException("Unknown resource type: " + r);
}

}

+ 80
- 37
sonar-batch/src/main/java/org/sonar/batch/report/IssuesPublisher.java View File

@@ -27,10 +27,13 @@ import org.sonar.api.utils.KeyValueFormat;
import org.sonar.batch.index.BatchResource;
import org.sonar.batch.index.ResourceCache;
import org.sonar.batch.issue.IssueCache;
import org.sonar.batch.protocol.output.ReportHelper;
import org.sonar.batch.protocol.output.issue.ReportIssue;
import org.sonar.batch.protocol.Constants;
import org.sonar.batch.protocol.output.BatchOutputWriter;
import org.sonar.batch.protocol.output.BatchOutput;

import java.io.IOException;
import javax.annotation.Nullable;

import java.util.Date;

public class IssuesPublisher implements ReportPublisher {

@@ -43,51 +46,91 @@ public class IssuesPublisher implements ReportPublisher {
}

@Override
public void export(ReportHelper reportHelper) throws IOException {
public void publish(BatchOutputWriter writer) {
for (BatchResource resource : resourceCache.all()) {
Iterable<DefaultIssue> issues = issueCache.byComponent(resource.resource().getEffectiveKey());
reportHelper.saveIssues(resource.batchId(), Iterables.transform(issues, new Function<DefaultIssue, ReportIssue>() {
writer.writeComponentIssues(resource.batchId(), Iterables.transform(issues, new Function<DefaultIssue, BatchOutput.ReportIssue>() {
@Override
public ReportIssue apply(DefaultIssue input) {
public BatchOutput.ReportIssue apply(DefaultIssue input) {
return toReportIssue(input);
}
}));
}
}

private ReportIssue toReportIssue(DefaultIssue issue) {
BatchResource batchResource = resourceCache.get(issue.componentKey());
return new ReportIssue()
.setKey(issue.key())
.setComponentBatchId(batchResource != null ? batchResource.batchId() : null)
.setNew(issue.isNew())
.setLine(issue.line())
.setMessage(issue.message())
.setEffortToFix(issue.effortToFix())
.setDebt(issue.debtInMinutes())
.setResolution(issue.resolution())
.setStatus(issue.status())
.setSeverity(issue.severity())
.setChecksum(issue.checksum())
.setManualSeverity(issue.manualSeverity())
.setReporter(issue.reporter())
.setAssignee(issue.assignee())
.setRuleKey(issue.ruleKey().repository(), issue.ruleKey().rule())
.setActionPlanKey(issue.actionPlanKey())
.setAttributes(KeyValueFormat.format(issue.attributes()))
.setAuthorLogin(issue.authorLogin())
.setCreationDate(issue.creationDate())
.setCloseDate(issue.closeDate())
.setUpdateDate(issue.updateDate())
.setSelectedAt(issue.selectedAt())
.setDiffFields(toString(issue.currentChange()))
.setTags(issue.tags())
.setMustSendNotification(issue.mustSendNotifications())
.setChanged(issue.isChanged());
private BatchOutput.ReportIssue toReportIssue(DefaultIssue issue) {
BatchOutput.ReportIssue.Builder builder = BatchOutput.ReportIssue.newBuilder();

// non-null fields
builder.setUuid(issue.key());
builder.setIsNew(issue.isNew());
builder.setSeverity(Constants.Severity.valueOf(issue.severity()));
builder.setRuleRepository(issue.ruleKey().repository());
builder.setRuleKey(issue.ruleKey().rule());
builder.setAttributes(KeyValueFormat.format(issue.attributes()));
builder.addAllTags(issue.tags());
builder.setMustSendNotification(issue.mustSendNotifications());
builder.setIsChanged(issue.isChanged());

// nullable fields
Integer line = issue.line();
if (line != null) {
builder.setLine(line);
}
builder.setMsg(issue.message());
if (issue.effortToFix() != null) {
builder.setEffortToFix(issue.effortToFix());
}
if (issue.debtInMinutes() != null) {
builder.setDebtInMinutes(issue.debtInMinutes());
}
if (issue.resolution() != null) {
builder.setResolution(issue.resolution());
}
if (issue.status() != null) {
builder.setStatus(issue.status());
}
if (issue.checksum() != null) {
builder.setChecksum(issue.checksum());
}
builder.setManualSeverity(issue.manualSeverity());
if (issue.reporter() != null) {
builder.setReporter(issue.reporter());
}
if (issue.assignee() != null) {
builder.setAssignee(issue.assignee());
}
if (issue.actionPlanKey() != null) {
builder.setActionPlanKey(issue.actionPlanKey());
}
if (issue.authorLogin() != null) {
builder.setAuthorLogin(issue.authorLogin());
}
String diff = diffsToString(issue.currentChange());
if (diff != null) {
builder.setDiffFields(diff);
}
Date creationDate = issue.creationDate();
if (creationDate != null) {
builder.setCreationDate(creationDate.getTime());
}
Long selectedAt = issue.selectedAt();
if (selectedAt != null) {
builder.setSelectedAt(selectedAt);
}
Date closeDate = issue.closeDate();
if (closeDate != null) {
builder.setCloseDate(closeDate.getTime());
}
Date updateDate = issue.updateDate();
if (updateDate != null) {
builder.setUpdateDate(updateDate.getTime());
}
return builder.build();
}

private String toString(FieldDiffs currentChange) {
return currentChange != null ? currentChange.toString() : null;
private String diffsToString(@Nullable FieldDiffs diffs) {
return diffs != null ? diffs.toString() : null;
}

}

+ 6
- 6
sonar-batch/src/main/java/org/sonar/batch/report/PublishReportJob.java View File

@@ -34,7 +34,7 @@ import org.sonar.api.utils.ZipUtils;
import org.sonar.batch.bootstrap.DefaultAnalysisMode;
import org.sonar.batch.bootstrap.ServerClient;
import org.sonar.batch.index.ResourceCache;
import org.sonar.batch.protocol.output.ReportHelper;
import org.sonar.batch.protocol.output.BatchOutputWriter;

import java.io.File;
import java.io.IOException;
@@ -87,19 +87,19 @@ public class PublishReportJob implements BatchComponent {
try {
long startTime = System.currentTimeMillis();
File reportDir = temp.newDir("batch-report");
ReportHelper reportHelper = ReportHelper.create(reportDir);
BatchOutputWriter writer = new BatchOutputWriter(reportDir);
for (ReportPublisher publisher : publishers) {
publisher.export(reportHelper);
publisher.publish(writer);
}
long stopTime = System.currentTimeMillis();
LOG.debug("Analysis reports generated in " + (stopTime - startTime) + "ms");
LOG.info("Analysis reports generated in " + (stopTime - startTime) + "ms, dir size=" + FileUtils.byteCountToDisplaySize(FileUtils.sizeOfDirectory(reportDir)));

startTime = System.currentTimeMillis();
File reportZip = temp.newFile("batch-report", ".zip");
ZipUtils.zipDir(reportDir, reportZip);
FileUtils.deleteDirectory(reportDir);
stopTime = System.currentTimeMillis();
LOG.debug("Analysis reports compressed in " + (stopTime - startTime) + "ms, zip size=" + FileUtils.byteCountToDisplaySize(FileUtils.sizeOf(reportZip)));
LOG.info("Analysis reports compressed in " + (stopTime - startTime) + "ms, zip size=" + FileUtils.byteCountToDisplaySize(FileUtils.sizeOf(reportZip)));
return reportZip;
} catch (IOException e) {
throw new IllegalStateException("Unable to prepare batch report", e);
@@ -135,7 +135,7 @@ public class PublishReportJob implements BatchComponent {
throw new IllegalStateException(String.format("Fail to execute request [code=%s, url=%s]: %s", responseCode, url, request.body()));
}
long stopTime = System.currentTimeMillis();
LOG.debug("Analysis reports sent to server in " + (stopTime - startTime) + "ms");
LOG.info("Analysis reports sent to server in " + (stopTime - startTime) + "ms");
}

@VisibleForTesting

+ 5
- 4
sonar-batch/src/main/java/org/sonar/batch/report/ReportPublisher.java View File

@@ -19,12 +19,13 @@
*/
package org.sonar.batch.report;

import org.sonar.batch.protocol.output.ReportHelper;

import java.io.IOException;
import org.sonar.batch.protocol.output.BatchOutputWriter;

/**
* Adds a sub-part of data to output report
*/
public interface ReportPublisher {

void export(ReportHelper reportHelper) throws IOException;
void publish(BatchOutputWriter writer);

}

+ 41
- 109
sonar-batch/src/test/java/org/sonar/batch/report/ComponentsPublisherTest.java View File

@@ -19,143 +19,75 @@
*/
package org.sonar.batch.report;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.skyscreamer.jsonassert.JSONAssert;
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.resources.Directory;
import org.sonar.api.resources.Java;
import org.sonar.api.resources.Language;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.resources.Scopes;
import org.sonar.api.utils.DateUtils;
import org.sonar.batch.index.ResourceCache;
import org.sonar.batch.protocol.output.ReportHelper;
import org.sonar.batch.protocol.output.BatchOutputWriter;
import org.sonar.batch.protocol.output.FileStructure;

import java.io.File;
import java.text.SimpleDateFormat;
import static org.assertj.core.api.Assertions.assertThat;

public class ComponentsPublisherTest {

@Rule
public TemporaryFolder temp = new TemporaryFolder();

@Test
public void testComponentPublisher() throws Exception {
ProjectReactor reactor = new ProjectReactor(ProjectDefinition.create().setKey("foo"));
ResourceCache resourceCache = new ResourceCache();
ComponentsPublisher publisher = new ComponentsPublisher(reactor, resourceCache);
ProjectReactor reactor = new ProjectReactor(ProjectDefinition.create().setKey("foo"));
ResourceCache resourceCache = new ResourceCache();
ComponentsPublisher publisher = new ComponentsPublisher(reactor, resourceCache);

Project root = new Project("foo").setName("Root project").setAnalysisDate(new SimpleDateFormat("dd/MM/yyyy").parse("12/12/2012"));
root.setId(1);
@Test
public void add_components_to_report() throws Exception {
// inputs
Project root = new Project("foo").setName("Root project")
.setAnalysisDate(DateUtils.parseDate(("2012-12-12")));
root.setId(1).setUuid("PROJECT_UUID");
resourceCache.add(root, null).setSnapshot(new Snapshot().setId(11));

Project module1 = new Project("module1").setName("Module1");
module1.setParent(root);
module1.setId(2);
module1.setId(2).setUuid("MODULE_UUID");
resourceCache.add(module1, root).setSnapshot(new Snapshot().setId(12));
Directory dir1 = Directory.create("src");
dir1.setEffectiveKey("foo:src");
dir1.setId(3);
resourceCache.add(dir1, module1).setSnapshot(new Snapshot().setId(13));
org.sonar.api.resources.File mainFile = org.sonar.api.resources.File.create("src/Foo.java", "Foo.java", Java.INSTANCE, false);
mainFile.setEffectiveKey("foo:src/Foo.java");
mainFile.setId(4);
resourceCache.add(mainFile, dir1).setSnapshot(new Snapshot().setId(14));
Directory dir2 = Directory.create("test");
dir2.setEffectiveKey("foo:test");
dir2.setId(5);
resourceCache.add(dir2, module1).setSnapshot(new Snapshot().setId(15));
org.sonar.api.resources.File testFile = org.sonar.api.resources.File.create("test/FooTest.java", "FooTest.java", Java.INSTANCE, true);
testFile.setEffectiveKey("foo:test/FooTest.java");
testFile.setId(6);
resourceCache.add(testFile, dir2).setSnapshot(new Snapshot().setId(16));

File exportDir = temp.newFolder();
ReportHelper helper = ReportHelper.create(exportDir);
publisher.export(helper);
Directory dir = Directory.create("src");
dir.setEffectiveKey("foo:src");
dir.setId(3).setUuid("DIR_UUID");
resourceCache.add(dir, module1).setSnapshot(new Snapshot().setId(13));

JSONAssert
.assertEquals(
IOUtils.toString(this.getClass().getResourceAsStream("ComponentsPublisherTest/expected.json"), "UTF-8"),
FileUtils.readFileToString(new File(exportDir, "components.json")), true);
}
org.sonar.api.resources.File file = org.sonar.api.resources.File.create("src/Foo.java", "Foo.java", Java.INSTANCE, false);
file.setEffectiveKey("foo:src/Foo.java");
file.setId(4).setUuid("FILE_UUID");
resourceCache.add(file, dir).setSnapshot(new Snapshot().setId(14));

@Test
public void testComponentPublisher_containing_file_without_language() throws Exception {
ProjectReactor reactor = new ProjectReactor(ProjectDefinition.create().setKey("ALL_PROJECT"));
ResourceCache resourceCache = new ResourceCache();
ComponentsPublisher publisher = new ComponentsPublisher(reactor, resourceCache);

View view = new View("ALL_PROJECT");
view.setId(1);
view.setAnalysisDate(new SimpleDateFormat("dd/MM/yyyy").parse("12/12/2012"));
resourceCache.add(view, null).setSnapshot(new Snapshot().setId(11));

org.sonar.api.resources.File mainFile = org.sonar.api.resources.File.create("ALL_PROJECTsample", "ALL_PROJECTsample", null, false);
mainFile.setEffectiveKey("ALL_PROJECTsample");
mainFile.setId(2);
resourceCache.add(mainFile, view).setSnapshot(new Snapshot().setId(12));
org.sonar.api.resources.File fileWithoutLang = org.sonar.api.resources.File.create("src/make", "make", null, false);
fileWithoutLang.setEffectiveKey("foo:src/make");
fileWithoutLang.setId(5).setUuid("FILE_WITHOUT_LANG_UUID");
resourceCache.add(fileWithoutLang, dir).setSnapshot(new Snapshot().setId(15));

File exportDir = temp.newFolder();
ReportHelper helper = ReportHelper.create(exportDir);
publisher.export(helper);

JSONAssert
.assertEquals(
IOUtils.toString(this.getClass().getResourceAsStream("ComponentsPublisherTest/testComponentPublisher_containing_file_without_language.json"), "UTF-8"),
FileUtils.readFileToString(new File(exportDir, "components.json")), true);
}

private static class View extends Project {

private View(String key) {
super(key);
}

@Override
public String getName() {
return "All Projects";
}

@Override
public String getLongName() {
return null;
}

@Override
public String getDescription() {
return null;
}

@Override
public Language getLanguage() {
return null;
}

@Override
public String getScope() {
return Scopes.PROJECT;
}
org.sonar.api.resources.File testFile = org.sonar.api.resources.File.create("test/FooTest.java", "FooTest.java", Java.INSTANCE, true);
testFile.setEffectiveKey("foo:test/FooTest.java");
testFile.setId(6).setUuid("TEST_FILE_UUID");
resourceCache.add(testFile, dir).setSnapshot(new Snapshot().setId(16));

@Override
public String getQualifier() {
return Qualifiers.VIEW;
}
BatchOutputWriter writer = new BatchOutputWriter(temp.newFolder());
publisher.publish(writer);

@Override
public Project getParent() {
return null;
}
assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 1)).isTrue();
assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 2)).isTrue();
assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 3)).isTrue();
assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 4)).isTrue();
assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 5)).isTrue();
assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 6)).isTrue();

@Override
public boolean matchFilePattern(String antPattern) {
return false;
}
// no such reference
assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 7)).isFalse();
}

}

+ 0
- 59
sonar-batch/src/test/resources/org/sonar/batch/report/ComponentsPublisherTest/expected.json View File

@@ -1,59 +0,0 @@
{
"analysisDate": "2012-12-12T00:00:00+0100",
"root": {
"batchId": 1,
"id": 1,
"snapshotId": 11,
"name": "Root project",
"type": "PRJ",
"children": [
{
"batchId": 2,
"id": 2,
"snapshotId": 12,
"name": "Module1",
"type": "MOD",
"children": [
{
"batchId": 3,
"id": 3,
"snapshotId": 13,
"path": "src",
"type": "DIR",
"children": [
{
"batchId": 4,
"id": 4,
"snapshotId": 14,
"path": "src/Foo.java",
"type": "FIL",
"isTest": false,
"languageKey": "java",
"children": []
}
]
},
{
"batchId": 5,
"id": 5,
"snapshotId": 15,
"path": "test",
"type": "DIR",
"children": [
{
"batchId": 6,
"id": 6,
"snapshotId": 16,
"path": "test/FooTest.java",
"type": "FIL",
"isTest": true,
"languageKey": "java",
"children": []
}
]
}
]
}
]
}
}

+ 0
- 21
sonar-batch/src/test/resources/org/sonar/batch/report/ComponentsPublisherTest/testComponentPublisher_containing_file_without_language.json View File

@@ -1,21 +0,0 @@
{
"analysisDate": "2012-12-12T00:00:00+0100",
"root": {
"batchId": 1,
"id": 1,
"snapshotId": 11,
"name": "All Projects",
"type": "VIEW",
"children": [
{
"batchId": 2,
"id": 2,
"snapshotId": 12,
"path": "ALL_PROJECTsample",
"type": "FIL",
"isTest": false,
"children": []
}
]
}
}

+ 15
- 4
sonar-plugin-api/src/test/java/org/sonar/api/utils/ZipUtilsTest.java View File

@@ -21,7 +21,9 @@ package org.sonar.api.utils;

import com.google.common.collect.Iterators;
import org.apache.commons.io.FileUtils;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

import java.io.File;
import java.io.IOException;
@@ -34,24 +36,33 @@ import static org.assertj.core.api.Assertions.assertThat;

public class ZipUtilsTest {

@Rule
public TemporaryFolder temp = new TemporaryFolder();

@Test
public void shouldZipDirectory() throws IOException {
File foo = FileUtils.toFile(getClass().getResource("/org/sonar/api/utils/ZipUtilsTest/shouldZipDirectory/foo.txt"));
File dir = foo.getParentFile();
File zip = new File("target/tmp/shouldZipDirectory.zip");
File zip = temp.newFile();

ZipUtils.zipDir(dir, zip);

assertThat(zip).exists();
assertThat(zip).exists().isFile();
assertThat(zip.length()).isGreaterThan(1L);
Iterator<? extends ZipEntry> zipEntries = Iterators.forEnumeration(new ZipFile(zip).entries());
assertThat(zipEntries).hasSize(4);

File unzipDir = temp.newFolder();
ZipUtils.unzip(zip, unzipDir);
assertThat(new File(unzipDir, "bar.txt")).exists().isFile();
assertThat(new File(unzipDir, "foo.txt")).exists().isFile();
assertThat(new File(unzipDir, "dir1/hello.properties")).exists().isFile();
}

@Test
public void shouldUnzipFile() throws IOException {
File zip = FileUtils.toFile(getClass().getResource("/org/sonar/api/utils/ZipUtilsTest/shouldUnzipFile.zip"));
File toDir = new File("target/tmp/shouldUnzipFile/");
File toDir = temp.newFolder();
ZipUtils.unzip(zip, toDir);
assertThat(toDir.list()).hasSize(3);
}
@@ -59,7 +70,7 @@ public class ZipUtilsTest {
@Test
public void should_unzip_stream_file() throws Exception {
InputStream zip = getClass().getResource("/org/sonar/api/utils/ZipUtilsTest/shouldUnzipFile.zip").openStream();
File toDir = new File("target/tmp/shouldUnzipStreamFile/");
File toDir = temp.newFolder();
ZipUtils.unzip(zip, toDir);
assertThat(toDir.list()).hasSize(3);
}

Loading…
Cancel
Save