@@ -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> |
@@ -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; | |||
} | |||
} |
@@ -41,7 +41,6 @@ public class ComputationComponents { | |||
return Arrays.asList( | |||
ComputationService.class, | |||
ComputationSteps.class, | |||
AnalysisReportService.class, | |||
// issues | |||
ScmAccountCacheLoader.class, |
@@ -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; | |||
} | |||
} |
@@ -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() + ")"); | |||
} | |||
} |
@@ -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) { |
@@ -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, |
@@ -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 |
@@ -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); | |||
// } | |||
// } | |||
// | |||
//} |
@@ -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); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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"); |
@@ -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)); | |||
} | |||
} |
@@ -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); | |||
@@ -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); | |||
@@ -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); | |||
} |
@@ -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 |
@@ -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> |
@@ -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) | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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"); | |||
} | |||
} |
@@ -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"); | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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)); | |||
} | |||
} |
@@ -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 { | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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"); | |||
} | |||
} |
@@ -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; | |||
} | |||
@@ -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); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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 |
@@ -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); | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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": [] | |||
} | |||
] | |||
} | |||
] | |||
} | |||
] | |||
} | |||
} |
@@ -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": [] | |||
} | |||
] | |||
} | |||
} |
@@ -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); | |||
} |