Java tutorial
/* * 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 com.google.common.base.Optional; import com.google.common.collect.Lists; import java.io.File; import java.io.IOException; import java.util.List; import org.apache.commons.io.FileUtils; import org.junit.After; import org.junit.Before; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.rules.TemporaryFolder; import org.sonar.api.config.Settings; import org.sonar.api.resources.Language; import org.sonar.api.utils.System2; import org.sonar.batch.protocol.Constants; import org.sonar.batch.protocol.output.BatchReport; import org.sonar.batch.protocol.output.BatchReportReader; import org.sonar.batch.protocol.output.BatchReportWriter; import org.sonar.batch.protocol.output.FileStructure; import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.DbTester; import org.sonar.core.source.db.FileSourceDto; import org.sonar.core.source.db.FileSourceDto.Type; import org.sonar.server.computation.ComputationContext; import org.sonar.server.computation.component.ComponentTreeBuilders; import org.sonar.server.computation.component.DumbComponent; import org.sonar.server.computation.language.LanguageRepository; import org.sonar.server.computation.component.DbComponentsRefCache; import org.sonar.server.db.DbClient; import org.sonar.server.source.db.FileSourceDao; import org.sonar.server.source.db.FileSourceDb; import org.sonar.test.DbTests; import static com.google.common.collect.Lists.newArrayList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @Category(DbTests.class) public class PersistFileSourcesStepTest extends BaseStepTest { private static final int FILE_REF = 3; private static final String PROJECT_UUID = "PROJECT"; private static final String PROJECT_KEY = "PROJECT_KEY"; private static final String FILE_UUID = "FILE"; @Rule public TemporaryFolder temp = new TemporaryFolder(); File reportDir; @ClassRule public static DbTester dbTester = new DbTester(); DbSession session; DbClient dbClient; Settings projectSettings; LanguageRepository languageRepository; System2 system2; DbComponentsRefCache dbComponentsRefCache; PersistFileSourcesStep sut; long now = 123456789L; @Before public void setup() throws Exception { dbTester.truncateTables(); session = dbTester.myBatis().openSession(false); dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new FileSourceDao(dbTester.myBatis())); projectSettings = new Settings(); languageRepository = mock(LanguageRepository.class); reportDir = temp.newFolder(); system2 = mock(System2.class); when(system2.now()).thenReturn(now); dbComponentsRefCache = new DbComponentsRefCache(); sut = new PersistFileSourcesStep(dbClient, system2, dbComponentsRefCache); } @Override protected ComputationStep step() { return sut; } @After public void tearDown() { session.close(); } @Test public void persist_sources() throws Exception { initBasicReport(2); sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, dbClient, ComponentTreeBuilders.from(DumbComponent.DUMB_PROJECT), new EmptyLanguageRepository())); assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSource(FILE_UUID); assertThat(fileSourceDto.getProjectUuid()).isEqualTo(PROJECT_UUID); assertThat(fileSourceDto.getFileUuid()).isEqualTo(FILE_UUID); assertThat(fileSourceDto.getBinaryData()).isNotEmpty(); assertThat(fileSourceDto.getDataHash()).isNotEmpty(); assertThat(fileSourceDto.getLineHashes()).isNotEmpty(); assertThat(fileSourceDto.getCreatedAt()).isEqualTo(now); assertThat(fileSourceDto.getUpdatedAt()).isEqualTo(now); FileSourceDb.Data data = FileSourceDto.decodeSourceData(fileSourceDto.getBinaryData()); assertThat(data.getLinesCount()).isEqualTo(2); assertThat(data.getLines(0).getLine()).isEqualTo(1); assertThat(data.getLines(0).getSource()).isEqualTo("line1"); assertThat(data.getLines(1).getLine()).isEqualTo(2); assertThat(data.getLines(1).getSource()).isEqualTo("line2"); } @Test public void persist_last_line() throws Exception { dbComponentsRefCache.addComponent(1, new DbComponentsRefCache.DbComponent(1L, PROJECT_KEY, PROJECT_UUID)); dbComponentsRefCache.addComponent(FILE_REF, new DbComponentsRefCache.DbComponent(2L, "PROJECT_KEY:file", FILE_UUID)); BatchReportWriter writer = new BatchReportWriter(reportDir); FileUtils.writeLines(writer.getFileStructure().fileFor(FileStructure.Domain.SOURCE, FILE_REF), Lists.newArrayList("line1", "line2")); writer.writeMetadata( BatchReport.Metadata.newBuilder().setRootComponentRef(1).setProjectKey(PROJECT_KEY).build()); writer.writeComponent(BatchReport.Component.newBuilder().setRef(1).setType(Constants.ComponentType.PROJECT) .addChildRef(FILE_REF).build()); writer.writeComponent( BatchReport.Component.newBuilder().setRef(FILE_REF).setType(Constants.ComponentType.FILE) // Lines is set to 3 but only 2 lines are read from the file -> the last lines should be added .setLines(3).build()); sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, dbClient, ComponentTreeBuilders.from(DumbComponent.DUMB_PROJECT), languageRepository)); assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSource(FILE_UUID); FileSourceDb.Data data = FileSourceDto.decodeSourceData(fileSourceDto.getBinaryData()); assertThat(data.getLinesCount()).isEqualTo(3); assertThat(data.getLines(2).getLine()).isEqualTo(3); assertThat(data.getLines(2).getSource()).isEmpty(); } @Test public void persist_source_hashes() throws Exception { initBasicReport(2); sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, dbClient, ComponentTreeBuilders.from(DumbComponent.DUMB_PROJECT), languageRepository)); assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSource("FILE"); assertThat(fileSourceDto.getLineHashes()) .isEqualTo("137f72c3708c6bd0de00a0e5a69c699b\ne6251bcf1a7dc3ba5e7933e325bbe605"); assertThat(fileSourceDto.getSrcHash()).isEqualTo("ee5a58024a155466b43bc559d953e018"); } @Test public void persist_coverage() throws Exception { BatchReportWriter writer = initBasicReport(1); writer.writeComponentCoverage(FILE_REF, newArrayList(BatchReport.Coverage.newBuilder().setLine(1).setConditions(10).setUtHits(true) .setUtCoveredConditions(2).setItHits(true).setItCoveredConditions(3) .setOverallCoveredConditions(4).build())); sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, dbClient, ComponentTreeBuilders.from(DumbComponent.DUMB_PROJECT), languageRepository)); assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSource(FILE_UUID); FileSourceDb.Data data = FileSourceDto.decodeSourceData(fileSourceDto.getBinaryData()); assertThat(data.getLinesList()).hasSize(1); assertThat(data.getLines(0).getUtLineHits()).isEqualTo(1); assertThat(data.getLines(0).getUtConditions()).isEqualTo(10); assertThat(data.getLines(0).getUtCoveredConditions()).isEqualTo(2); assertThat(data.getLines(0).hasItLineHits()).isTrue(); assertThat(data.getLines(0).getItConditions()).isEqualTo(10); assertThat(data.getLines(0).getItCoveredConditions()).isEqualTo(3); assertThat(data.getLines(0).getOverallLineHits()).isEqualTo(1); assertThat(data.getLines(0).getOverallConditions()).isEqualTo(10); assertThat(data.getLines(0).getOverallCoveredConditions()).isEqualTo(4); } @Test public void persist_scm() throws Exception { BatchReportWriter writer = initBasicReport(1); writer.writeComponentChangesets(BatchReport.Changesets .newBuilder().setComponentRef(FILE_REF).addChangeset(BatchReport.Changesets.Changeset.newBuilder() .setAuthor("john").setDate(123456789L).setRevision("rev-1").build()) .addChangesetIndexByLine(0).build()); sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, dbClient, ComponentTreeBuilders.from(DumbComponent.DUMB_PROJECT), languageRepository)); assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSource(FILE_UUID); FileSourceDb.Data data = FileSourceDto.decodeSourceData(fileSourceDto.getBinaryData()); assertThat(data.getLinesList()).hasSize(1); assertThat(data.getLines(0).getScmAuthor()).isEqualTo("john"); assertThat(data.getLines(0).getScmDate()).isEqualTo(123456789L); assertThat(data.getLines(0).getScmRevision()).isEqualTo("rev-1"); } @Test public void persist_highlighting() throws Exception { BatchReportWriter writer = initBasicReport(1); writer.writeComponentSyntaxHighlighting(FILE_REF, newArrayList(BatchReport.SyntaxHighlighting .newBuilder().setRange(BatchReport.Range.newBuilder().setStartLine(1).setEndLine(1) .setStartOffset(2).setEndOffset(4).build()) .setType(Constants.HighlightingType.ANNOTATION).build())); sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, dbClient, ComponentTreeBuilders.from(DumbComponent.DUMB_PROJECT), languageRepository)); assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSource(FILE_UUID); FileSourceDb.Data data = FileSourceDto.decodeSourceData(fileSourceDto.getBinaryData()); assertThat(data.getLinesList()).hasSize(1); assertThat(data.getLines(0).getHighlighting()).isEqualTo("2,4,a"); } @Test public void persist_symbols() throws Exception { BatchReportWriter writer = initBasicReport(3); writer.writeComponentSymbols(FILE_REF, newArrayList(BatchReport.Symbols.Symbol.newBuilder() .setDeclaration(BatchReport.Range.newBuilder().setStartLine(1).setEndLine(1) .setStartOffset(2).setEndOffset(4).build()) .addReference(BatchReport.Range.newBuilder().setStartLine(3).setEndLine(3).setStartOffset(1) .setEndOffset(3).build()) .build())); sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, dbClient, ComponentTreeBuilders.from(DumbComponent.DUMB_PROJECT), languageRepository)); assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSource(FILE_UUID); FileSourceDb.Data data = FileSourceDto.decodeSourceData(fileSourceDto.getBinaryData()); assertThat(data.getLinesList()).hasSize(3); assertThat(data.getLines(0).getSymbols()).isEqualTo("2,4,1"); assertThat(data.getLines(1).getSymbols()).isEmpty(); assertThat(data.getLines(2).getSymbols()).isEqualTo("1,3,1"); } @Test public void persist_duplication() throws Exception { BatchReportWriter writer = initBasicReport(1); writer.writeComponentDuplications(FILE_REF, newArrayList(BatchReport.Duplication.newBuilder() .setOriginPosition(BatchReport.Range.newBuilder().setStartLine(1).setEndLine(2).build()) .addDuplicate(BatchReport.Duplicate.newBuilder() .setRange(BatchReport.Range.newBuilder().setStartLine(3).setEndLine(4).build()).build()) .build())); sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, dbClient, ComponentTreeBuilders.from(DumbComponent.DUMB_PROJECT), languageRepository)); assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSource(FILE_UUID); FileSourceDb.Data data = FileSourceDto.decodeSourceData(fileSourceDto.getBinaryData()); assertThat(data.getLinesList()).hasSize(1); assertThat(data.getLines(0).getDuplicationList()).hasSize(1); } @Test public void not_update_sources_when_nothing_has_changed() throws Exception { // Existing sources long past = 150000L; String srcHash = "137f72c3708c6bd0de00a0e5a69c699b"; String lineHashes = "137f72c3708c6bd0de00a0e5a69c699b"; String dataHash = "29f25900140c94db38035128cb6de6a2"; dbClient.fileSourceDao().insert(session, new FileSourceDto().setProjectUuid(PROJECT_UUID) .setFileUuid(FILE_UUID).setSrcHash(srcHash).setLineHashes(lineHashes).setDataHash(dataHash) .setSourceData(FileSourceDb.Data.newBuilder() .addLines(FileSourceDb.Line.newBuilder().setLine(1).setSource("line1").build()).build()) .setCreatedAt(past).setUpdatedAt(past)); session.commit(); // Sources from the report initBasicReport(1); sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, dbClient, ComponentTreeBuilders.from(DumbComponent.DUMB_PROJECT), languageRepository)); assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSource(FILE_UUID); assertThat(fileSourceDto.getSrcHash()).isEqualTo(srcHash); assertThat(fileSourceDto.getLineHashes()).isEqualTo(lineHashes); assertThat(fileSourceDto.getDataHash()).isEqualTo(dataHash); assertThat(fileSourceDto.getCreatedAt()).isEqualTo(past); assertThat(fileSourceDto.getUpdatedAt()).isEqualTo(past); } @Test public void update_sources_when_source_updated() throws Exception { // Existing sources long past = 150000L; dbClient.fileSourceDao().insert(session, new FileSourceDto().setProjectUuid(PROJECT_UUID) .setFileUuid(FILE_UUID).setDataType(Type.SOURCE).setSrcHash("5b4bd9815cdb17b8ceae19eb1810c34c") .setLineHashes("6438c669e0d0de98e6929c2cc0fac474\n").setDataHash("6cad150e3d065976c230cddc5a09efaa") .setSourceData(FileSourceDb.Data.newBuilder() .addLines(FileSourceDb.Line.newBuilder().setLine(1).setSource("old line").build()).build()) .setCreatedAt(past).setUpdatedAt(past)); session.commit(); initBasicReport(1); sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, dbClient, ComponentTreeBuilders.from(DumbComponent.DUMB_PROJECT), languageRepository)); assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSource(FILE_UUID); assertThat(fileSourceDto.getCreatedAt()).isEqualTo(past); assertThat(fileSourceDto.getUpdatedAt()).isEqualTo(now); } @Test public void update_sources_when_src_hash_is_missing() throws Exception { // Existing sources long past = 150000L; dbClient.fileSourceDao().insert(session, new FileSourceDto().setProjectUuid(PROJECT_UUID) .setFileUuid(FILE_UUID).setDataType(Type.SOURCE) // Source hash is missing, update will be made .setLineHashes("137f72c3708c6bd0de00a0e5a69c699b").setDataHash("29f25900140c94db38035128cb6de6a2") .setSourceData(FileSourceDb.Data.newBuilder() .addLines(FileSourceDb.Line.newBuilder().setLine(1).setSource("line").build()).build()) .setCreatedAt(past).setUpdatedAt(past)); session.commit(); initBasicReport(1); sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, dbClient, ComponentTreeBuilders.from(DumbComponent.DUMB_PROJECT), languageRepository)); assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1); FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSource(FILE_UUID); assertThat(fileSourceDto.getCreatedAt()).isEqualTo(past); // Updated at is not updated to not reindex the file source in E/S as the src hash is not indexed assertThat(fileSourceDto.getUpdatedAt()).isEqualTo(past); assertThat(fileSourceDto.getSrcHash()).isEqualTo("137f72c3708c6bd0de00a0e5a69c699b"); } @Test public void display_file_path_when_exception_is_generated() throws Exception { BatchReportWriter writer = initBasicReport(1); writer.writeComponentSyntaxHighlighting(FILE_REF, newArrayList(BatchReport.SyntaxHighlighting.newBuilder() .setRange(BatchReport.Range.newBuilder().setStartLine(1).setEndLine(1) // Wrong offset -> fail .setStartOffset(4).setEndOffset(2).build()) .setType(Constants.HighlightingType.ANNOTATION).build())); try { sut.execute(new ComputationContext(new BatchReportReader(reportDir), PROJECT_KEY, projectSettings, dbClient, ComponentTreeBuilders.from(DumbComponent.DUMB_PROJECT), languageRepository)); failBecauseExceptionWasNotThrown(IllegalStateException.class); } catch (IllegalStateException e) { assertThat(e).hasMessage("Cannot persist sources of src/Foo.java") .hasCauseInstanceOf(IllegalArgumentException.class); } } private BatchReportWriter initBasicReport(int numberOfLines) throws IOException { dbComponentsRefCache.addComponent(1, new DbComponentsRefCache.DbComponent(1L, PROJECT_KEY, PROJECT_UUID)); dbComponentsRefCache.addComponent(2, new DbComponentsRefCache.DbComponent(2L, "MODULE_KEY", "MODULE")); dbComponentsRefCache.addComponent(FILE_REF, new DbComponentsRefCache.DbComponent(3L, "MODULE_KEY:src/Foo.java", FILE_UUID)); BatchReportWriter writer = new BatchReportWriter(reportDir); writer.writeMetadata( BatchReport.Metadata.newBuilder().setRootComponentRef(1).setProjectKey(PROJECT_KEY).build()); writer.writeComponent(BatchReport.Component.newBuilder().setRef(1).setType(Constants.ComponentType.PROJECT) .addChildRef(2).build()); writer.writeComponent(BatchReport.Component.newBuilder().setRef(2).setType(Constants.ComponentType.MODULE) .addChildRef(FILE_REF).build()); writer.writeComponent(BatchReport.Component.newBuilder().setRef(FILE_REF) .setType(Constants.ComponentType.FILE).setPath("src/Foo.java").setLines(numberOfLines).build()); List<String> lines = newArrayList(); for (int i = 1; i <= numberOfLines; i++) { lines.add("line" + i); } FileUtils.writeLines(writer.getSourceFile(FILE_REF), lines); return writer; } private static class EmptyLanguageRepository implements LanguageRepository { @Override public Optional<Language> find(String languageKey) { return Optional.absent(); } } }