diff options
author | Simon Brandhof <simon.brandhof@sonarsource.com> | 2015-07-22 14:17:06 +0200 |
---|---|---|
committer | Simon Brandhof <simon.brandhof@sonarsource.com> | 2015-07-22 14:17:16 +0200 |
commit | c98aca6054048d0293fdb713910b4fa98beaa524 (patch) | |
tree | 2335acba0b9ecab3115dc6a49e67db4431dcd53b | |
parent | 5539cfffd327697a49278361901335e2d4d6e075 (diff) | |
download | sonarqube-c98aca6054048d0293fdb713910b4fa98beaa524.tar.gz sonarqube-c98aca6054048d0293fdb713910b4fa98beaa524.zip |
Improve utility org.sonar.core.util.Protobuf
13 files changed, 992 insertions, 240 deletions
diff --git a/compile_protobuf.sh b/compile_protobuf.sh new file mode 100755 index 00000000000..8e07c4dc2cd --- /dev/null +++ b/compile_protobuf.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +# Usage: compile_protobuf <inputDir> <outputDir> +function compile_protobuf { + echo "Compiling [$1] to [$2]..." + mkdir -p $2 + protoc --proto_path=$1 --java_out=$2 $1/*.proto +} + +compile_protobuf "sonar-core/src/test/protobuf" "sonar-core/src/test/gen-java" +compile_protobuf "sonar-batch-protocol/src/main/protobuf" "sonar-batch-protocol/src/main/gen-java" + + + diff --git a/sonar-batch-protocol/compile_protobuf.sh b/sonar-batch-protocol/compile_protobuf.sh deleted file mode 100755 index f2d14c4eebc..00000000000 --- a/sonar-batch-protocol/compile_protobuf.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/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 diff --git a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/ProtobufUtil.java b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/ProtobufUtil.java deleted file mode 100644 index 43d8af6bfbf..00000000000 --- a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/ProtobufUtil.java +++ /dev/null @@ -1,121 +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; - -import com.google.protobuf.InvalidProtocolBufferException; -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.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import org.apache.commons.io.IOUtils; -import org.sonar.core.util.CloseableIterator; - -import static java.lang.String.format; - -public class ProtobufUtil { - private ProtobufUtil() { - // only static stuff - } - - /** - * Returns the message contained in the given file. Throws an unchecked exception - * if the file does not exist or is empty. - */ - public static <MSG extends Message> MSG readFile(File file, Parser<MSG> parser) { - InputStream input = null; - try { - input = new BufferedInputStream(new FileInputStream(file)); - return parser.parseFrom(input); - } catch (IOException e) { - throw new IllegalStateException(format("Unable to read file %s", file), e); - } finally { - IOUtils.closeQuietly(input); - } - } - - /** - * Writes a single message to a file, by replacing existing content. The message is - * NOT appended. - */ - public static void writeToFile(Message message, File toFile) { - OutputStream out = null; - try { - out = new BufferedOutputStream(new FileOutputStream(toFile, false)); - message.writeTo(out); - } catch (IOException e) { - throw new IllegalStateException(format("Unable to write protobuf message to file %s", toFile), e); - } finally { - IOUtils.closeQuietly(out); - } - } - - public static void writeStreamToFile(Iterable<? extends Message> messages, File toFile, boolean append) { - OutputStream out = null; - try { - out = new BufferedOutputStream(new FileOutputStream(toFile, append)); - for (Message message : messages) { - message.writeDelimitedTo(out); - } - } catch (IOException e) { - throw new IllegalStateException(format("Unable to write protobuf messages to file %s", toFile), e); - } finally { - IOUtils.closeQuietly(out); - } - } - - public static <MSG extends Message> CloseableIterator<MSG> readStreamFromFile(File file, Parser<MSG> parser) { - try { - return new ProtobufIterator<>(parser, new BufferedInputStream(new FileInputStream(file))); - } catch (FileNotFoundException e) { - throw new IllegalStateException(format("Unable to read protobuf file %s", file), e); - } - } - - private static class ProtobufIterator<MSG extends Message> extends CloseableIterator<MSG> { - private final Parser<MSG> parser; - private final InputStream input; - - private ProtobufIterator(Parser<MSG> parser, InputStream input) { - this.parser = parser; - this.input = input; - } - - @Override - protected MSG doNext() { - try { - return parser.parseDelimitedFrom(input); - } catch (InvalidProtocolBufferException e) { - throw new IllegalStateException(e); - } - } - - @Override - protected void doClose() throws Exception { - IOUtils.closeQuietly(input); - } - } -} diff --git a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/BatchReportReader.java b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/BatchReportReader.java index 70da7179b9f..9cc90da20c5 100644 --- a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/BatchReportReader.java +++ b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/BatchReportReader.java @@ -21,8 +21,8 @@ package org.sonar.batch.protocol.output; import java.io.File; import javax.annotation.CheckForNull; -import org.sonar.batch.protocol.ProtobufUtil; import org.sonar.core.util.CloseableIterator; +import org.sonar.core.util.Protobuf; public class BatchReportReader { @@ -37,7 +37,7 @@ public class BatchReportReader { if (!fileExists(file)) { throw new IllegalStateException("Metadata file is missing in analysis report: " + file); } - return ProtobufUtil.readFile(file, BatchReport.Metadata.PARSER); + return Protobuf.read(file, BatchReport.Metadata.PARSER); } public CloseableIterator<BatchReport.ActiveRule> readActiveRules() { @@ -45,13 +45,13 @@ public class BatchReportReader { if (!fileExists(file)) { return CloseableIterator.emptyCloseableIterator(); } - return ProtobufUtil.readStreamFromFile(file, BatchReport.ActiveRule.PARSER); + return Protobuf.readStream(file, BatchReport.ActiveRule.PARSER); } public CloseableIterator<BatchReport.Measure> readComponentMeasures(int componentRef) { File file = fileStructure.fileFor(FileStructure.Domain.MEASURES, componentRef); if (fileExists(file)) { - return ProtobufUtil.readStreamFromFile(file, BatchReport.Measure.PARSER); + return Protobuf.readStream(file, BatchReport.Measure.PARSER); } return CloseableIterator.emptyCloseableIterator(); } @@ -60,7 +60,7 @@ public class BatchReportReader { public BatchReport.Changesets readChangesets(int componentRef) { File file = fileStructure.fileFor(FileStructure.Domain.CHANGESETS, componentRef); if (fileExists(file)) { - return ProtobufUtil.readFile(file, BatchReport.Changesets.PARSER); + return Protobuf.read(file, BatchReport.Changesets.PARSER); } return null; } @@ -70,13 +70,13 @@ public class BatchReportReader { if (!fileExists(file)) { throw new IllegalStateException("Unable to find report for component #" + componentRef + ". File does not exist: " + file); } - return ProtobufUtil.readFile(file, BatchReport.Component.PARSER); + return Protobuf.read(file, BatchReport.Component.PARSER); } public CloseableIterator<BatchReport.Issue> readComponentIssues(int componentRef) { File file = fileStructure.fileFor(FileStructure.Domain.ISSUES, componentRef); if (fileExists(file)) { - return ProtobufUtil.readStreamFromFile(file, BatchReport.Issue.PARSER); + return Protobuf.readStream(file, BatchReport.Issue.PARSER); } return CloseableIterator.emptyCloseableIterator(); } @@ -84,7 +84,7 @@ public class BatchReportReader { public CloseableIterator<BatchReport.Duplication> readComponentDuplications(int componentRef) { File file = fileStructure.fileFor(FileStructure.Domain.DUPLICATIONS, componentRef); if (fileExists(file)) { - return ProtobufUtil.readStreamFromFile(file, BatchReport.Duplication.PARSER); + return Protobuf.readStream(file, BatchReport.Duplication.PARSER); } return CloseableIterator.emptyCloseableIterator(); } @@ -92,7 +92,7 @@ public class BatchReportReader { public CloseableIterator<BatchReport.Symbol> readComponentSymbols(int componentRef) { File file = fileStructure.fileFor(FileStructure.Domain.SYMBOLS, componentRef); if (fileExists(file)) { - return ProtobufUtil.readStreamFromFile(file, BatchReport.Symbol.PARSER); + return Protobuf.readStream(file, BatchReport.Symbol.PARSER); } return CloseableIterator.emptyCloseableIterator(); } diff --git a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/BatchReportWriter.java b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/BatchReportWriter.java index b7ac04ed116..bf0cdfcd736 100644 --- a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/BatchReportWriter.java +++ b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/BatchReportWriter.java @@ -20,7 +20,7 @@ package org.sonar.batch.protocol.output; import java.io.File; -import org.sonar.batch.protocol.ProtobufUtil; +import org.sonar.core.util.Protobuf; public class BatchReportWriter { @@ -46,72 +46,72 @@ public class BatchReportWriter { * Metadata is mandatory */ public File writeMetadata(BatchReport.Metadata metadata) { - ProtobufUtil.writeToFile(metadata, fileStructure.metadataFile()); + Protobuf.write(metadata, fileStructure.metadataFile()); return fileStructure.metadataFile(); } public File writeActiveRules(Iterable<BatchReport.ActiveRule> activeRules) { - ProtobufUtil.writeStreamToFile(activeRules, fileStructure.activeRules(), false); + Protobuf.writeStream(activeRules, fileStructure.activeRules(), false); return fileStructure.metadataFile(); } public File writeComponent(BatchReport.Component component) { File file = fileStructure.fileFor(FileStructure.Domain.COMPONENT, component.getRef()); - ProtobufUtil.writeToFile(component, file); + Protobuf.write(component, file); return file; } public File writeComponentIssues(int componentRef, Iterable<BatchReport.Issue> issues) { File file = fileStructure.fileFor(FileStructure.Domain.ISSUES, componentRef); - ProtobufUtil.writeStreamToFile(issues, file, false); + Protobuf.writeStream(issues, file, false); return file; } public File writeComponentMeasures(int componentRef, Iterable<BatchReport.Measure> measures) { File file = fileStructure.fileFor(FileStructure.Domain.MEASURES, componentRef); - ProtobufUtil.writeStreamToFile(measures, file, false); + Protobuf.writeStream(measures, file, false); return file; } public File writeComponentChangesets(BatchReport.Changesets changesets) { File file = fileStructure.fileFor(FileStructure.Domain.CHANGESETS, changesets.getComponentRef()); - ProtobufUtil.writeToFile(changesets, file); + Protobuf.write(changesets, file); return file; } public File writeComponentDuplications(int componentRef, Iterable<BatchReport.Duplication> duplications) { File file = fileStructure.fileFor(FileStructure.Domain.DUPLICATIONS, componentRef); - ProtobufUtil.writeStreamToFile(duplications, file, false); + Protobuf.writeStream(duplications, file, false); return file; } public File writeComponentSymbols(int componentRef, Iterable<BatchReport.Symbol> symbols) { File file = fileStructure.fileFor(FileStructure.Domain.SYMBOLS, componentRef); - ProtobufUtil.writeStreamToFile(symbols, file, false); + Protobuf.writeStream(symbols, file, false); return file; } public File writeComponentSyntaxHighlighting(int componentRef, Iterable<BatchReport.SyntaxHighlighting> syntaxHighlightingRules) { File file = fileStructure.fileFor(FileStructure.Domain.SYNTAX_HIGHLIGHTINGS, componentRef); - ProtobufUtil.writeStreamToFile(syntaxHighlightingRules, file, false); + Protobuf.writeStream(syntaxHighlightingRules, file, false); return file; } public File writeComponentCoverage(int componentRef, Iterable<BatchReport.Coverage> coverageList) { File file = fileStructure.fileFor(FileStructure.Domain.COVERAGES, componentRef); - ProtobufUtil.writeStreamToFile(coverageList, file, false); + Protobuf.writeStream(coverageList, file, false); return file; } public File writeTests(int componentRef, Iterable<BatchReport.Test> tests) { File file = fileStructure.fileFor(FileStructure.Domain.TESTS, componentRef); - ProtobufUtil.writeStreamToFile(tests, file, false); + Protobuf.writeStream(tests, file, false); return file; } public File writeCoverageDetails(int componentRef, Iterable<BatchReport.CoverageDetail> tests) { File file = fileStructure.fileFor(FileStructure.Domain.COVERAGE_DETAILS, componentRef); - ProtobufUtil.writeStreamToFile(tests, file, false); + Protobuf.writeStream(tests, file, false); return file; } diff --git a/sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/ProtobufUtilTest.java b/sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/ProtobufUtilTest.java deleted file mode 100644 index 04ebcf7f989..00000000000 --- a/sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/ProtobufUtilTest.java +++ /dev/null @@ -1,71 +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; - -import java.io.File; -import org.apache.commons.io.FileUtils; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.rules.TemporaryFolder; -import org.sonar.batch.protocol.output.BatchReport; -import org.sonar.test.TestUtils; - -import static org.assertj.core.api.Assertions.assertThat; - -public class ProtobufUtilTest { - - @Rule - public ExpectedException thrown = ExpectedException.none(); - - @Rule - public TemporaryFolder temp = new TemporaryFolder(); - - @Test - public void only_utils() { - assertThat(TestUtils.hasOnlyPrivateConstructors(ProtobufUtil.class)); - } - - @Test - public void readFile_fails_if_file_does_not_exist() throws Exception { - thrown.expect(IllegalStateException.class); - - File file = temp.newFile(); - FileUtils.forceDelete(file); - ProtobufUtil.readFile(file, BatchReport.Metadata.PARSER); - } - - @Test - public void readFile_returns_empty_message_if_file_is_empty() throws Exception { - File file = temp.newFile(); - BatchReport.Metadata msg = ProtobufUtil.readFile(file, BatchReport.Metadata.PARSER); - assertThat(msg).isNotNull(); - assertThat(msg.isInitialized()).isTrue(); - } - - @Test - public void readFile_returns_message() throws Exception { - File file = temp.newFile(); - ProtobufUtil.writeToFile(BatchReport.Metadata.getDefaultInstance(), file); - BatchReport.Metadata message = ProtobufUtil.readFile(file, BatchReport.Metadata.PARSER); - assertThat(message).isNotNull(); - assertThat(message.isInitialized()).isTrue(); - } -} diff --git a/sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/output/BatchReportWriterTest.java b/sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/output/BatchReportWriterTest.java index e19e044d2c5..ae1a6cd143e 100644 --- a/sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/output/BatchReportWriterTest.java +++ b/sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/output/BatchReportWriterTest.java @@ -28,9 +28,9 @@ 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 org.sonar.batch.protocol.output.BatchReport.Range; import org.sonar.core.util.CloseableIterator; +import org.sonar.core.util.Protobuf; import static org.assertj.core.api.Assertions.assertThat; @@ -63,7 +63,7 @@ public class BatchReportWriterTest { .setRootComponentRef(1); underTest.writeMetadata(metadata.build()); - BatchReport.Metadata read = ProtobufUtil.readFile(underTest.getFileStructure().metadataFile(), BatchReport.Metadata.PARSER); + BatchReport.Metadata read = Protobuf.read(underTest.getFileStructure().metadataFile(), BatchReport.Metadata.PARSER); assertThat(read.getAnalysisDate()).isEqualTo(15000000L); assertThat(read.getProjectKey()).isEqualTo("PROJECT_A"); assertThat(read.getRootComponentRef()).isEqualTo(1); @@ -88,7 +88,7 @@ public class BatchReportWriterTest { assertThat(underTest.hasComponentData(FileStructure.Domain.COMPONENT, 1)).isTrue(); File file = underTest.getFileStructure().fileFor(FileStructure.Domain.COMPONENT, 1); assertThat(file).exists().isFile(); - BatchReport.Component read = ProtobufUtil.readFile(file, BatchReport.Component.PARSER); + BatchReport.Component read = Protobuf.read(file, BatchReport.Component.PARSER); assertThat(read.getRef()).isEqualTo(1); assertThat(read.getChildRefList()).containsOnly(5, 42); assertThat(read.hasName()).isFalse(); @@ -111,7 +111,7 @@ public class BatchReportWriterTest { assertThat(underTest.hasComponentData(FileStructure.Domain.ISSUES, 1)).isTrue(); File file = underTest.getFileStructure().fileFor(FileStructure.Domain.ISSUES, 1); assertThat(file).exists().isFile(); - try (CloseableIterator<BatchReport.Issue> read = ProtobufUtil.readStreamFromFile(file, BatchReport.Issue.PARSER)) { + try (CloseableIterator<BatchReport.Issue> read = Protobuf.readStream(file, BatchReport.Issue.PARSER)) { assertThat(Iterators.size(read)).isEqualTo(1); } } @@ -131,7 +131,7 @@ public class BatchReportWriterTest { assertThat(underTest.hasComponentData(FileStructure.Domain.MEASURES, 1)).isTrue(); File file = underTest.getFileStructure().fileFor(FileStructure.Domain.MEASURES, 1); assertThat(file).exists().isFile(); - try (CloseableIterator<BatchReport.Measure> read = ProtobufUtil.readStreamFromFile(file, BatchReport.Measure.PARSER)) { + try (CloseableIterator<BatchReport.Measure> read = Protobuf.readStream(file, BatchReport.Measure.PARSER)) { assertThat(Iterators.size(read)).isEqualTo(1); } } @@ -154,7 +154,7 @@ public class BatchReportWriterTest { assertThat(underTest.hasComponentData(FileStructure.Domain.CHANGESETS, 1)).isTrue(); File file = underTest.getFileStructure().fileFor(FileStructure.Domain.CHANGESETS, 1); assertThat(file).exists().isFile(); - BatchReport.Changesets read = ProtobufUtil.readFile(file, BatchReport.Changesets.PARSER); + BatchReport.Changesets read = Protobuf.read(file, BatchReport.Changesets.PARSER); assertThat(read.getComponentRef()).isEqualTo(1); assertThat(read.getChangesetCount()).isEqualTo(1); assertThat(read.getChangesetList()).hasSize(1); @@ -184,7 +184,7 @@ public class BatchReportWriterTest { assertThat(underTest.hasComponentData(FileStructure.Domain.DUPLICATIONS, 1)).isTrue(); File file = underTest.getFileStructure().fileFor(FileStructure.Domain.DUPLICATIONS, 1); assertThat(file).exists().isFile(); - try (CloseableIterator<BatchReport.Duplication> duplications = ProtobufUtil.readStreamFromFile(file, BatchReport.Duplication.PARSER)) { + try (CloseableIterator<BatchReport.Duplication> duplications = Protobuf.readStream(file, BatchReport.Duplication.PARSER)) { BatchReport.Duplication dup = duplications.next(); assertThat(dup.getOriginPosition()).isNotNull(); assertThat(dup.getDuplicateList()).hasSize(1); @@ -218,7 +218,7 @@ public class BatchReportWriterTest { File file = underTest.getFileStructure().fileFor(FileStructure.Domain.SYMBOLS, 1); assertThat(file).exists().isFile(); - try (CloseableIterator<BatchReport.Symbol> read = ProtobufUtil.readStreamFromFile(file, BatchReport.Symbol.PARSER)) { + try (CloseableIterator<BatchReport.Symbol> read = Protobuf.readStream(file, BatchReport.Symbol.PARSER)) { assertThat(read).hasSize(1); } } @@ -235,8 +235,7 @@ public class BatchReportWriterTest { .setEndLine(1) .build()) .setType(Constants.HighlightingType.ANNOTATION) - .build() - )); + .build())); assertThat(underTest.hasComponentData(FileStructure.Domain.SYNTAX_HIGHLIGHTINGS, 1)).isTrue(); } @@ -255,8 +254,7 @@ public class BatchReportWriterTest { .setUtCoveredConditions(1) .setItCoveredConditions(1) .setOverallCoveredConditions(1) - .build() - )); + .build())); assertThat(underTest.hasComponentData(FileStructure.Domain.COVERAGES, 1)).isTrue(); } @@ -266,8 +264,7 @@ public class BatchReportWriterTest { assertThat(underTest.hasComponentData(FileStructure.Domain.TESTS, 1)).isFalse(); underTest.writeTests(1, Arrays.asList( - BatchReport.Test.getDefaultInstance() - )); + BatchReport.Test.getDefaultInstance())); assertThat(underTest.hasComponentData(FileStructure.Domain.TESTS, 1)).isTrue(); @@ -278,8 +275,7 @@ public class BatchReportWriterTest { assertThat(underTest.hasComponentData(FileStructure.Domain.COVERAGE_DETAILS, 1)).isFalse(); underTest.writeCoverageDetails(1, Arrays.asList( - BatchReport.CoverageDetail.getDefaultInstance() - )); + BatchReport.CoverageDetail.getDefaultInstance())); assertThat(underTest.hasComponentData(FileStructure.Domain.COVERAGE_DETAILS, 1)).isTrue(); } diff --git a/sonar-batch/src/test/java/org/sonar/batch/report/MeasuresPublisherTest.java b/sonar-batch/src/test/java/org/sonar/batch/report/MeasuresPublisherTest.java index 5ee11f7cdf9..a14389648e6 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/report/MeasuresPublisherTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/report/MeasuresPublisherTest.java @@ -22,6 +22,7 @@ package org.sonar.batch.report; import java.io.File; import java.util.Collections; import java.util.Date; +import org.apache.commons.lang.exception.ExceptionUtils; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -42,6 +43,7 @@ import org.sonar.core.util.CloseableIterator; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -106,16 +108,18 @@ public class MeasuresPublisherTest { @Test public void fail_with_IAE_when_measure_has_no_value() throws Exception { - thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("Measure on metric 'coverage' and component 'foo:src/Foo.php' has no value, but it's not allowed"); - Measure measure = new Measure<>(CoreMetrics.COVERAGE); when(measureCache.byResource(sampleFile)).thenReturn(Collections.singletonList(measure)); File outputDir = temp.newFolder(); BatchReportWriter writer = new BatchReportWriter(outputDir); - publisher.publish(writer); + try { + publisher.publish(writer); + fail(); + } catch (RuntimeException e) { + assertThat(ExceptionUtils.getFullStackTrace(e)).contains("Measure on metric 'coverage' and component 'foo:src/Foo.php' has no value, but it's not allowed"); + } } } diff --git a/sonar-core/pom.xml b/sonar-core/pom.xml index 2f9a09ab9d8..010524b4614 100644 --- a/sonar-core/pom.xml +++ b/sonar-core/pom.xml @@ -28,6 +28,10 @@ <artifactId>picocontainer</artifactId> </dependency> <dependency> + <groupId>com.google.protobuf</groupId> + <artifactId>protobuf-java</artifactId> + </dependency> + <dependency> <groupId>org.codehaus.sonar</groupId> <artifactId>sonar-classloader</artifactId> </dependency> @@ -107,6 +111,25 @@ <build> <plugins> <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <executions> + <execution> + <id>add-test-source</id> + <phase>generate-test-sources</phase> + <goals> + <goal>add-test-source</goal> + </goals> + <configuration> + <sources> + <source>src/test/gen-java</source> + </sources> + </configuration> + </execution> + </executions> + </plugin> + + <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <executions> diff --git a/sonar-core/src/main/java/org/sonar/core/util/Protobuf.java b/sonar-core/src/main/java/org/sonar/core/util/Protobuf.java new file mode 100644 index 00000000000..7b8b25c6e73 --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/util/Protobuf.java @@ -0,0 +1,162 @@ +/* + * 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.core.util; + +import com.google.protobuf.InvalidProtocolBufferException; +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.InputStream; +import java.io.OutputStream; +import org.apache.commons.io.IOUtils; + +/** + * Utility to read and write Protocol Buffers messages + */ +public class Protobuf { + private Protobuf() { + // only static stuff + } + + /** + * Returns the message contained in {@code file}. Throws an unchecked exception + * if the file does not exist, is empty or does not contain message with the + * expected type. + */ + public static <MSG extends Message> MSG read(File file, Parser<MSG> parser) { + InputStream input = null; + try { + input = new BufferedInputStream(new FileInputStream(file)); + return parser.parseFrom(input); + } catch (Exception e) { + throw ContextException.of("Unable to read message", e).addContext("file", file); + } finally { + IOUtils.closeQuietly(input); + } + } + + /** + * Writes a single message to {@code file}. Existing content is replaced, the message is not + * appended. + */ + public static void write(Message message, File toFile) { + OutputStream out = null; + try { + out = new BufferedOutputStream(new FileOutputStream(toFile, false)); + message.writeTo(out); + } catch (Exception e) { + throw ContextException.of("Unable to write message", e).addContext("file", toFile); + } finally { + IOUtils.closeQuietly(out); + } + } + + /** + * Streams multiple messages to {@code file}. Reading the messages back requires to + * call methods {@code readStream(...)}. + * <p> + * See https://developers.google.com/protocol-buffers/docs/techniques#streaming + * </p> + */ + public static <MSG extends Message> void writeStream(Iterable<MSG> messages, File toFile, boolean append) { + OutputStream out = null; + try { + out = new BufferedOutputStream(new FileOutputStream(toFile, append)); + writeStream(messages, out); + } catch (Exception e) { + throw ContextException.of("Unable to write messages", e).addContext("file", toFile); + } finally { + IOUtils.closeQuietly(out); + } + } + + /** + * Streams multiple messages to {@code output}. Reading the messages back requires to + * call methods {@code readStream(...)}. + * <p> + * See https://developers.google.com/protocol-buffers/docs/techniques#streaming + * </p> + */ + public static <MSG extends Message> void writeStream(Iterable<MSG> messages, OutputStream output) { + try { + for (Message message : messages) { + message.writeDelimitedTo(output); + } + } catch (Exception e) { + throw ContextException.of("Unable to write messages", e); + } + } + + /** + * Reads a stream of messages. This method returns an empty iterator if there are no messages. An + * exception is raised on IO error, if file does not exist or if messages have a + * different type than {@code parser}. + */ + public static <MSG extends Message> CloseableIterator<MSG> readStream(File file, Parser<MSG> parser) { + try { + // the input stream is closed by the CloseableIterator + BufferedInputStream input = new BufferedInputStream(new FileInputStream(file)); + return readStream(input, parser); + } catch (Exception e) { + throw ContextException.of("Unable to read messages", e).addContext("file", file); + } + } + + /** + * Reads a stream of messages. This method returns an empty iterator if there are no messages. An + * exception is raised on IO error or if messages have a different type than {@code parser}. + * <p> + * The stream is not closed by this method. It is closed when {@link CloseableIterator} traverses + * all messages or when {@link CloseableIterator#close()} is called. + * </p> + */ + public static <MSG extends Message> CloseableIterator<MSG> readStream(InputStream input, Parser<MSG> parser) { + // the stream is closed by the CloseableIterator + return new StreamIterator<>(parser, input); + } + + private static class StreamIterator<MSG extends Message> extends CloseableIterator<MSG> { + private final Parser<MSG> parser; + private final InputStream input; + + private StreamIterator(Parser<MSG> parser, InputStream input) { + this.parser = parser; + this.input = input; + } + + @Override + protected MSG doNext() { + try { + return parser.parsePartialDelimitedFrom(input); + } catch (InvalidProtocolBufferException e) { + throw ContextException.of(e); + } + } + + @Override + protected void doClose() { + IOUtils.closeQuietly(input); + } + } +} diff --git a/sonar-core/src/test/gen-java/org/sonar/core/test/Test.java b/sonar-core/src/test/gen-java/org/sonar/core/test/Test.java new file mode 100644 index 00000000000..9797ed15898 --- /dev/null +++ b/sonar-core/src/test/gen-java/org/sonar/core/test/Test.java @@ -0,0 +1,603 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: test.proto + +package org.sonar.core.test; + +public final class Test { + private Test() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + } + public interface FakeOrBuilder extends + // @@protoc_insertion_point(interface_extends:Fake) + com.google.protobuf.MessageOrBuilder { + + /** + * <code>optional string key = 1;</code> + */ + boolean hasKey(); + /** + * <code>optional string key = 1;</code> + */ + java.lang.String getKey(); + /** + * <code>optional string key = 1;</code> + */ + com.google.protobuf.ByteString + getKeyBytes(); + + /** + * <code>optional int32 line = 2;</code> + */ + boolean hasLine(); + /** + * <code>optional int32 line = 2;</code> + */ + int getLine(); + } + /** + * Protobuf type {@code Fake} + */ + public static final class Fake extends + com.google.protobuf.GeneratedMessage implements + // @@protoc_insertion_point(message_implements:Fake) + FakeOrBuilder { + // Use Fake.newBuilder() to construct. + private Fake(com.google.protobuf.GeneratedMessage.Builder<?> builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Fake(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Fake defaultInstance; + public static Fake getDefaultInstance() { + return defaultInstance; + } + + public Fake getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Fake( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + com.google.protobuf.ByteString bs = input.readBytes(); + bitField0_ |= 0x00000001; + key_ = bs; + break; + } + case 16: { + bitField0_ |= 0x00000002; + line_ = input.readInt32(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.sonar.core.test.Test.internal_static_Fake_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.sonar.core.test.Test.internal_static_Fake_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.sonar.core.test.Test.Fake.class, org.sonar.core.test.Test.Fake.Builder.class); + } + + public static com.google.protobuf.Parser<Fake> PARSER = + new com.google.protobuf.AbstractParser<Fake>() { + public Fake parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Fake(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser<Fake> getParserForType() { + return PARSER; + } + + private int bitField0_; + public static final int KEY_FIELD_NUMBER = 1; + private java.lang.Object key_; + /** + * <code>optional string key = 1;</code> + */ + public boolean hasKey() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * <code>optional string key = 1;</code> + */ + public java.lang.String getKey() { + java.lang.Object ref = key_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + key_ = s; + } + return s; + } + } + /** + * <code>optional string key = 1;</code> + */ + public com.google.protobuf.ByteString + getKeyBytes() { + java.lang.Object ref = key_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + key_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int LINE_FIELD_NUMBER = 2; + private int line_; + /** + * <code>optional int32 line = 2;</code> + */ + public boolean hasLine() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * <code>optional int32 line = 2;</code> + */ + public int getLine() { + return line_; + } + + private void initFields() { + key_ = ""; + line_ = 0; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, getKeyBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeInt32(2, line_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, getKeyBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(2, line_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.sonar.core.test.Test.Fake parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.sonar.core.test.Test.Fake parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.sonar.core.test.Test.Fake parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.sonar.core.test.Test.Fake parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.sonar.core.test.Test.Fake parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.sonar.core.test.Test.Fake parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.sonar.core.test.Test.Fake parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.sonar.core.test.Test.Fake parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.sonar.core.test.Test.Fake parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.sonar.core.test.Test.Fake parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.sonar.core.test.Test.Fake prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code Fake} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder<Builder> implements + // @@protoc_insertion_point(builder_implements:Fake) + org.sonar.core.test.Test.FakeOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.sonar.core.test.Test.internal_static_Fake_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.sonar.core.test.Test.internal_static_Fake_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.sonar.core.test.Test.Fake.class, org.sonar.core.test.Test.Fake.Builder.class); + } + + // Construct using org.sonar.core.test.Test.Fake.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + key_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + line_ = 0; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.sonar.core.test.Test.internal_static_Fake_descriptor; + } + + public org.sonar.core.test.Test.Fake getDefaultInstanceForType() { + return org.sonar.core.test.Test.Fake.getDefaultInstance(); + } + + public org.sonar.core.test.Test.Fake build() { + org.sonar.core.test.Test.Fake result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.sonar.core.test.Test.Fake buildPartial() { + org.sonar.core.test.Test.Fake result = new org.sonar.core.test.Test.Fake(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.key_ = key_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.line_ = line_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.sonar.core.test.Test.Fake) { + return mergeFrom((org.sonar.core.test.Test.Fake)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.sonar.core.test.Test.Fake other) { + if (other == org.sonar.core.test.Test.Fake.getDefaultInstance()) return this; + if (other.hasKey()) { + bitField0_ |= 0x00000001; + key_ = other.key_; + onChanged(); + } + if (other.hasLine()) { + setLine(other.getLine()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.sonar.core.test.Test.Fake parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.sonar.core.test.Test.Fake) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private java.lang.Object key_ = ""; + /** + * <code>optional string key = 1;</code> + */ + public boolean hasKey() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * <code>optional string key = 1;</code> + */ + public java.lang.String getKey() { + java.lang.Object ref = key_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + key_ = s; + } + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * <code>optional string key = 1;</code> + */ + public com.google.protobuf.ByteString + getKeyBytes() { + java.lang.Object ref = key_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + key_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * <code>optional string key = 1;</code> + */ + public Builder setKey( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + key_ = value; + onChanged(); + return this; + } + /** + * <code>optional string key = 1;</code> + */ + public Builder clearKey() { + bitField0_ = (bitField0_ & ~0x00000001); + key_ = getDefaultInstance().getKey(); + onChanged(); + return this; + } + /** + * <code>optional string key = 1;</code> + */ + public Builder setKeyBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + key_ = value; + onChanged(); + return this; + } + + private int line_ ; + /** + * <code>optional int32 line = 2;</code> + */ + public boolean hasLine() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * <code>optional int32 line = 2;</code> + */ + public int getLine() { + return line_; + } + /** + * <code>optional int32 line = 2;</code> + */ + public Builder setLine(int value) { + bitField0_ |= 0x00000002; + line_ = value; + onChanged(); + return this; + } + /** + * <code>optional int32 line = 2;</code> + */ + public Builder clearLine() { + bitField0_ = (bitField0_ & ~0x00000002); + line_ = 0; + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:Fake) + } + + static { + defaultInstance = new Fake(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:Fake) + } + + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_Fake_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_Fake_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + java.lang.String[] descriptorData = { + "\n\ntest.proto\"!\n\004Fake\022\013\n\003key\030\001 \001(\t\022\014\n\004lin" + + "e\030\002 \001(\005B\027\n\023org.sonar.core.testH\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); + internal_static_Fake_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_Fake_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_Fake_descriptor, + new java.lang.String[] { "Key", "Line", }); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/sonar-core/src/test/java/org/sonar/core/util/ProtobufTest.java b/sonar-core/src/test/java/org/sonar/core/util/ProtobufTest.java new file mode 100644 index 00000000000..5b30c2b9a7c --- /dev/null +++ b/sonar-core/src/test/java/org/sonar/core/util/ProtobufTest.java @@ -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.core.util; + +import java.io.File; +import java.util.Arrays; +import org.apache.commons.io.FileUtils; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; +import org.sonar.test.TestUtils; + +import static java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.core.test.Test.Fake; + +public class ProtobufTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Test + public void only_utils() { + assertThat(TestUtils.hasOnlyPrivateConstructors(Protobuf.class)); + } + + @Test + public void read_file_fails_if_file_does_not_exist() throws Exception { + thrown.expect(ContextException.class); + thrown.expectMessage("Unable to read message"); + + File file = temp.newFile(); + FileUtils.forceDelete(file); + Protobuf.read(file, Fake.PARSER); + } + + @Test + public void read_file_returns_empty_message_if_file_is_empty() throws Exception { + File file = temp.newFile(); + Fake msg = Protobuf.read(file, Fake.PARSER); + assertThat(msg).isNotNull(); + assertThat(msg.isInitialized()).isTrue(); + } + + @Test + public void read_file_returns_message() throws Exception { + File file = temp.newFile(); + Protobuf.write(Fake.getDefaultInstance(), file); + Fake message = Protobuf.read(file, Fake.PARSER); + assertThat(message).isNotNull(); + assertThat(message.isInitialized()).isTrue(); + } + + @Test + public void fail_to_write_single_message() throws Exception { + thrown.expect(ContextException.class); + thrown.expectMessage("Unable to write message"); + + File dir = temp.newFolder(); + Protobuf.write(Fake.getDefaultInstance(), dir); + } + + @Test + public void write_and_read_streams() throws Exception { + File file = temp.newFile(); + + Fake item1 = Fake.newBuilder().setKey("one").setLine(1).build(); + Fake item2 = Fake.newBuilder().setKey("two").build(); + Protobuf.writeStream(asList(item1, item2), file, false); + + CloseableIterator<Fake> it = Protobuf.readStream(file, Fake.PARSER); + Fake read = it.next(); + assertThat(read.getKey()).isEqualTo("one"); + assertThat(read.getLine()).isEqualTo(1); + read = it.next(); + assertThat(read.getKey()).isEqualTo("two"); + assertThat(read.hasLine()).isFalse(); + assertThat(it.hasNext()).isFalse(); + } + + @Test + public void fail_to_read_stream() throws Exception { + thrown.expect(ContextException.class); + thrown.expectMessage("Unable to read messages"); + + File dir = temp.newFolder(); + Protobuf.readStream(dir, Fake.PARSER); + } + + @Test + public void read_empty_stream() throws Exception { + File file = temp.newFile(); + CloseableIterator<Fake> it = Protobuf.readStream(file, Fake.PARSER); + assertThat(it).isNotNull(); + assertThat(it.hasNext()).isFalse(); + } + + // TODO test in-moemry file +} diff --git a/sonar-core/src/test/protobuf/test.proto b/sonar-core/src/test/protobuf/test.proto new file mode 100644 index 00000000000..c449a6a0596 --- /dev/null +++ b/sonar-core/src/test/protobuf/test.proto @@ -0,0 +1,28 @@ +/* + 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.core.test"; +option optimize_for = SPEED; + +message Fake { + optional string key = 1; + optional int32 line = 2; +} |