From: Duarte Meneses Date: Wed, 25 Apr 2018 13:42:40 +0000 (+0200) Subject: Minor refactoring X-Git-Tag: 7.5~1249 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=9aa90ae28dc7ce6c22c6e51c6d847c848a30c949;p=sonarqube.git Minor refactoring --- diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/source/FileSourceDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/source/FileSourceDao.java index b660050cde9..7921adb039f 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/source/FileSourceDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/source/FileSourceDao.java @@ -27,7 +27,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.Collections; import java.util.List; -import java.util.function.Function; +import java.util.function.Consumer; import javax.annotation.CheckForNull; import org.apache.commons.dbutils.DbUtils; import org.apache.commons.io.IOUtils; @@ -45,7 +45,7 @@ public class FileSourceDao implements Dao { } @CheckForNull - public FileSourceDto selectTest(DbSession dbSession, String fileUuid) { + public FileSourceDto selectTestByFileUuid(DbSession dbSession, String fileUuid) { return mapper(dbSession).select(fileUuid, Type.TEST); } @@ -74,7 +74,7 @@ public class FileSourceDao implements Dao { } } - public void readLineHashesStream(DbSession dbSession, String fileUuid, Function function) { + public void readLineHashesStream(DbSession dbSession, String fileUuid, Consumer consumer) { Connection connection = dbSession.getConnection(); PreparedStatement pstmt = null; ResultSet rs = null; @@ -87,7 +87,7 @@ public class FileSourceDao implements Dao { if (rs.next()) { reader = rs.getCharacterStream(1); if (reader != null) { - function.apply(reader); + consumer.accept(reader); } } } catch (SQLException e) { diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/source/FileSourceDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/source/FileSourceDto.java index 3982bb6c646..1ac9fbe4043 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/source/FileSourceDto.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/source/FileSourceDto.java @@ -206,7 +206,7 @@ public class FileSourceDto { } /** - * Compressed value of serialized protobuf message {@link org.sonar.db.protobuf.DbFileSources.Data} + * Decompressed value of serialized protobuf message {@link org.sonar.db.protobuf.DbFileSources.Data} */ public DbFileSources.Data getSourceData() { return decodeSourceData(binaryData); diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/source/FileSourceDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/source/FileSourceDaoTest.java index 718c4ee7096..13df64a6fec 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/source/FileSourceDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/source/FileSourceDaoTest.java @@ -21,7 +21,7 @@ package org.sonar.db.source; import java.io.IOException; import java.io.Reader; -import java.util.function.Function; +import java.util.function.Consumer; import javax.annotation.Nullable; import org.apache.commons.io.IOUtils; import org.junit.Rule; @@ -63,7 +63,7 @@ public class FileSourceDaoTest { public void select_line_hashes() { dbTester.prepareDbUnit(getClass(), "shared.xml"); - ReaderToStringFunction fn = new ReaderToStringFunction(); + ReaderToStringConsumer fn = new ReaderToStringConsumer(); underTest.readLineHashesStream(dbTester.getSession(), "FILE1_UUID", fn); assertThat(fn.result).isEqualTo("ABC\\nDEF\\nGHI"); @@ -73,7 +73,7 @@ public class FileSourceDaoTest { public void no_line_hashes_on_unknown_file() { dbTester.prepareDbUnit(getClass(), "shared.xml"); - ReaderToStringFunction fn = new ReaderToStringFunction(); + ReaderToStringConsumer fn = new ReaderToStringConsumer(); underTest.readLineHashesStream(dbTester.getSession(), "unknown", fn); assertThat(fn.result).isNull(); @@ -83,7 +83,7 @@ public class FileSourceDaoTest { public void no_line_hashes_when_only_test_data() { dbTester.prepareDbUnit(getClass(), "no_line_hashes_when_only_test_data.xml"); - ReaderToStringFunction fn = new ReaderToStringFunction(); + ReaderToStringConsumer fn = new ReaderToStringConsumer(); underTest.readLineHashesStream(dbTester.getSession(), "FILE1_UUID", fn); assertThat(fn.result).isNull(); @@ -115,15 +115,15 @@ public class FileSourceDaoTest { dbTester.prepareDbUnit(getClass(), "shared.xml"); underTest.insert(session, new FileSourceDto() - .setProjectUuid("PRJ_UUID") - .setFileUuid("FILE2_UUID") - .setBinaryData("FILE2_BINARY_DATA".getBytes()) - .setDataHash("FILE2_DATA_HASH") - .setSrcHash("FILE2_HASH") - .setDataType(Type.SOURCE) - .setCreatedAt(1500000000000L) - .setUpdatedAt(1500000000001L) - .setRevision("123456789")); + .setProjectUuid("PRJ_UUID") + .setFileUuid("FILE2_UUID") + .setBinaryData("FILE2_BINARY_DATA".getBytes()) + .setDataHash("FILE2_DATA_HASH") + .setSrcHash("FILE2_HASH") + .setDataType(Type.SOURCE) + .setCreatedAt(1500000000000L) + .setUpdatedAt(1500000000001L) + .setRevision("123456789")); session.commit(); assertThat(underTest.selectLineHashes(dbTester.getSession(), "FILE2_UUID")).isEmpty(); @@ -134,25 +134,23 @@ public class FileSourceDaoTest { dbTester.prepareDbUnit(getClass(), "shared.xml"); underTest.insert(session, new FileSourceDto() - .setProjectUuid("PRJ_UUID") - .setFileUuid("FILE2_UUID") - .setBinaryData("FILE2_BINARY_DATA".getBytes()) - .setDataHash("FILE2_DATA_HASH") - .setSrcHash("FILE2_HASH") - .setDataType(Type.SOURCE) - .setCreatedAt(1500000000000L) - .setUpdatedAt(1500000000001L) - .setRevision("123456789")); + .setProjectUuid("PRJ_UUID") + .setFileUuid("FILE2_UUID") + .setBinaryData("FILE2_BINARY_DATA".getBytes()) + .setDataHash("FILE2_DATA_HASH") + .setSrcHash("FILE2_HASH") + .setDataType(Type.SOURCE) + .setCreatedAt(1500000000000L) + .setUpdatedAt(1500000000001L) + .setRevision("123456789")); session.commit(); boolean[] flag = {false}; - underTest.readLineHashesStream(dbTester.getSession(), "FILE2_UUID", new Function() { - @Nullable + underTest.readLineHashesStream(dbTester.getSession(), "FILE2_UUID", new Consumer() { @Override - public Void apply(@Nullable Reader input) { + public void accept(@Nullable Reader input) { fail("function must never been called since there is no data to read"); flag[0] = true; - return null; } }); assertThat(flag[0]).isFalse(); @@ -179,15 +177,14 @@ public class FileSourceDaoTest { "project_uuid", "file_uuid", "data_hash", "line_hashes", "src_hash", "created_at", "updated_at", "data_type", "revision"); } - private static class ReaderToStringFunction implements Function { + private static class ReaderToStringConsumer implements Consumer { String result = null; @Override - public String apply(Reader input) { + public void accept(Reader input) { try { result = IOUtils.toString(input); - return IOUtils.toString(input); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/batch/BatchReportReader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/batch/BatchReportReader.java index 9557e969e2c..80adfdee00a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/batch/BatchReportReader.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/batch/BatchReportReader.java @@ -53,7 +53,7 @@ public interface BatchReportReader { CloseableIterator readComponentCoverage(int fileRef); /** - * Reads file source line by line. Return an absent optional if the file doest not exist + * Reads a file's source code, line by line. Returns an absent optional if the file does not exist */ Optional> readFileSource(int fileRef); @@ -62,4 +62,6 @@ public interface BatchReportReader { CloseableIterator readCoverageDetails(int testFileRef); CloseableIterator readContextProperties(); + + CloseableIterator readComponentSignificantCode(int fileRef); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/batch/BatchReportReaderImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/batch/BatchReportReaderImpl.java index b7dc446fe28..e9f2c81ace3 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/batch/BatchReportReaderImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/batch/BatchReportReaderImpl.java @@ -36,6 +36,7 @@ import org.apache.commons.io.LineIterator; import org.sonar.core.util.CloseableIterator; import org.sonar.core.util.LineReaderIterator; import org.sonar.scanner.protocol.output.ScannerReport; +import org.sonar.scanner.protocol.output.ScannerReport.LineSgnificantCode; public class BatchReportReaderImpl implements BatchReportReader { @@ -254,4 +255,10 @@ public class BatchReportReaderImpl implements BatchReportReader { fileInputStream.close(); } } + + @Override + public CloseableIterator readComponentSignificantCode(int fileRef) { + ensureInitialized(); + return delegate.readComponentSignificantCode(fileRef); + } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueAssigner.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueAssigner.java index d366cb11526..ec270607c43 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueAssigner.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueAssigner.java @@ -19,9 +19,9 @@ */ package org.sonar.server.computation.task.projectanalysis.issue; -import com.google.common.base.Optional; import com.google.common.base.Strings; import java.util.Date; +import java.util.Optional; import javax.annotation.CheckForNull; import org.apache.commons.lang.StringUtils; import org.sonar.api.utils.log.Logger; diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueCreationDateCalculator.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueCreationDateCalculator.java index 70a8a66633a..a43250189d3 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueCreationDateCalculator.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueCreationDateCalculator.java @@ -139,7 +139,7 @@ public class IssueCreationDateCalculator extends IssueVisitor { } private Optional getScmInfo(Component component) { - return toJavaUtilOptional(scmInfoRepository.getScmInfo(component)); + return scmInfoRepository.getScmInfo(component); } private static Optional getChangeset(Component component, ScmInfo scmInfo, DefaultIssue issue) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/qualitymodel/NewMaintainabilityMeasuresVisitor.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/qualitymodel/NewMaintainabilityMeasuresVisitor.java index 86df58c3fe9..cc69b2a5ae1 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/qualitymodel/NewMaintainabilityMeasuresVisitor.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/qualitymodel/NewMaintainabilityMeasuresVisitor.java @@ -165,7 +165,7 @@ public class NewMaintainabilityMeasuresVisitor extends PathAwareVisitorAdapter scmInfoOptional = scmInfoRepository.getScmInfo(file); + java.util.Optional scmInfoOptional = scmInfoRepository.getScmInfo(file); if (!scmInfoOptional.isPresent()) { LOG.trace(String.format("No changeset for file %s. Dev cost will be zero.", file.getKey())); return; diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoDbLoader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoDbLoader.java index 7ee76a4f646..4225dff3b59 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoDbLoader.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoDbLoader.java @@ -49,7 +49,7 @@ public class ScmInfoDbLoader { return Optional.empty(); } - LOGGER.trace("Reading SCM info from db for file '{}'", uuid.get()); + LOGGER.trace("Reading SCM info from DB for file '{}'", uuid.get()); try (DbSession dbSession = dbClient.openSession(false)) { FileSourceDto dto = dbClient.fileSourceDao().selectSourceByFileUuid(dbSession, uuid.get()); if (dto == null) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoRepository.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoRepository.java index 8daffdaa749..2a80de6d63a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoRepository.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoRepository.java @@ -19,7 +19,7 @@ */ package org.sonar.server.computation.task.projectanalysis.scm; -import com.google.common.base.Optional; +import java.util.Optional; import org.sonar.server.computation.task.projectanalysis.component.Component; /** diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoRepositoryImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoRepositoryImpl.java index 24e714a5683..c4d89610db9 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoRepositoryImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoRepositoryImpl.java @@ -58,18 +58,14 @@ public class ScmInfoRepositoryImpl implements ScmInfoRepository { } @Override - public com.google.common.base.Optional getScmInfo(Component component) { + public Optional getScmInfo(Component component) { requireNonNull(component, "Component cannot be null"); if (component.getType() != Component.Type.FILE) { - return com.google.common.base.Optional.absent(); + return Optional.empty(); } - return toGuavaOptional(scmInfoCache.computeIfAbsent(component, this::getScmInfoForComponent)); - } - - private static com.google.common.base.Optional toGuavaOptional(Optional scmInfo) { - return com.google.common.base.Optional.fromNullable(scmInfo.orElse(null)); + return scmInfoCache.computeIfAbsent(component, this::getScmInfoForComponent); } private Optional getScmInfoForComponent(Component component) { @@ -77,7 +73,7 @@ public class ScmInfoRepositoryImpl implements ScmInfoRepository { if (changesets == null) { LOGGER.trace("No SCM info for file '{}'", component.getKey()); - // SCM not available. It might have been available before - don't keep author and revision. + // SCM not available. It might have been available before - copy information for unchanged lines but don't keep author and revision. return generateAndMergeDb(component, false); } @@ -111,6 +107,12 @@ public class ScmInfoRepositoryImpl implements ScmInfoRepository { return Changeset.newChangesetBuilder().setDate(changeset.getDate()).build(); } + /** + * Get SCM information in the DB, if it exists, and use it for lines that didn't change. It optionally removes author and revision + * information (only keeping change dates). + * If the information is not present in the DB or some lines don't match existing lines in the DB, + * we generate change dates based on the analysis date. + */ private Optional generateAndMergeDb(Component file, boolean keepAuthorAndRevision) { Optional dbInfoOpt = scmInfoDbLoader.getScmInfo(file); if (!dbInfoOpt.isPresent()) { @@ -125,7 +127,7 @@ public class ScmInfoRepositoryImpl implements ScmInfoRepository { } // generate date for new/changed lines - int[] matchingLines = sourceLinesDiff.getMatchingLines(file); + int[] matchingLines = sourceLinesDiff.computeMatchingLines(file); return Optional.of(GeneratedScmInfo.create(analysisMetadata.getAnalysisDate(), matchingLines, scmInfo)); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/ComputeFileSourceData.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/ComputeFileSourceData.java index 45f40f648e4..dd06717d66d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/ComputeFileSourceData.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/ComputeFileSourceData.java @@ -25,6 +25,7 @@ import java.util.List; import org.sonar.core.hash.SourceHashComputer; import org.sonar.core.hash.SourceLinesHashesComputer; import org.sonar.db.protobuf.DbFileSources; +import org.sonar.server.computation.task.projectanalysis.source.linereader.LineReader; public class ComputeFileSourceData { @@ -50,12 +51,13 @@ public class ComputeFileSourceData { return data; } - private void read(Data data, String source, boolean hasNextLine) { - data.linesHashesComputer.addLine(source); - data.sourceHashComputer.addLine(source, hasNextLine); + private void read(Data data, String lineSource, boolean hasNextLine) { + data.linesHashesComputer.addLine(lineSource); + data.sourceHashComputer.addLine(lineSource, hasNextLine); - DbFileSources.Line.Builder lineBuilder = data.fileSourceBuilder.addLinesBuilder() - .setSource(source) + DbFileSources.Line.Builder lineBuilder = data.fileSourceBuilder + .addLinesBuilder() + .setSource(lineSource) .setLine(currentLine); for (LineReader lineReader : lineReaders) { lineReader.read(lineBuilder); diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/CoverageLineReader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/CoverageLineReader.java deleted file mode 100644 index 8bfc6644fad..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/CoverageLineReader.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program 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. - * - * This program 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.task.projectanalysis.source; - -import java.util.Iterator; -import javax.annotation.CheckForNull; -import org.sonar.db.protobuf.DbFileSources; -import org.sonar.scanner.protocol.output.ScannerReport; -import org.sonar.scanner.protocol.output.ScannerReport.LineCoverage.HasCoveredConditionsCase; -import org.sonar.scanner.protocol.output.ScannerReport.LineCoverage.HasHitsCase; - -public class CoverageLineReader implements LineReader { - - private final Iterator coverageIterator; - private ScannerReport.LineCoverage coverage; - - public CoverageLineReader(Iterator coverageIterator) { - this.coverageIterator = coverageIterator; - } - - @Override - public void read(DbFileSources.Line.Builder lineBuilder) { - ScannerReport.LineCoverage reportCoverage = getNextLineCoverageIfMatchLine(lineBuilder.getLine()); - if (reportCoverage != null) { - processCoverage(lineBuilder, reportCoverage); - coverage = null; - } - } - - private static void processCoverage(DbFileSources.Line.Builder lineBuilder, ScannerReport.LineCoverage reportCoverage) { - if (reportCoverage.getHasHitsCase() == HasHitsCase.HITS) { - lineBuilder.setLineHits(reportCoverage.getHits() ? 1 : 0); - } - if (reportCoverage.getHasCoveredConditionsCase() == HasCoveredConditionsCase.COVERED_CONDITIONS) { - lineBuilder.setConditions(reportCoverage.getConditions()); - lineBuilder.setCoveredConditions(reportCoverage.getCoveredConditions()); - } - } - - @CheckForNull - private ScannerReport.LineCoverage getNextLineCoverageIfMatchLine(int line) { - // Get next element (if exists) - if (coverage == null && coverageIterator.hasNext()) { - coverage = coverageIterator.next(); - } - // Return current element if lines match - if (coverage != null && coverage.getLine() == line) { - return coverage; - } - return null; - } - -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/DuplicationLineReader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/DuplicationLineReader.java deleted file mode 100644 index afd7e870a12..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/DuplicationLineReader.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program 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. - * - * This program 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.task.projectanalysis.source; - -import com.google.common.base.Function; -import com.google.common.base.Predicate; -import com.google.common.collect.Ordering; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import org.sonar.db.protobuf.DbFileSources; -import org.sonar.server.computation.task.projectanalysis.duplication.Duplication; -import org.sonar.server.computation.task.projectanalysis.duplication.InnerDuplicate; -import org.sonar.server.computation.task.projectanalysis.duplication.TextBlock; - -import static com.google.common.collect.FluentIterable.from; -import static com.google.common.collect.Iterables.size; - -public class DuplicationLineReader implements LineReader { - - private final Map duplicatedTextBlockIndexByTextBlock; - - public DuplicationLineReader(Iterable duplications) { - this.duplicatedTextBlockIndexByTextBlock = createIndexOfDuplicatedTextBlocks(duplications); - } - - @Override - public void read(DbFileSources.Line.Builder lineBuilder) { - Predicate> containsLine = new TextBlockContainsLine(lineBuilder.getLine()); - for (Integer textBlockIndex : from(duplicatedTextBlockIndexByTextBlock.entrySet()) - .filter(containsLine) - .transform(MapEntryToBlockId.INSTANCE) - // list is sorted to cope with the non-guaranteed order of Map entries which would trigger false detection of changes - // in {@link DbFileSources.Line#getDuplicationList()} - .toSortedList(Ordering.natural())) { - lineBuilder.addDuplication(textBlockIndex); - } - } - - /** - * - *

- * This method uses the natural order of TextBlocks to ensure that given the same set of TextBlocks, they get the same - * index. It avoids false detections of changes in {@link DbFileSources.Line#getDuplicationList()}. - *

- */ - private static Map createIndexOfDuplicatedTextBlocks(Iterable duplications) { - List duplicatedTextBlocks = extractAllDuplicatedTextBlocks(duplications); - Collections.sort(duplicatedTextBlocks); - return from(duplicatedTextBlocks) - .toMap(new TextBlockIndexGenerator()); - } - - /** - * Duplicated blocks in the current file are either {@link Duplication#getOriginal()} or {@link Duplication#getDuplicates()} - * when the {@link org.sonar.server.computation.task.projectanalysis.duplication.Duplicate} is a {@link InnerDuplicate}. - *

- * The returned list is mutable on purpose because it will be sorted. - *

- * - * @see {@link #createIndexOfDuplicatedTextBlocks(Iterable)} - */ - private static List extractAllDuplicatedTextBlocks(Iterable duplications) { - List duplicatedBlock = new ArrayList<>(size(duplications)); - for (Duplication duplication : duplications) { - duplicatedBlock.add(duplication.getOriginal()); - for (InnerDuplicate duplicate : from(duplication.getDuplicates()).filter(InnerDuplicate.class)) { - duplicatedBlock.add(duplicate.getTextBlock()); - } - } - return duplicatedBlock; - } - - private static class TextBlockContainsLine implements Predicate> { - private final int line; - - public TextBlockContainsLine(int line) { - this.line = line; - } - - @Override - public boolean apply(@Nonnull Map.Entry input) { - return isLineInBlock(input.getKey(), line); - } - - private static boolean isLineInBlock(TextBlock range, int line) { - return line >= range.getStart() && line <= range.getEnd(); - } - } - - private enum MapEntryToBlockId implements Function, Integer> { - INSTANCE; - - @Override - @Nonnull - public Integer apply(@Nonnull Map.Entry input) { - return input.getValue(); - } - } - - private static class TextBlockIndexGenerator implements Function { - int i = 1; - - @Nullable - @Override - public Integer apply(TextBlock input) { - return i++; - } - } -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/HighlightingLineReader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/HighlightingLineReader.java deleted file mode 100644 index a758ebb66a9..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/HighlightingLineReader.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program 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. - * - * This program 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.task.projectanalysis.source; - -import com.google.common.collect.ImmutableMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import javax.annotation.CheckForNull; -import org.sonar.api.utils.log.Logger; -import org.sonar.api.utils.log.Loggers; -import org.sonar.db.protobuf.DbFileSources; -import org.sonar.scanner.protocol.output.ScannerReport; -import org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType; -import org.sonar.server.computation.task.projectanalysis.component.Component; -import org.sonar.server.computation.task.projectanalysis.source.RangeOffsetConverter.RangeOffsetConverterException; - -import static com.google.common.collect.Lists.newArrayList; -import static java.lang.String.format; -import static org.sonar.server.computation.task.projectanalysis.source.RangeOffsetConverter.OFFSET_SEPARATOR; -import static org.sonar.server.computation.task.projectanalysis.source.RangeOffsetConverter.SYMBOLS_SEPARATOR; - -public class HighlightingLineReader implements LineReader { - - private static final Logger LOG = Loggers.get(HighlightingLineReader.class); - - private boolean isHighlightingValid = true; - - private static final Map cssClassByType = ImmutableMap.builder() - .put(HighlightingType.ANNOTATION, "a") - .put(HighlightingType.CONSTANT, "c") - .put(HighlightingType.COMMENT, "cd") - .put(HighlightingType.CPP_DOC, "cppd") - .put(HighlightingType.STRUCTURED_COMMENT, "j") - .put(HighlightingType.KEYWORD, "k") - .put(HighlightingType.KEYWORD_LIGHT, "h") - .put(HighlightingType.HIGHLIGHTING_STRING, "s") - .put(HighlightingType.PREPROCESS_DIRECTIVE, "p") - .build(); - - private final Component file; - private final Iterator lineHighlightingIterator; - private final RangeOffsetConverter rangeOffsetConverter; - private final List highlightingList; - - private ScannerReport.SyntaxHighlightingRule currentItem; - - public HighlightingLineReader(Component file, Iterator lineHighlightingIterator, RangeOffsetConverter rangeOffsetConverter) { - this.file = file; - this.lineHighlightingIterator = lineHighlightingIterator; - this.rangeOffsetConverter = rangeOffsetConverter; - this.highlightingList = newArrayList(); - } - - @Override - public void read(DbFileSources.Line.Builder lineBuilder) { - if (!isHighlightingValid) { - return; - } - try { - processHighlightings(lineBuilder); - } catch (RangeOffsetConverterException e) { - isHighlightingValid = false; - LOG.warn(format("Inconsistency detected in Highlighting data. Highlighting will be ignored for file '%s'", file.getKey()), e); - } - } - - private void processHighlightings(DbFileSources.Line.Builder lineBuilder) { - int line = lineBuilder.getLine(); - StringBuilder highlighting = new StringBuilder(); - - incrementHighlightingListMatchingLine(line); - for (Iterator syntaxHighlightingIterator = highlightingList.iterator(); syntaxHighlightingIterator.hasNext();) { - processHighlighting(syntaxHighlightingIterator, highlighting, lineBuilder); - } - if (highlighting.length() > 0) { - lineBuilder.setHighlighting(highlighting.toString()); - } - } - - private void processHighlighting(Iterator syntaxHighlightingIterator, StringBuilder highlighting, - DbFileSources.Line.Builder lineBuilder) { - ScannerReport.SyntaxHighlightingRule syntaxHighlighting = syntaxHighlightingIterator.next(); - int line = lineBuilder.getLine(); - ScannerReport.TextRange range = syntaxHighlighting.getRange(); - if (range.getStartLine() <= line) { - String offsets = rangeOffsetConverter.offsetToString(syntaxHighlighting.getRange(), line, lineBuilder.getSource().length()); - if (offsets.isEmpty()) { - if (range.getEndLine() == line) { - syntaxHighlightingIterator.remove(); - } - } else { - if (highlighting.length() > 0) { - highlighting.append(SYMBOLS_SEPARATOR); - } - highlighting.append(offsets) - .append(OFFSET_SEPARATOR) - .append(getCssClass(syntaxHighlighting.getType())); - if (range.getEndLine() == line) { - syntaxHighlightingIterator.remove(); - } - } - } - } - - private static String getCssClass(HighlightingType type) { - String cssClass = cssClassByType.get(type); - if (cssClass != null) { - return cssClass; - } else { - throw new IllegalArgumentException(format("Unknown type %s ", type.toString())); - } - } - - private void incrementHighlightingListMatchingLine(int line) { - ScannerReport.SyntaxHighlightingRule syntaxHighlighting = getNextHighlightingMatchingLine(line); - while (syntaxHighlighting != null) { - highlightingList.add(syntaxHighlighting); - this.currentItem = null; - syntaxHighlighting = getNextHighlightingMatchingLine(line); - } - } - - @CheckForNull - private ScannerReport.SyntaxHighlightingRule getNextHighlightingMatchingLine(int line) { - // Get next element (if exists) - if (currentItem == null && lineHighlightingIterator.hasNext()) { - currentItem = lineHighlightingIterator.next(); - } - // Return current element if lines match - if (currentItem != null && currentItem.getRange().getStartLine() == line) { - return currentItem; - } - return null; - } - -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/LastCommitVisitor.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/LastCommitVisitor.java index 9260a8b22ca..584598b3834 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/LastCommitVisitor.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/LastCommitVisitor.java @@ -78,7 +78,7 @@ public class LastCommitVisitor extends PathAwareVisitorAdapter scmInfoOptional = scmInfoRepository.getScmInfo(file); + java.util.Optional scmInfoOptional = scmInfoRepository.getScmInfo(file); if (scmInfoOptional.isPresent()) { ScmInfo scmInfo = scmInfoOptional.get(); path.current().addDate(scmInfo.getLatestChangeset().getDate()); diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/LineReader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/LineReader.java deleted file mode 100644 index b688dee12b6..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/LineReader.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program 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. - * - * This program 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.task.projectanalysis.source; - -import org.sonar.db.protobuf.DbFileSources; - -public interface LineReader { - - void read(DbFileSources.Line.Builder lineBuilder); - -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/RangeOffsetConverter.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/RangeOffsetConverter.java deleted file mode 100644 index 86083eb475b..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/RangeOffsetConverter.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program 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. - * - * This program 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.task.projectanalysis.source; - -import org.sonar.scanner.protocol.output.ScannerReport; - -import static java.lang.String.format; - -public class RangeOffsetConverter { - - static final String OFFSET_SEPARATOR = ","; - static final String SYMBOLS_SEPARATOR = ";"; - - public String offsetToString(ScannerReport.TextRange range, int lineIndex, int lineLength) { - validateOffsetOrder(range, lineIndex); - validateStartOffsetNotGreaterThanLineLength(range, lineLength, lineIndex); - validateEndOffsetNotGreaterThanLineLength(range, lineLength, lineIndex); - - int startOffset = range.getStartLine() == lineIndex ? range.getStartOffset() : 0; - int endOffset = range.getEndLine() == lineIndex ? range.getEndOffset() : lineLength; - - StringBuilder element = new StringBuilder(); - if (startOffset < endOffset) { - element.append(startOffset).append(OFFSET_SEPARATOR); - element.append(endOffset); - } - - return element.toString(); - } - - private static void validateOffsetOrder(ScannerReport.TextRange range, int line) { - checkExpression(range.getStartLine() != range.getEndLine() || range.getStartOffset() <= range.getEndOffset(), - "End offset %s cannot be defined before start offset %s on line %s", range.getEndOffset(), range.getStartOffset(), line); - } - - private static void validateStartOffsetNotGreaterThanLineLength(ScannerReport.TextRange range, int lineLength, int line) { - checkExpression(range.getStartLine() != line || range.getStartOffset() <= lineLength, - "Start offset %s is defined outside the length (%s) of the line %s", range.getStartOffset(), lineLength, line); - } - - private static void validateEndOffsetNotGreaterThanLineLength(ScannerReport.TextRange range, int lineLength, int line) { - checkExpression(range.getEndLine() != line || range.getEndOffset() <= lineLength, - "End offset %s is defined outside the length (%s) of the line %s", range.getEndOffset(), lineLength, line); - } - - private static void checkExpression(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { - if (!expression) { - throw new RangeOffsetConverterException(format(errorMessageTemplate, errorMessageArgs)); - } - } - - public static class RangeOffsetConverterException extends RuntimeException { - public RangeOffsetConverterException(String message) { - super(message); - } - } - -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/ScmLineReader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/ScmLineReader.java deleted file mode 100644 index b09edffc585..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/ScmLineReader.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program 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. - * - * This program 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.task.projectanalysis.source; - -import javax.annotation.CheckForNull; -import org.sonar.db.protobuf.DbFileSources; -import org.sonar.server.computation.task.projectanalysis.scm.Changeset; -import org.sonar.server.computation.task.projectanalysis.scm.ScmInfo; - -public class ScmLineReader implements LineReader { - - private final ScmInfo scmReport; - @CheckForNull - private Changeset latestChange; - @CheckForNull - private Changeset latestChangeWithRevision; - - public ScmLineReader(ScmInfo scmReport) { - this.scmReport = scmReport; - } - - @Override - public void read(DbFileSources.Line.Builder lineBuilder) { - if (scmReport.hasChangesetForLine(lineBuilder.getLine())) { - Changeset changeset = scmReport.getChangesetForLine(lineBuilder.getLine()); - String author = changeset.getAuthor(); - if (author != null) { - lineBuilder.setScmAuthor(author); - } - String revision = changeset.getRevision(); - if (revision != null) { - lineBuilder.setScmRevision(revision); - } - lineBuilder.setScmDate(changeset.getDate()); - updateLatestChange(changeset); - - if (revision != null) { - updateLatestChangeWithRevision(changeset); - } - } - } - - private void updateLatestChange(Changeset newChangeSet) { - if (latestChange == null) { - latestChange = newChangeSet; - } else { - long newChangesetDate = newChangeSet.getDate(); - long latestChangeDate = latestChange.getDate(); - if (newChangesetDate > latestChangeDate) { - latestChange = newChangeSet; - } - } - } - - private void updateLatestChangeWithRevision(Changeset newChangeSet) { - if (latestChangeWithRevision == null) { - latestChangeWithRevision = newChangeSet; - } else { - long newChangesetDate = newChangeSet.getDate(); - long latestChangeDate = latestChangeWithRevision.getDate(); - if (newChangesetDate > latestChangeDate) { - latestChangeWithRevision = newChangeSet; - } - } - } - - @CheckForNull - public Changeset getLatestChangeWithRevision() { - return latestChangeWithRevision; - } - - @CheckForNull - public Changeset getLatestChange() { - return latestChange; - } -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesDiff.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesDiff.java index 61b6763df88..99e33032004 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesDiff.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesDiff.java @@ -28,5 +28,5 @@ public interface SourceLinesDiff { * @return an array with one entry for each line in the left side. Those entries point either to a line in the right side, or to 0, * in which case it means the line was added. */ - int[] getMatchingLines(Component component); + int[] computeMatchingLines(Component component); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesDiffFinder.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesDiffFinder.java index f0bc058c695..e4ac5938b68 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesDiffFinder.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesDiffFinder.java @@ -28,15 +28,8 @@ import org.sonar.api.utils.log.Loggers; public class SourceLinesDiffFinder { private static final Logger LOG = Loggers.get(SourceLinesDiffFinder.class); - private final List left; - private final List right; - public SourceLinesDiffFinder(List left, List right) { - this.left = left; - this.right = right; - } - - public int[] findMatchingLines() { + public int[] findMatchingLines(List left, List right) { int[] index = new int[right.size()]; int dbLine = left.size(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesDiffImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesDiffImpl.java index 904072747df..02e08eea5a7 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesDiffImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesDiffImpl.java @@ -19,7 +19,7 @@ */ package org.sonar.server.computation.task.projectanalysis.source; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.sonar.core.hash.SourceLinesHashesComputer; import org.sonar.core.util.CloseableIterator; @@ -42,17 +42,25 @@ public class SourceLinesDiffImpl implements SourceLinesDiff { } @Override - public int[] getMatchingLines(Component component) { + public int[] computeMatchingLines(Component component) { - List database; + List database = getDBLines(component); + List report = getReportLines(component); + + return new SourceLinesDiffFinder().findMatchingLines(database, report); + } + + private List getDBLines(Component component) { try (DbSession dbSession = dbClient.openSession(false)) { - database = fileSourceDao.selectLineHashes(dbSession, component.getUuid()); + List database = fileSourceDao.selectLineHashes(dbSession, component.getUuid()); if (database == null) { - database = new ArrayList<>(); + return Collections.emptyList(); } + return database; } + } - List report; + private List getReportLines(Component component) { SourceLinesHashesComputer linesHashesComputer = new SourceLinesHashesComputer(); try (CloseableIterator lineIterator = sourceLinesRepository.readLines(component)) { while (lineIterator.hasNext()) { @@ -60,10 +68,7 @@ public class SourceLinesDiffImpl implements SourceLinesDiff { linesHashesComputer.addLine(line); } } - report = linesHashesComputer.getLineHashes(); - - return new SourceLinesDiffFinder(database, report).findMatchingLines(); - + return linesHashesComputer.getLineHashes(); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesRepositoryImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesRepositoryImpl.java index dcb5cff5e8c..3366a28802b 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesRepositoryImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesRepositoryImpl.java @@ -39,7 +39,7 @@ public class SourceLinesRepositoryImpl implements SourceLinesRepository { @Override public CloseableIterator readLines(Component file) { - requireNonNull(file, "Component should not be bull"); + requireNonNull(file, "Component should not be null"); checkArgument(file.getType() == FILE, "Component '%s' is not a file", file); Optional> linesIteratorOptional = reportReader.readFileSource(file.getReportAttributes().getRef()); @@ -90,7 +90,7 @@ public class SourceLinesRepositoryImpl implements SourceLinesRepository { @Override protected String doNext() { - throw new UnsupportedOperationException("No implemented because hasNext and next are override"); + throw new UnsupportedOperationException("Not implemented because hasNext() and next() are overriden"); } @Override diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/SymbolsLineReader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/SymbolsLineReader.java deleted file mode 100644 index 486c97c7a86..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/SymbolsLineReader.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program 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. - * - * This program 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.task.projectanalysis.source; - -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Lists; -import com.google.common.collect.SetMultimap; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.IdentityHashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import org.sonar.api.utils.log.Logger; -import org.sonar.api.utils.log.Loggers; -import org.sonar.db.protobuf.DbFileSources; -import org.sonar.scanner.protocol.output.ScannerReport; -import org.sonar.server.computation.task.projectanalysis.component.Component; - -import static java.lang.String.format; -import static org.sonar.server.computation.task.projectanalysis.source.RangeOffsetConverter.OFFSET_SEPARATOR; -import static org.sonar.server.computation.task.projectanalysis.source.RangeOffsetConverter.SYMBOLS_SEPARATOR; - -public class SymbolsLineReader implements LineReader { - - private static final Logger LOG = Loggers.get(HighlightingLineReader.class); - - private final Component file; - private final RangeOffsetConverter rangeOffsetConverter; - private final Map idsBySymbol; - private final SetMultimap symbolsPerLine; - - private boolean areSymbolsValid = true; - - public SymbolsLineReader(Component file, Iterator symbolIterator, RangeOffsetConverter rangeOffsetConverter) { - this.file = file; - this.rangeOffsetConverter = rangeOffsetConverter; - List symbols = Lists.newArrayList(symbolIterator); - // Sort symbols to have deterministic id generation - Collections.sort(symbols, SymbolsComparator.INSTANCE); - - this.idsBySymbol = createIdsBySymbolMap(symbols); - this.symbolsPerLine = buildSymbolsPerLine(symbols); - } - - @Override - public void read(DbFileSources.Line.Builder lineBuilder) { - if (!areSymbolsValid) { - return; - } - try { - processSymbols(lineBuilder); - } catch (RangeOffsetConverter.RangeOffsetConverterException e) { - areSymbolsValid = false; - LOG.warn(format("Inconsistency detected in Symbols data. Symbols will be ignored for file '%s'", file.getKey()), e); - } - } - - private void processSymbols(DbFileSources.Line.Builder lineBuilder) { - int line = lineBuilder.getLine(); - - List lineSymbols = new ArrayList<>(this.symbolsPerLine.get(line)); - // Sort symbols to have deterministic results and avoid false variation that would lead to an unnecessary update of the source files - // data - Collections.sort(lineSymbols, SymbolsComparator.INSTANCE); - - StringBuilder symbolString = new StringBuilder(); - for (ScannerReport.Symbol lineSymbol : lineSymbols) { - int symbolId = idsBySymbol.get(lineSymbol); - - appendSymbol(symbolString, lineSymbol.getDeclaration(), line, symbolId, lineBuilder.getSource()); - for (ScannerReport.TextRange range : lineSymbol.getReferenceList()) { - appendSymbol(symbolString, range, line, symbolId, lineBuilder.getSource()); - } - } - if (symbolString.length() > 0) { - lineBuilder.setSymbols(symbolString.toString()); - } - } - - private void appendSymbol(StringBuilder lineSymbol, ScannerReport.TextRange range, int line, int symbolId, String sourceLine) { - if (matchLine(range, line)) { - String offsets = rangeOffsetConverter.offsetToString(range, line, sourceLine.length()); - if (!offsets.isEmpty()) { - if (lineSymbol.length() > 0) { - lineSymbol.append(SYMBOLS_SEPARATOR); - } - lineSymbol.append(offsets) - .append(OFFSET_SEPARATOR) - .append(symbolId); - } - } - } - - private static boolean matchLine(ScannerReport.TextRange range, int line) { - return range.getStartLine() <= line && range.getEndLine() >= line; - } - - private static Map createIdsBySymbolMap(List symbols) { - Map map = new IdentityHashMap<>(symbols.size()); - int symbolId = 1; - for (ScannerReport.Symbol symbol : symbols) { - map.put(symbol, symbolId); - symbolId++; - } - return map; - } - - private static SetMultimap buildSymbolsPerLine(List symbols) { - SetMultimap res = HashMultimap.create(); - for (ScannerReport.Symbol symbol : symbols) { - putForTextRange(res, symbol, symbol.getDeclaration()); - for (ScannerReport.TextRange textRange : symbol.getReferenceList()) { - putForTextRange(res, symbol, textRange); - } - } - return res; - } - - private static void putForTextRange(SetMultimap res, ScannerReport.Symbol symbol, ScannerReport.TextRange declaration) { - for (int i = declaration.getStartLine(); i <= declaration.getEndLine(); i++) { - res.put(i, symbol); - } - } - - private enum SymbolsComparator implements Comparator { - INSTANCE; - - @Override - public int compare(ScannerReport.Symbol o1, ScannerReport.Symbol o2) { - if (o1.getDeclaration().getStartLine() == o2.getDeclaration().getStartLine()) { - return Integer.compare(o1.getDeclaration().getStartOffset(), o2.getDeclaration().getStartOffset()); - } else { - return Integer.compare(o1.getDeclaration().getStartLine(), o2.getDeclaration().getStartLine()); - } - } - } -} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/CoverageLineReader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/CoverageLineReader.java new file mode 100644 index 00000000000..7660491eb72 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/CoverageLineReader.java @@ -0,0 +1,70 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program 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.task.projectanalysis.source.linereader; + +import java.util.Iterator; +import javax.annotation.CheckForNull; +import org.sonar.db.protobuf.DbFileSources; +import org.sonar.scanner.protocol.output.ScannerReport; +import org.sonar.scanner.protocol.output.ScannerReport.LineCoverage.HasCoveredConditionsCase; +import org.sonar.scanner.protocol.output.ScannerReport.LineCoverage.HasHitsCase; + +public class CoverageLineReader implements LineReader { + + private final Iterator coverageIterator; + private ScannerReport.LineCoverage coverage; + + public CoverageLineReader(Iterator coverageIterator) { + this.coverageIterator = coverageIterator; + } + + @Override + public void read(DbFileSources.Line.Builder lineBuilder) { + ScannerReport.LineCoverage reportCoverage = getNextLineCoverageIfMatchLine(lineBuilder.getLine()); + if (reportCoverage != null) { + processCoverage(lineBuilder, reportCoverage); + coverage = null; + } + } + + private static void processCoverage(DbFileSources.Line.Builder lineBuilder, ScannerReport.LineCoverage reportCoverage) { + if (reportCoverage.getHasHitsCase() == HasHitsCase.HITS) { + lineBuilder.setLineHits(reportCoverage.getHits() ? 1 : 0); + } + if (reportCoverage.getHasCoveredConditionsCase() == HasCoveredConditionsCase.COVERED_CONDITIONS) { + lineBuilder.setConditions(reportCoverage.getConditions()); + lineBuilder.setCoveredConditions(reportCoverage.getCoveredConditions()); + } + } + + @CheckForNull + private ScannerReport.LineCoverage getNextLineCoverageIfMatchLine(int line) { + // Get next element (if exists) + if (coverage == null && coverageIterator.hasNext()) { + coverage = coverageIterator.next(); + } + // Return current element if lines match + if (coverage != null && coverage.getLine() == line) { + return coverage; + } + return null; + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/DuplicationLineReader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/DuplicationLineReader.java new file mode 100644 index 00000000000..ffaa942de91 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/DuplicationLineReader.java @@ -0,0 +1,130 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program 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.task.projectanalysis.source.linereader; + +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.collect.Ordering; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.sonar.db.protobuf.DbFileSources; +import org.sonar.server.computation.task.projectanalysis.duplication.Duplication; +import org.sonar.server.computation.task.projectanalysis.duplication.InnerDuplicate; +import org.sonar.server.computation.task.projectanalysis.duplication.TextBlock; + +import static com.google.common.collect.FluentIterable.from; +import static com.google.common.collect.Iterables.size; + +public class DuplicationLineReader implements LineReader { + + private final Map duplicatedTextBlockIndexByTextBlock; + + public DuplicationLineReader(Iterable duplications) { + this.duplicatedTextBlockIndexByTextBlock = createIndexOfDuplicatedTextBlocks(duplications); + } + + @Override + public void read(DbFileSources.Line.Builder lineBuilder) { + Predicate> containsLine = new TextBlockContainsLine(lineBuilder.getLine()); + for (Integer textBlockIndex : from(duplicatedTextBlockIndexByTextBlock.entrySet()) + .filter(containsLine) + .transform(MapEntryToBlockId.INSTANCE) + // list is sorted to cope with the non-guaranteed order of Map entries which would trigger false detection of changes + // in {@link DbFileSources.Line#getDuplicationList()} + .toSortedList(Ordering.natural())) { + lineBuilder.addDuplication(textBlockIndex); + } + } + + /** + * + *

+ * This method uses the natural order of TextBlocks to ensure that given the same set of TextBlocks, they get the same + * index. It avoids false detections of changes in {@link DbFileSources.Line#getDuplicationList()}. + *

+ */ + private static Map createIndexOfDuplicatedTextBlocks(Iterable duplications) { + List duplicatedTextBlocks = extractAllDuplicatedTextBlocks(duplications); + Collections.sort(duplicatedTextBlocks); + return from(duplicatedTextBlocks) + .toMap(new TextBlockIndexGenerator()); + } + + /** + * Duplicated blocks in the current file are either {@link Duplication#getOriginal()} or {@link Duplication#getDuplicates()} + * when the {@link org.sonar.server.computation.task.projectanalysis.duplication.Duplicate} is a {@link InnerDuplicate}. + *

+ * The returned list is mutable on purpose because it will be sorted. + *

+ * + * @see {@link #createIndexOfDuplicatedTextBlocks(Iterable)} + */ + private static List extractAllDuplicatedTextBlocks(Iterable duplications) { + List duplicatedBlock = new ArrayList<>(size(duplications)); + for (Duplication duplication : duplications) { + duplicatedBlock.add(duplication.getOriginal()); + for (InnerDuplicate duplicate : from(duplication.getDuplicates()).filter(InnerDuplicate.class)) { + duplicatedBlock.add(duplicate.getTextBlock()); + } + } + return duplicatedBlock; + } + + private static class TextBlockContainsLine implements Predicate> { + private final int line; + + public TextBlockContainsLine(int line) { + this.line = line; + } + + @Override + public boolean apply(@Nonnull Map.Entry input) { + return isLineInBlock(input.getKey(), line); + } + + private static boolean isLineInBlock(TextBlock range, int line) { + return line >= range.getStart() && line <= range.getEnd(); + } + } + + private enum MapEntryToBlockId implements Function, Integer> { + INSTANCE; + + @Override + @Nonnull + public Integer apply(@Nonnull Map.Entry input) { + return input.getValue(); + } + } + + private static class TextBlockIndexGenerator implements Function { + int i = 1; + + @Nullable + @Override + public Integer apply(TextBlock input) { + return i++; + } + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/HighlightingLineReader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/HighlightingLineReader.java new file mode 100644 index 00000000000..69f1306f82a --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/HighlightingLineReader.java @@ -0,0 +1,154 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program 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.task.projectanalysis.source.linereader; + +import com.google.common.collect.ImmutableMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import javax.annotation.CheckForNull; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.db.protobuf.DbFileSources; +import org.sonar.scanner.protocol.output.ScannerReport; +import org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType; +import org.sonar.server.computation.task.projectanalysis.component.Component; +import org.sonar.server.computation.task.projectanalysis.source.linereader.RangeOffsetConverter.RangeOffsetConverterException; + +import static com.google.common.collect.Lists.newArrayList; +import static java.lang.String.format; +import static org.sonar.server.computation.task.projectanalysis.source.linereader.RangeOffsetConverter.OFFSET_SEPARATOR; +import static org.sonar.server.computation.task.projectanalysis.source.linereader.RangeOffsetConverter.SYMBOLS_SEPARATOR; + +public class HighlightingLineReader implements LineReader { + + private static final Logger LOG = Loggers.get(HighlightingLineReader.class); + + private boolean isHighlightingValid = true; + + private static final Map cssClassByType = ImmutableMap.builder() + .put(HighlightingType.ANNOTATION, "a") + .put(HighlightingType.CONSTANT, "c") + .put(HighlightingType.COMMENT, "cd") + .put(HighlightingType.CPP_DOC, "cppd") + .put(HighlightingType.STRUCTURED_COMMENT, "j") + .put(HighlightingType.KEYWORD, "k") + .put(HighlightingType.KEYWORD_LIGHT, "h") + .put(HighlightingType.HIGHLIGHTING_STRING, "s") + .put(HighlightingType.PREPROCESS_DIRECTIVE, "p") + .build(); + + private final Component file; + private final Iterator lineHighlightingIterator; + private final RangeOffsetConverter rangeOffsetConverter; + private final List highlightingList; + + private ScannerReport.SyntaxHighlightingRule currentItem; + + public HighlightingLineReader(Component file, Iterator lineHighlightingIterator, RangeOffsetConverter rangeOffsetConverter) { + this.file = file; + this.lineHighlightingIterator = lineHighlightingIterator; + this.rangeOffsetConverter = rangeOffsetConverter; + this.highlightingList = newArrayList(); + } + + @Override + public void read(DbFileSources.Line.Builder lineBuilder) { + if (!isHighlightingValid) { + return; + } + try { + processHighlightings(lineBuilder); + } catch (RangeOffsetConverterException e) { + isHighlightingValid = false; + LOG.warn(format("Inconsistency detected in Highlighting data. Highlighting will be ignored for file '%s'", file.getKey()), e); + } + } + + private void processHighlightings(DbFileSources.Line.Builder lineBuilder) { + int line = lineBuilder.getLine(); + StringBuilder highlighting = new StringBuilder(); + + incrementHighlightingListMatchingLine(line); + for (Iterator syntaxHighlightingIterator = highlightingList.iterator(); syntaxHighlightingIterator.hasNext();) { + processHighlighting(syntaxHighlightingIterator, highlighting, lineBuilder); + } + if (highlighting.length() > 0) { + lineBuilder.setHighlighting(highlighting.toString()); + } + } + + private void processHighlighting(Iterator syntaxHighlightingIterator, StringBuilder highlighting, + DbFileSources.Line.Builder lineBuilder) { + ScannerReport.SyntaxHighlightingRule syntaxHighlighting = syntaxHighlightingIterator.next(); + int line = lineBuilder.getLine(); + ScannerReport.TextRange range = syntaxHighlighting.getRange(); + if (range.getStartLine() <= line) { + String offsets = rangeOffsetConverter.offsetToString(syntaxHighlighting.getRange(), line, lineBuilder.getSource().length()); + if (offsets.isEmpty()) { + if (range.getEndLine() == line) { + syntaxHighlightingIterator.remove(); + } + } else { + if (highlighting.length() > 0) { + highlighting.append(SYMBOLS_SEPARATOR); + } + highlighting.append(offsets) + .append(OFFSET_SEPARATOR) + .append(getCssClass(syntaxHighlighting.getType())); + if (range.getEndLine() == line) { + syntaxHighlightingIterator.remove(); + } + } + } + } + + private static String getCssClass(HighlightingType type) { + String cssClass = cssClassByType.get(type); + if (cssClass != null) { + return cssClass; + } else { + throw new IllegalArgumentException(format("Unknown type %s ", type.toString())); + } + } + + private void incrementHighlightingListMatchingLine(int line) { + ScannerReport.SyntaxHighlightingRule syntaxHighlighting = getNextHighlightingMatchingLine(line); + while (syntaxHighlighting != null) { + highlightingList.add(syntaxHighlighting); + this.currentItem = null; + syntaxHighlighting = getNextHighlightingMatchingLine(line); + } + } + + @CheckForNull + private ScannerReport.SyntaxHighlightingRule getNextHighlightingMatchingLine(int line) { + // Get next element (if exists) + if (currentItem == null && lineHighlightingIterator.hasNext()) { + currentItem = lineHighlightingIterator.next(); + } + // Return current element if lines match + if (currentItem != null && currentItem.getRange().getStartLine() == line) { + return currentItem; + } + return null; + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/LineReader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/LineReader.java new file mode 100644 index 00000000000..4acf9e08341 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/LineReader.java @@ -0,0 +1,28 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program 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.task.projectanalysis.source.linereader; + +import org.sonar.db.protobuf.DbFileSources; + +public interface LineReader { + + void read(DbFileSources.Line.Builder lineBuilder); + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/RangeOffsetConverter.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/RangeOffsetConverter.java new file mode 100644 index 00000000000..d7f1e355bad --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/RangeOffsetConverter.java @@ -0,0 +1,75 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program 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.task.projectanalysis.source.linereader; + +import org.sonar.scanner.protocol.output.ScannerReport; + +import static java.lang.String.format; + +public class RangeOffsetConverter { + + static final String OFFSET_SEPARATOR = ","; + static final String SYMBOLS_SEPARATOR = ";"; + + public String offsetToString(ScannerReport.TextRange range, int lineIndex, int lineLength) { + validateOffsetOrder(range, lineIndex); + validateStartOffsetNotGreaterThanLineLength(range, lineLength, lineIndex); + validateEndOffsetNotGreaterThanLineLength(range, lineLength, lineIndex); + + int startOffset = range.getStartLine() == lineIndex ? range.getStartOffset() : 0; + int endOffset = range.getEndLine() == lineIndex ? range.getEndOffset() : lineLength; + + StringBuilder element = new StringBuilder(); + if (startOffset < endOffset) { + element.append(startOffset).append(OFFSET_SEPARATOR); + element.append(endOffset); + } + + return element.toString(); + } + + private static void validateOffsetOrder(ScannerReport.TextRange range, int line) { + checkExpression(range.getStartLine() != range.getEndLine() || range.getStartOffset() <= range.getEndOffset(), + "End offset %s cannot be defined before start offset %s on line %s", range.getEndOffset(), range.getStartOffset(), line); + } + + private static void validateStartOffsetNotGreaterThanLineLength(ScannerReport.TextRange range, int lineLength, int line) { + checkExpression(range.getStartLine() != line || range.getStartOffset() <= lineLength, + "Start offset %s is defined outside the length (%s) of the line %s", range.getStartOffset(), lineLength, line); + } + + private static void validateEndOffsetNotGreaterThanLineLength(ScannerReport.TextRange range, int lineLength, int line) { + checkExpression(range.getEndLine() != line || range.getEndOffset() <= lineLength, + "End offset %s is defined outside the length (%s) of the line %s", range.getEndOffset(), lineLength, line); + } + + private static void checkExpression(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { + if (!expression) { + throw new RangeOffsetConverterException(format(errorMessageTemplate, errorMessageArgs)); + } + } + + public static class RangeOffsetConverterException extends RuntimeException { + public RangeOffsetConverterException(String message) { + super(message); + } + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/ScmLineReader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/ScmLineReader.java new file mode 100644 index 00000000000..c2759f60c86 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/ScmLineReader.java @@ -0,0 +1,93 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program 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.task.projectanalysis.source.linereader; + +import javax.annotation.CheckForNull; +import org.sonar.db.protobuf.DbFileSources; +import org.sonar.server.computation.task.projectanalysis.scm.Changeset; +import org.sonar.server.computation.task.projectanalysis.scm.ScmInfo; + +public class ScmLineReader implements LineReader { + + private final ScmInfo scmReport; + @CheckForNull + private Changeset latestChange; + @CheckForNull + private Changeset latestChangeWithRevision; + + public ScmLineReader(ScmInfo scmReport) { + this.scmReport = scmReport; + } + + @Override + public void read(DbFileSources.Line.Builder lineBuilder) { + if (scmReport.hasChangesetForLine(lineBuilder.getLine())) { + Changeset changeset = scmReport.getChangesetForLine(lineBuilder.getLine()); + String author = changeset.getAuthor(); + if (author != null) { + lineBuilder.setScmAuthor(author); + } + String revision = changeset.getRevision(); + if (revision != null) { + lineBuilder.setScmRevision(revision); + } + lineBuilder.setScmDate(changeset.getDate()); + updateLatestChange(changeset); + + if (revision != null) { + updateLatestChangeWithRevision(changeset); + } + } + } + + private void updateLatestChange(Changeset newChangeSet) { + if (latestChange == null) { + latestChange = newChangeSet; + } else { + long newChangesetDate = newChangeSet.getDate(); + long latestChangeDate = latestChange.getDate(); + if (newChangesetDate > latestChangeDate) { + latestChange = newChangeSet; + } + } + } + + private void updateLatestChangeWithRevision(Changeset newChangeSet) { + if (latestChangeWithRevision == null) { + latestChangeWithRevision = newChangeSet; + } else { + long newChangesetDate = newChangeSet.getDate(); + long latestChangeDate = latestChangeWithRevision.getDate(); + if (newChangesetDate > latestChangeDate) { + latestChangeWithRevision = newChangeSet; + } + } + } + + @CheckForNull + public Changeset getLatestChangeWithRevision() { + return latestChangeWithRevision; + } + + @CheckForNull + public Changeset getLatestChange() { + return latestChange; + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/SymbolsLineReader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/SymbolsLineReader.java new file mode 100644 index 00000000000..6526d0c4e30 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/SymbolsLineReader.java @@ -0,0 +1,157 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program 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.task.projectanalysis.source.linereader; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.SetMultimap; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.db.protobuf.DbFileSources; +import org.sonar.scanner.protocol.output.ScannerReport; +import org.sonar.server.computation.task.projectanalysis.component.Component; +import org.sonar.server.computation.task.projectanalysis.source.linereader.RangeOffsetConverter.RangeOffsetConverterException; + +import static java.lang.String.format; +import static org.sonar.server.computation.task.projectanalysis.source.linereader.RangeOffsetConverter.OFFSET_SEPARATOR; +import static org.sonar.server.computation.task.projectanalysis.source.linereader.RangeOffsetConverter.SYMBOLS_SEPARATOR; + +public class SymbolsLineReader implements LineReader { + + private static final Logger LOG = Loggers.get(HighlightingLineReader.class); + + private final Component file; + private final RangeOffsetConverter rangeOffsetConverter; + private final Map idsBySymbol; + private final SetMultimap symbolsPerLine; + + private boolean areSymbolsValid = true; + + public SymbolsLineReader(Component file, Iterator symbolIterator, RangeOffsetConverter rangeOffsetConverter) { + this.file = file; + this.rangeOffsetConverter = rangeOffsetConverter; + List symbols = Lists.newArrayList(symbolIterator); + // Sort symbols to have deterministic id generation + Collections.sort(symbols, SymbolsComparator.INSTANCE); + + this.idsBySymbol = createIdsBySymbolMap(symbols); + this.symbolsPerLine = buildSymbolsPerLine(symbols); + } + + @Override + public void read(DbFileSources.Line.Builder lineBuilder) { + if (!areSymbolsValid) { + return; + } + try { + processSymbols(lineBuilder); + } catch (RangeOffsetConverter.RangeOffsetConverterException e) { + areSymbolsValid = false; + LOG.warn(format("Inconsistency detected in Symbols data. Symbols will be ignored for file '%s'", file.getKey()), e); + } + } + + private void processSymbols(DbFileSources.Line.Builder lineBuilder) { + int line = lineBuilder.getLine(); + + List lineSymbols = new ArrayList<>(this.symbolsPerLine.get(line)); + // Sort symbols to have deterministic results and avoid false variation that would lead to an unnecessary update of the source files + // data + Collections.sort(lineSymbols, SymbolsComparator.INSTANCE); + + StringBuilder symbolString = new StringBuilder(); + for (ScannerReport.Symbol lineSymbol : lineSymbols) { + int symbolId = idsBySymbol.get(lineSymbol); + + appendSymbol(symbolString, lineSymbol.getDeclaration(), line, symbolId, lineBuilder.getSource()); + for (ScannerReport.TextRange range : lineSymbol.getReferenceList()) { + appendSymbol(symbolString, range, line, symbolId, lineBuilder.getSource()); + } + } + if (symbolString.length() > 0) { + lineBuilder.setSymbols(symbolString.toString()); + } + } + + private void appendSymbol(StringBuilder lineSymbol, ScannerReport.TextRange range, int line, int symbolId, String sourceLine) { + if (matchLine(range, line)) { + String offsets = rangeOffsetConverter.offsetToString(range, line, sourceLine.length()); + if (!offsets.isEmpty()) { + if (lineSymbol.length() > 0) { + lineSymbol.append(SYMBOLS_SEPARATOR); + } + lineSymbol.append(offsets) + .append(OFFSET_SEPARATOR) + .append(symbolId); + } + } + } + + private static boolean matchLine(ScannerReport.TextRange range, int line) { + return range.getStartLine() <= line && range.getEndLine() >= line; + } + + private static Map createIdsBySymbolMap(List symbols) { + Map map = new IdentityHashMap<>(symbols.size()); + int symbolId = 1; + for (ScannerReport.Symbol symbol : symbols) { + map.put(symbol, symbolId); + symbolId++; + } + return map; + } + + private static SetMultimap buildSymbolsPerLine(List symbols) { + SetMultimap res = HashMultimap.create(); + for (ScannerReport.Symbol symbol : symbols) { + putForTextRange(res, symbol, symbol.getDeclaration()); + for (ScannerReport.TextRange textRange : symbol.getReferenceList()) { + putForTextRange(res, symbol, textRange); + } + } + return res; + } + + private static void putForTextRange(SetMultimap res, ScannerReport.Symbol symbol, ScannerReport.TextRange declaration) { + for (int i = declaration.getStartLine(); i <= declaration.getEndLine(); i++) { + res.put(i, symbol); + } + } + + private enum SymbolsComparator implements Comparator { + INSTANCE; + + @Override + public int compare(ScannerReport.Symbol o1, ScannerReport.Symbol o2) { + if (o1.getDeclaration().getStartLine() == o2.getDeclaration().getStartLine()) { + return Integer.compare(o1.getDeclaration().getStartOffset(), o2.getDeclaration().getStartOffset()); + } else { + return Integer.compare(o1.getDeclaration().getStartLine(), o2.getDeclaration().getStartLine()); + } + } + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/package-info.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/package-info.java new file mode 100644 index 00000000000..8cb1be4a093 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/source/linereader/package-info.java @@ -0,0 +1,23 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program 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.server.computation.task.projectanalysis.source.linereader; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/NewCoverageMeasuresStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/NewCoverageMeasuresStep.java index 1eede2525ee..9e5188ae035 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/NewCoverageMeasuresStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/NewCoverageMeasuresStep.java @@ -251,7 +251,7 @@ public class NewCoverageMeasuresStep implements ComputationStep { @Override public void initialize(CounterInitializationContext context) { Component fileComponent = context.getLeaf(); - Optional scmInfoOptional = scmInfoRepository.getScmInfo(fileComponent); + java.util.Optional scmInfoOptional = scmInfoRepository.getScmInfo(fileComponent); if (!scmInfoOptional.isPresent() || !context.hasPeriod()) { return; } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/NewSizeMeasuresStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/NewSizeMeasuresStep.java index ccd08059c46..20250311c05 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/NewSizeMeasuresStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/NewSizeMeasuresStep.java @@ -109,7 +109,7 @@ public class NewSizeMeasuresStep implements ComputationStep { @Override public void initialize(CounterInitializationContext context) { Component leaf = context.getLeaf(); - Optional scmInfo = scmInfoRepository.getScmInfo(leaf); + java.util.Optional scmInfo = scmInfoRepository.getScmInfo(leaf); if (!scmInfo.isPresent() || !context.hasPeriod()) { return; } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/PersistFileSourcesStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/PersistFileSourcesStep.java index a7c5d4cdcde..2a9e3de22b8 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/PersistFileSourcesStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/PersistFileSourcesStep.java @@ -19,12 +19,12 @@ */ package org.sonar.server.computation.task.projectanalysis.step; -import com.google.common.base.Optional; import com.google.common.collect.ImmutableMap; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import javax.annotation.CheckForNull; import javax.annotation.Nullable; import org.apache.commons.codec.digest.DigestUtils; @@ -48,14 +48,14 @@ import org.sonar.server.computation.task.projectanalysis.scm.Changeset; import org.sonar.server.computation.task.projectanalysis.scm.ScmInfo; import org.sonar.server.computation.task.projectanalysis.scm.ScmInfoRepository; import org.sonar.server.computation.task.projectanalysis.source.ComputeFileSourceData; -import org.sonar.server.computation.task.projectanalysis.source.CoverageLineReader; -import org.sonar.server.computation.task.projectanalysis.source.DuplicationLineReader; -import org.sonar.server.computation.task.projectanalysis.source.HighlightingLineReader; -import org.sonar.server.computation.task.projectanalysis.source.LineReader; -import org.sonar.server.computation.task.projectanalysis.source.RangeOffsetConverter; -import org.sonar.server.computation.task.projectanalysis.source.ScmLineReader; import org.sonar.server.computation.task.projectanalysis.source.SourceLinesRepository; -import org.sonar.server.computation.task.projectanalysis.source.SymbolsLineReader; +import org.sonar.server.computation.task.projectanalysis.source.linereader.CoverageLineReader; +import org.sonar.server.computation.task.projectanalysis.source.linereader.DuplicationLineReader; +import org.sonar.server.computation.task.projectanalysis.source.linereader.HighlightingLineReader; +import org.sonar.server.computation.task.projectanalysis.source.linereader.LineReader; +import org.sonar.server.computation.task.projectanalysis.source.linereader.RangeOffsetConverter; +import org.sonar.server.computation.task.projectanalysis.source.linereader.ScmLineReader; +import org.sonar.server.computation.task.projectanalysis.source.linereader.SymbolsLineReader; import org.sonar.server.computation.task.step.ComputationStep; import static org.sonar.server.computation.task.projectanalysis.component.ComponentVisitor.Order.PRE_ORDER; @@ -220,10 +220,7 @@ public class PersistFileSourcesStep implements ComputationStep { @CheckForNull public Changeset getLatestChangeWithRevision() { - if (scmLineReader == null) { - return null; - } - return scmLineReader.getLatestChangeWithRevision(); + return scmLineReader == null ? null : scmLineReader.getLatestChangeWithRevision(); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/source/SourceService.java b/server/sonar-server/src/main/java/org/sonar/server/source/SourceService.java index 2328cac62f8..d493b048a4d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/source/SourceService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/source/SourceService.java @@ -33,10 +33,12 @@ public class SourceService { private final DbClient dbClient; private final HtmlSourceDecorator htmlDecorator; + private final Function lineToHtml; public SourceService(DbClient dbClient, HtmlSourceDecorator htmlDecorator) { this.dbClient = dbClient; this.htmlDecorator = htmlDecorator; + this.lineToHtml = lineToHtml(); } /** @@ -57,7 +59,7 @@ public class SourceService { } public Optional> getLinesAsHtml(DbSession dbSession, String fileUuid, int from, int toInclusive) { - return getLines(dbSession, fileUuid, from, toInclusive, lineToHtml()); + return getLines(dbSession, fileUuid, from, toInclusive, lineToHtml); } private Optional> getLines(DbSession dbSession, String fileUuid, int from, int toInclusive, Function function) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/source/ws/HashAction.java b/server/sonar-server/src/main/java/org/sonar/server/source/ws/HashAction.java index 89e733a906c..ad536e6a8cc 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/source/ws/HashAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/source/ws/HashAction.java @@ -25,7 +25,7 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Reader; import java.nio.charset.StandardCharsets; -import java.util.function.Function; +import java.util.function.Consumer; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; @@ -75,7 +75,7 @@ public class HashAction implements SourcesWsAction { response.stream().setMediaType("text/plain"); try (OutputStreamWriter writer = new OutputStreamWriter(response.stream().output(), StandardCharsets.UTF_8)) { - HashFunction hashFunction = new HashFunction(writer, componentKey); + HashConsumer hashFunction = new HashConsumer(writer, componentKey); dbClient.fileSourceDao().readLineHashesStream(session, component.uuid(), hashFunction); if (!hashFunction.hasData()) { response.noContent(); @@ -84,26 +84,25 @@ public class HashAction implements SourcesWsAction { } } - private static class HashFunction implements Function { + private static class HashConsumer implements Consumer { private final OutputStreamWriter writer; private final String componentKey; private boolean hasData = false; - public HashFunction(OutputStreamWriter writer, String componentKey) { + public HashConsumer(OutputStreamWriter writer, String componentKey) { this.writer = writer; this.componentKey = componentKey; } @Override - public Void apply(Reader input) { + public void accept(Reader input) { try { hasData = true; CharStreams.copy(input, writer); } catch (IOException e) { throw new IllegalStateException(String.format("Can't read line hashes of file '%s'", componentKey), e); } - return null; } public boolean hasData() { diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IssueCreationDateCalculatorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IssueCreationDateCalculatorTest.java index 2314d14056e..cde7eea7a1b 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IssueCreationDateCalculatorTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IssueCreationDateCalculatorTest.java @@ -363,7 +363,7 @@ public class IssueCreationDateCalculatorTest { private void noScm() { when(scmInfoRepository.getScmInfo(component)) - .thenReturn(Optional.absent()); + .thenReturn(java.util.Optional.empty()); } private void withScm(long blame) { @@ -376,7 +376,7 @@ public class IssueCreationDateCalculatorTest { if (scmInfo == null) { scmInfo = mock(ScmInfo.class); when(scmInfoRepository.getScmInfo(component)) - .thenReturn(Optional.of(scmInfo)); + .thenReturn(java.util.Optional.empty()); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoDbLoaderTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoDbLoaderTest.java index bd1845e4832..a231f5512d6 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoDbLoaderTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoDbLoaderTest.java @@ -83,7 +83,7 @@ public class ScmInfoDbLoaderTest { assertThat(scmInfo.getAllChangesets()).hasSize(1); assertThat(scmInfo.fileHash()).isEqualTo(hash); - assertThat(logTester.logs(TRACE)).containsOnly("Reading SCM info from db for file 'FILE_UUID'"); + assertThat(logTester.logs(TRACE)).containsOnly("Reading SCM info from DB for file 'FILE_UUID'"); } @Test @@ -101,7 +101,7 @@ public class ScmInfoDbLoaderTest { DbScmInfo scmInfo = underTest.getScmInfo(FILE).get(); assertThat(scmInfo.getAllChangesets()).hasSize(1); assertThat(scmInfo.fileHash()).isEqualTo(hash); - assertThat(logTester.logs(TRACE)).containsOnly("Reading SCM info from db for file 'mergeFileUuid'"); + assertThat(logTester.logs(TRACE)).containsOnly("Reading SCM info from DB for file 'mergeFileUuid'"); } @Test @@ -111,7 +111,7 @@ public class ScmInfoDbLoaderTest { Optional scmInfo = underTest.getScmInfo(FILE); - assertThat(logTester.logs(TRACE)).containsOnly("Reading SCM info from db for file 'FILE_UUID'"); + assertThat(logTester.logs(TRACE)).containsOnly("Reading SCM info from DB for file 'FILE_UUID'"); assertThat(scmInfo).isEmpty(); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoRepositoryImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoRepositoryImplTest.java index aa6522f5a31..b66f5b2281e 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoRepositoryImplTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoRepositoryImplTest.java @@ -50,7 +50,6 @@ import org.sonar.server.computation.task.projectanalysis.source.SourceHashReposi import org.sonar.server.computation.task.projectanalysis.source.SourceLinesDiff; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.guava.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -93,7 +92,7 @@ public class ScmInfoRepositoryImplTest { public void return_empty_if_component_is_not_file() { Component c = mock(Component.class); when(c.getType()).thenReturn(Type.DIRECTORY); - assertThat(underTest.getScmInfo(c)).isAbsent(); + assertThat(underTest.getScmInfo(c)).isEmpty(); } @Test @@ -205,7 +204,7 @@ public class ScmInfoRepositoryImplTest { @Test public void generate_scm_info_for_new_and_changed_lines_when_report_is_empty() { createDbScmInfoWithOneLine("hash"); - when(diff.getMatchingLines(FILE)).thenReturn(new int[] {1, 0, 0}); + when(diff.computeMatchingLines(FILE)).thenReturn(new int[] {1, 0, 0}); addFileSourceInReport(3); ScmInfo scmInfo = underTest.getScmInfo(FILE).get(); assertThat(scmInfo.getAllChangesets()).hasSize(3); @@ -215,7 +214,7 @@ public class ScmInfoRepositoryImplTest { assertChangeset(scmInfo.getChangesetForLine(3), null, null, analysisDate.getTime()); verify(dbLoader).getScmInfo(FILE); - verify(diff).getMatchingLines(FILE); + verify(diff).computeMatchingLines(FILE); verifyNoMoreInteractions(dbLoader); verifyZeroInteractions(sourceHashRepository); verifyNoMoreInteractions(diff); @@ -235,7 +234,7 @@ public class ScmInfoRepositoryImplTest { BatchReportReader batchReportReader = mock(BatchReportReader.class); ScmInfoRepositoryImpl underTest = new ScmInfoRepositoryImpl(batchReportReader, analysisMetadata, dbLoader, diff, sourceHashRepository); - assertThat(underTest.getScmInfo(component)).isAbsent(); + assertThat(underTest.getScmInfo(component)).isEmpty(); verifyZeroInteractions(batchReportReader, dbLoader); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoRepositoryRule.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoRepositoryRule.java index 0c42e7bde71..613ad85f791 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoRepositoryRule.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/scm/ScmInfoRepositoryRule.java @@ -19,9 +19,9 @@ */ package org.sonar.server.computation.task.projectanalysis.scm; -import com.google.common.base.Optional; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.IntStream; import org.junit.rules.ExternalResource; @@ -42,7 +42,7 @@ public class ScmInfoRepositoryRule extends ExternalResource implements ScmInfoRe public Optional getScmInfo(Component component) { checkNotNull(component, "Component cannot be bull"); ScmInfo scmInfo = scmInfoByFileRef.get(component.getReportAttributes().getRef()); - return Optional.fromNullable(scmInfo); + return Optional.ofNullable(scmInfo); } public ScmInfoRepositoryRule setScmInfo(int fileRef, Changeset... changesetList) { diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/ComputeFileSourceDataTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/ComputeFileSourceDataTest.java index a0eb78a925d..c6282f6dcc8 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/ComputeFileSourceDataTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/ComputeFileSourceDataTest.java @@ -22,6 +22,7 @@ package org.sonar.server.computation.task.projectanalysis.source; import com.google.common.collect.Lists; import org.junit.Test; import org.sonar.db.protobuf.DbFileSources; +import org.sonar.server.computation.task.projectanalysis.source.linereader.LineReader; import static com.google.common.collect.Lists.newArrayList; import static org.assertj.core.api.Assertions.assertThat; diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/CoverageLineReaderTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/CoverageLineReaderTest.java deleted file mode 100644 index fa43ff268eb..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/CoverageLineReaderTest.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program 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. - * - * This program 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.task.projectanalysis.source; - -import java.util.Collections; -import org.junit.Test; -import org.sonar.db.protobuf.DbFileSources; -import org.sonar.scanner.protocol.output.ScannerReport; - -import static com.google.common.collect.Lists.newArrayList; -import static org.assertj.core.api.Assertions.assertThat; - -public class CoverageLineReaderTest { - - @Test - public void set_coverage() { - CoverageLineReader computeCoverageLine = new CoverageLineReader(newArrayList(ScannerReport.LineCoverage.newBuilder() - .setLine(1) - .setConditions(10) - .setHits(true) - .setCoveredConditions(2) - .build()).iterator()); - - DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1); - computeCoverageLine.read(lineBuilder); - - assertThat(lineBuilder.getLineHits()).isEqualTo(1); - assertThat(lineBuilder.getConditions()).isEqualTo(10); - assertThat(lineBuilder.getCoveredConditions()).isEqualTo(2); - } - - // Some tools are only able to report condition coverage - @Test - public void set_coverage_only_conditions() { - CoverageLineReader computeCoverageLine = new CoverageLineReader(newArrayList(ScannerReport.LineCoverage.newBuilder() - .setLine(1) - .setConditions(10) - .setCoveredConditions(2) - .build()).iterator()); - - DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1); - computeCoverageLine.read(lineBuilder); - - assertThat(lineBuilder.hasLineHits()).isFalse(); - assertThat(lineBuilder.getConditions()).isEqualTo(10); - } - - @Test - public void set_coverage_on_uncovered_lines() { - CoverageLineReader computeCoverageLine = new CoverageLineReader(newArrayList(ScannerReport.LineCoverage.newBuilder() - .setLine(1) - .setHits(false) - .build()).iterator()); - - DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1); - computeCoverageLine.read(lineBuilder); - - assertThat(lineBuilder.hasLineHits()).isTrue(); - assertThat(lineBuilder.getLineHits()).isEqualTo(0); - } - - @Test - public void nothing_to_do_when_no_coverage_info() { - CoverageLineReader computeCoverageLine = new CoverageLineReader(Collections.emptyList().iterator()); - - DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1); - computeCoverageLine.read(lineBuilder); - - assertThat(lineBuilder.hasLineHits()).isFalse(); - assertThat(lineBuilder.hasConditions()).isFalse(); - assertThat(lineBuilder.hasCoveredConditions()).isFalse(); - } - - @Test - public void nothing_to_do_when_no_coverage_info_for_current_line() { - CoverageLineReader computeCoverageLine = new CoverageLineReader(newArrayList( - ScannerReport.LineCoverage.newBuilder() - .setLine(1) - .setConditions(10) - .setHits(true) - .setCoveredConditions(2) - .build() - // No coverage info on line 2 - ).iterator()); - - DbFileSources.Line.Builder line2Builder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(2); - computeCoverageLine.read(line2Builder); - - assertThat(line2Builder.hasLineHits()).isFalse(); - assertThat(line2Builder.hasConditions()).isFalse(); - assertThat(line2Builder.hasCoveredConditions()).isFalse(); - } - - @Test - public void nothing_to_do_when_no_coverage_info_for_next_line() { - CoverageLineReader computeCoverageLine = new CoverageLineReader(newArrayList( - ScannerReport.LineCoverage.newBuilder() - .setLine(1) - .setConditions(10) - .setHits(true) - .setCoveredConditions(2) - .build() - // No coverage info on line 2 - ).iterator()); - - DbFileSources.Data.Builder fileSourceBuilder = DbFileSources.Data.newBuilder(); - DbFileSources.Line.Builder line1Builder = fileSourceBuilder.addLinesBuilder().setLine(1); - DbFileSources.Line.Builder line2Builder = fileSourceBuilder.addLinesBuilder().setLine(2); - computeCoverageLine.read(line1Builder); - computeCoverageLine.read(line2Builder); - - assertThat(line2Builder.hasLineHits()).isFalse(); - assertThat(line2Builder.hasConditions()).isFalse(); - assertThat(line2Builder.hasCoveredConditions()).isFalse(); - } - - @Test - public void does_not_set_deprecated_coverage_fields() { - CoverageLineReader computeCoverageLine = new CoverageLineReader(newArrayList(ScannerReport.LineCoverage.newBuilder() - .setLine(1) - .setConditions(10) - .setHits(true) - .setCoveredConditions(2) - .build()).iterator()); - - DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1); - computeCoverageLine.read(lineBuilder); - - assertThat(lineBuilder.hasDeprecatedUtLineHits()).isFalse(); - assertThat(lineBuilder.hasDeprecatedUtConditions()).isFalse(); - assertThat(lineBuilder.hasDeprecatedUtCoveredConditions()).isFalse(); - assertThat(lineBuilder.hasDeprecatedOverallLineHits()).isFalse(); - assertThat(lineBuilder.hasDeprecatedOverallConditions()).isFalse(); - assertThat(lineBuilder.hasDeprecatedOverallCoveredConditions()).isFalse(); - assertThat(lineBuilder.hasDeprecatedItLineHits()).isFalse(); - assertThat(lineBuilder.hasDeprecatedItConditions()).isFalse(); - assertThat(lineBuilder.hasDeprecatedItCoveredConditions()).isFalse(); - } - -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/DuplicationLineReaderTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/DuplicationLineReaderTest.java deleted file mode 100644 index 3aa0886c1c1..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/DuplicationLineReaderTest.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program 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. - * - * This program 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.task.projectanalysis.source; - -import com.google.common.collect.ImmutableSet; -import java.util.Arrays; -import java.util.Collections; -import org.junit.Test; -import org.sonar.db.protobuf.DbFileSources; -import org.sonar.server.computation.task.projectanalysis.component.Component; -import org.sonar.server.computation.task.projectanalysis.component.ReportComponent; -import org.sonar.server.computation.task.projectanalysis.duplication.CrossProjectDuplicate; -import org.sonar.server.computation.task.projectanalysis.duplication.Duplicate; -import org.sonar.server.computation.task.projectanalysis.duplication.Duplication; -import org.sonar.server.computation.task.projectanalysis.duplication.InProjectDuplicate; -import org.sonar.server.computation.task.projectanalysis.duplication.InnerDuplicate; -import org.sonar.server.computation.task.projectanalysis.duplication.TextBlock; - -import static org.assertj.core.api.Assertions.assertThat; - -public class DuplicationLineReaderTest { - - DbFileSources.Data.Builder sourceData = DbFileSources.Data.newBuilder(); - DbFileSources.Line.Builder line1 = sourceData.addLinesBuilder().setSource("line1").setLine(1); - DbFileSources.Line.Builder line2 = sourceData.addLinesBuilder().setSource("line2").setLine(2); - DbFileSources.Line.Builder line3 = sourceData.addLinesBuilder().setSource("line3").setLine(3); - DbFileSources.Line.Builder line4 = sourceData.addLinesBuilder().setSource("line4").setLine(4); - - @Test - public void read_nothing() { - DuplicationLineReader reader = new DuplicationLineReader(Collections.emptySet()); - - reader.read(line1); - - assertThat(line1.getDuplicationList()).isEmpty(); - } - - @Test - public void read_duplication_with_duplicates_on_same_file() { - DuplicationLineReader reader = duplicationLineReader(duplication(1, 2, innerDuplicate(3, 4))); - - reader.read(line1); - reader.read(line2); - reader.read(line3); - reader.read(line4); - - assertThat(line1.getDuplicationList()).containsExactly(1); - assertThat(line2.getDuplicationList()).containsExactly(1); - assertThat(line3.getDuplicationList()).containsExactly(2); - assertThat(line4.getDuplicationList()).containsExactly(2); - } - - @Test - public void read_duplication_with_duplicates_on_other_file() { - DuplicationLineReader reader = duplicationLineReader( - duplication( - 1, 2, - new InProjectDuplicate(fileComponent(1).build(), new TextBlock(3, 4)))); - - reader.read(line1); - reader.read(line2); - reader.read(line3); - reader.read(line4); - - assertThat(line1.getDuplicationList()).containsExactly(1); - assertThat(line2.getDuplicationList()).containsExactly(1); - assertThat(line3.getDuplicationList()).isEmpty(); - assertThat(line4.getDuplicationList()).isEmpty(); - } - - @Test - public void read_duplication_with_duplicates_on_other_file_from_other_project() { - DuplicationLineReader reader = duplicationLineReader( - duplication( - 1, 2, - new CrossProjectDuplicate("other-component-key-from-another-project", new TextBlock(3, 4)))); - - reader.read(line1); - reader.read(line2); - reader.read(line3); - reader.read(line4); - - assertThat(line1.getDuplicationList()).containsExactly(1); - assertThat(line2.getDuplicationList()).containsExactly(1); - assertThat(line3.getDuplicationList()).isEmpty(); - assertThat(line4.getDuplicationList()).isEmpty(); - } - - @Test - public void read_many_duplications() { - DuplicationLineReader reader = duplicationLineReader( - duplication( - 1, 1, - innerDuplicate(2, 2)), - duplication( - 1, 2, - innerDuplicate(3, 4)) - ); - - reader.read(line1); - reader.read(line2); - reader.read(line3); - reader.read(line4); - - assertThat(line1.getDuplicationList()).containsExactly(1, 2); - assertThat(line2.getDuplicationList()).containsExactly(2, 3); - assertThat(line3.getDuplicationList()).containsExactly(4); - assertThat(line4.getDuplicationList()).containsExactly(4); - } - - @Test - public void should_be_sorted_by_line_block() { - DuplicationLineReader reader = duplicationLineReader( - duplication( - 2, 2, - innerDuplicate(4, 4)), - duplication( - 1, 1, - innerDuplicate(3, 3)) - ); - - reader.read(line1); - reader.read(line2); - reader.read(line3); - reader.read(line4); - - assertThat(line1.getDuplicationList()).containsExactly(1); - assertThat(line2.getDuplicationList()).containsExactly(2); - assertThat(line3.getDuplicationList()).containsExactly(3); - assertThat(line4.getDuplicationList()).containsExactly(4); - } - - @Test - public void should_be_sorted_by_line_length() { - DuplicationLineReader reader = duplicationLineReader( - duplication( - 1, 2, - innerDuplicate(3, 4)), - duplication( - 1, 1, - innerDuplicate(4, 4) - ) - ); - - reader.read(line1); - reader.read(line2); - reader.read(line3); - reader.read(line4); - - assertThat(line1.getDuplicationList()).containsExactly(1, 2); - assertThat(line2.getDuplicationList()).containsExactly(2); - assertThat(line3.getDuplicationList()).containsExactly(3); - assertThat(line4.getDuplicationList()).containsExactly(3, 4); - } - - private static ReportComponent.Builder fileComponent(int ref) { - return ReportComponent.builder(Component.Type.FILE, ref); - } - - private static DuplicationLineReader duplicationLineReader(Duplication... duplications) { - return new DuplicationLineReader(ImmutableSet.copyOf(Arrays.asList(duplications))); - } - - private static Duplication duplication(int originalStart, int originalEnd, Duplicate... duplicates) { - return new Duplication(new TextBlock(originalStart, originalEnd), Arrays.asList(duplicates)); - } - - private static InnerDuplicate innerDuplicate(int start, int end) { - return new InnerDuplicate(new TextBlock(start, end)); - } - -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/HighlightingLineReaderTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/HighlightingLineReaderTest.java deleted file mode 100644 index 090e9bd309f..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/HighlightingLineReaderTest.java +++ /dev/null @@ -1,324 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program 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. - * - * This program 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.task.projectanalysis.source; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Random; -import org.junit.Rule; -import org.junit.Test; -import org.sonar.api.utils.log.LogTester; -import org.sonar.db.protobuf.DbFileSources; -import org.sonar.scanner.protocol.output.ScannerReport; -import org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType; -import org.sonar.scanner.protocol.output.ScannerReport.TextRange; -import org.sonar.server.computation.task.projectanalysis.component.Component; -import org.sonar.server.computation.task.projectanalysis.source.RangeOffsetConverter.RangeOffsetConverterException; - -import static com.google.common.collect.ImmutableMap.of; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import static org.sonar.api.utils.log.LoggerLevel.WARN; -import static org.sonar.db.protobuf.DbFileSources.Data.newBuilder; -import static org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType.ANNOTATION; -import static org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType.COMMENT; -import static org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType.CONSTANT; -import static org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType.CPP_DOC; -import static org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType.HIGHLIGHTING_STRING; -import static org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType.KEYWORD; -import static org.sonar.server.computation.task.projectanalysis.component.ReportComponent.builder; - -public class HighlightingLineReaderTest { - - @Rule - public LogTester logTester = new LogTester(); - - private static final Component FILE = builder(Component.Type.FILE, 1).setUuid("FILE_UUID").setKey("FILE_KEY").build(); - - private static final int DEFAULT_LINE_LENGTH = 5; - - private static final int LINE_1 = 1; - private static final int LINE_2 = 2; - private static final int LINE_3 = 3; - private static final int LINE_4 = 4; - - private static final String RANGE_LABEL_1 = "1,2"; - private static final String RANGE_LABEL_2 = "2,3"; - private static final String RANGE_LABEL_3 = "3,4"; - private static final String RANGE_LABEL_4 = "0,2"; - private static final String RANGE_LABEL_5 = "0,3"; - - private RangeOffsetConverter rangeOffsetConverter = mock(RangeOffsetConverter.class); - - private DbFileSources.Data.Builder sourceData = newBuilder(); - private DbFileSources.Line.Builder line1 = sourceData.addLinesBuilder().setSource("line1").setLine(1); - private DbFileSources.Line.Builder line2 = sourceData.addLinesBuilder().setSource("line2").setLine(2); - private DbFileSources.Line.Builder line3 = sourceData.addLinesBuilder().setSource("line3").setLine(3); - private DbFileSources.Line.Builder line4 = sourceData.addLinesBuilder().setSource("line4").setLine(4); - - @Test - public void nothing_to_read() { - HighlightingLineReader highlightingLineReader = newReader(Collections.emptyMap()); - - DbFileSources.Line.Builder lineBuilder = newBuilder().addLinesBuilder().setLine(1); - highlightingLineReader.read(lineBuilder); - - assertThat(lineBuilder.hasHighlighting()).isFalse(); - } - - @Test - public void read_one_line() { - HighlightingLineReader highlightingLineReader = newReader(of( - newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_1), ANNOTATION)); - - highlightingLineReader.read(line1); - - assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a"); - } - - @Test - public void read_many_lines() { - HighlightingLineReader highlightingLineReader = newReader(of( - newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_1), ANNOTATION, - newSingleLineTextRangeWithExpectingLabel(LINE_2, RANGE_LABEL_2), COMMENT, - newSingleLineTextRangeWithExpectingLabel(LINE_4, RANGE_LABEL_3), CONSTANT)); - - highlightingLineReader.read(line1); - highlightingLineReader.read(line2); - highlightingLineReader.read(line3); - highlightingLineReader.read(line4); - - assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a"); - assertThat(line2.getHighlighting()).isEqualTo(RANGE_LABEL_2 + ",cd"); - assertThat(line4.getHighlighting()).isEqualTo(RANGE_LABEL_3 + ",c"); - } - - @Test - public void supports_highlighting_over_multiple_lines_including_an_empty_one() { - List syntaxHighlightingList = new ArrayList<>(); - addHighlighting(syntaxHighlightingList, 1, 0, 1, 7, KEYWORD); // package - addHighlighting(syntaxHighlightingList, 2, 0, 4, 6, CPP_DOC); // comment over 3 lines - addHighlighting(syntaxHighlightingList, 5, 0, 5, 6, KEYWORD); // public - addHighlighting(syntaxHighlightingList, 5, 7, 5, 12, KEYWORD); // class - HighlightingLineReader highlightingLineReader = new HighlightingLineReader(FILE, syntaxHighlightingList.iterator(), new RangeOffsetConverter()); - - DbFileSources.Line.Builder[] builders = new DbFileSources.Line.Builder[] { - addSourceLine(highlightingLineReader, 1, "package example;"), - addSourceLine(highlightingLineReader, 2, "/*"), - addSourceLine(highlightingLineReader, 3, ""), - addSourceLine(highlightingLineReader, 4, " foo*/"), - addSourceLine(highlightingLineReader, 5, "public class One {"), - addSourceLine(highlightingLineReader, 6, "}") - }; - - assertThat(builders) - .extracting("highlighting") - .containsExactly( - "0,7,k", - "0,2,cppd", - "", - "0,6,cppd", - "0,6,k;7,12,k", - ""); - } - - private DbFileSources.Line.Builder addSourceLine(HighlightingLineReader highlightingLineReader, int line, String source) { - DbFileSources.Line.Builder lineBuilder = sourceData.addLinesBuilder().setSource(source).setLine(line); - highlightingLineReader.read(lineBuilder); - return lineBuilder; - } - - private void addHighlighting(List syntaxHighlightingList, - int startLine, int startOffset, - int endLine, int endOffset, - HighlightingType type) { - TextRange.Builder textRangeBuilder = TextRange.newBuilder(); - ScannerReport.SyntaxHighlightingRule.Builder ruleBuilder = ScannerReport.SyntaxHighlightingRule.newBuilder(); - syntaxHighlightingList.add(ruleBuilder - .setRange(textRangeBuilder - .setStartLine(startLine).setEndLine(endLine) - .setStartOffset(startOffset).setEndOffset(endOffset) - .build()) - .setType(type) - .build()); - } - - @Test - public void read_many_syntax_highlighting_on_same_line() { - HighlightingLineReader highlightingLineReader = newReader(of( - newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_1), ANNOTATION, - newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_2), COMMENT)); - - highlightingLineReader.read(line1); - - assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a;" + RANGE_LABEL_2 + ",cd"); - } - - @Test - public void read_one_syntax_highlighting_on_many_lines() { - // This highlighting begin on line 1 and finish on line 3 - TextRange textRange = newTextRange(LINE_1, LINE_3); - when(rangeOffsetConverter.offsetToString(textRange, LINE_1, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_1); - when(rangeOffsetConverter.offsetToString(textRange, LINE_2, 6)).thenReturn(RANGE_LABEL_2); - when(rangeOffsetConverter.offsetToString(textRange, LINE_3, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_3); - - HighlightingLineReader highlightingLineReader = newReader(of(textRange, ANNOTATION)); - - highlightingLineReader.read(line1); - DbFileSources.Line.Builder line2 = sourceData.addLinesBuilder().setSource("line 2").setLine(2); - highlightingLineReader.read(line2); - highlightingLineReader.read(line3); - - assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a"); - assertThat(line2.getHighlighting()).isEqualTo(RANGE_LABEL_2 + ",a"); - assertThat(line3.getHighlighting()).isEqualTo(RANGE_LABEL_3 + ",a"); - } - - @Test - public void read_many_syntax_highlighting_on_many_lines() { - TextRange textRange1 = newTextRange(LINE_1, LINE_3); - when(rangeOffsetConverter.offsetToString(textRange1, LINE_1, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_1); - when(rangeOffsetConverter.offsetToString(textRange1, LINE_2, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_2); - when(rangeOffsetConverter.offsetToString(textRange1, LINE_3, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_3); - - TextRange textRange2 = newTextRange(LINE_2, LINE_4); - when(rangeOffsetConverter.offsetToString(textRange2, LINE_2, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_2); - when(rangeOffsetConverter.offsetToString(textRange2, LINE_3, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_2); - when(rangeOffsetConverter.offsetToString(textRange2, LINE_4, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_4); - - TextRange textRange3 = newTextRange(LINE_2, LINE_2); - when(rangeOffsetConverter.offsetToString(textRange3, LINE_2, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_5); - - HighlightingLineReader highlightingLineReader = newReader(of( - textRange1, ANNOTATION, - textRange2, HIGHLIGHTING_STRING, - textRange3, COMMENT)); - - highlightingLineReader.read(line1); - highlightingLineReader.read(line2); - highlightingLineReader.read(line3); - highlightingLineReader.read(line4); - - assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a"); - assertThat(line2.getHighlighting()).isEqualTo(RANGE_LABEL_2 + ",a;" + RANGE_LABEL_2 + ",s;" + RANGE_LABEL_5 + ",cd"); - assertThat(line3.getHighlighting()).isEqualTo(RANGE_LABEL_3 + ",a;" + RANGE_LABEL_2 + ",s"); - assertThat(line4.getHighlighting()).isEqualTo(RANGE_LABEL_4 + ",s"); - } - - @Test - public void read_highlighting_declared_on_a_whole_line() { - TextRange textRange = newTextRange(LINE_1, LINE_2); - when(rangeOffsetConverter.offsetToString(textRange, LINE_1, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_1); - when(rangeOffsetConverter.offsetToString(textRange, LINE_2, DEFAULT_LINE_LENGTH)).thenReturn(""); - - HighlightingLineReader highlightingLineReader = newReader(of(textRange, ANNOTATION)); - - highlightingLineReader.read(line1); - highlightingLineReader.read(line2); - highlightingLineReader.read(line3); - - assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a"); - // Nothing should be set on line 2 - assertThat(line2.getHighlighting()).isEmpty(); - assertThat(line3.getHighlighting()).isEmpty(); - } - - @Test - public void not_fail_and_stop_processing_when_range_offset_converter_throw_RangeOffsetConverterException() { - TextRange textRange1 = newTextRange(LINE_1, LINE_1); - doThrow(RangeOffsetConverterException.class).when(rangeOffsetConverter).offsetToString(textRange1, LINE_1, DEFAULT_LINE_LENGTH); - - HighlightingLineReader highlightingLineReader = newReader(of( - textRange1, HighlightingType.ANNOTATION, - newSingleLineTextRangeWithExpectingLabel(LINE_2, RANGE_LABEL_1), HIGHLIGHTING_STRING)); - - highlightingLineReader.read(line1); - highlightingLineReader.read(line2); - - assertNoHighlighting(); - assertThat(logTester.logs(WARN)).isNotEmpty(); - } - - @Test - public void keep_existing_processed_highlighting_when_range_offset_converter_throw_RangeOffsetConverterException() { - TextRange textRange2 = newTextRange(LINE_2, LINE_2); - doThrow(RangeOffsetConverterException.class).when(rangeOffsetConverter).offsetToString(textRange2, LINE_2, DEFAULT_LINE_LENGTH); - - HighlightingLineReader highlightingLineReader = newReader(of( - newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_1), ANNOTATION, - textRange2, HIGHLIGHTING_STRING)); - - highlightingLineReader.read(line1); - highlightingLineReader.read(line2); - - assertThat(line1.hasHighlighting()).isTrue(); - assertThat(line2.hasHighlighting()).isFalse(); - assertThat(logTester.logs(WARN)).isNotEmpty(); - } - - @Test - public void display_file_key_in_warning_when_range_offset_converter_throw_RangeOffsetConverterException() { - TextRange textRange1 = newTextRange(LINE_1, LINE_1); - doThrow(RangeOffsetConverterException.class).when(rangeOffsetConverter).offsetToString(textRange1, LINE_1, DEFAULT_LINE_LENGTH); - HighlightingLineReader highlightingLineReader = newReader(of(textRange1, ANNOTATION)); - - highlightingLineReader.read(line1); - - assertThat(logTester.logs(WARN)).containsOnly("Inconsistency detected in Highlighting data. Highlighting will be ignored for file 'FILE_KEY'"); - } - - private HighlightingLineReader newReader(Map textRangeByType) { - List syntaxHighlightingList = new ArrayList<>(); - for (Map.Entry entry : textRangeByType.entrySet()) { - syntaxHighlightingList.add(ScannerReport.SyntaxHighlightingRule.newBuilder() - .setRange(entry.getKey()) - .setType(entry.getValue()) - .build()); - } - return new HighlightingLineReader(FILE, syntaxHighlightingList.iterator(), rangeOffsetConverter); - } - - private static TextRange newTextRange(int startLine, int enLine) { - Random random = new Random(); - return TextRange.newBuilder() - .setStartLine(startLine).setEndLine(enLine) - // Offsets are not used by the reader - .setStartOffset(random.nextInt()).setEndOffset(random.nextInt()) - .build(); - } - - private TextRange newSingleLineTextRangeWithExpectingLabel(int line, String rangeLabel) { - TextRange textRange = newTextRange(line, line); - when(rangeOffsetConverter.offsetToString(textRange, line, DEFAULT_LINE_LENGTH)).thenReturn(rangeLabel); - return textRange; - } - - private void assertNoHighlighting() { - assertThat(line1.hasHighlighting()).isFalse(); - assertThat(line2.hasHighlighting()).isFalse(); - assertThat(line3.hasHighlighting()).isFalse(); - assertThat(line4.hasHighlighting()).isFalse(); - } - -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/RangeOffsetConverterTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/RangeOffsetConverterTest.java index 99c03ad4317..88c19784e19 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/RangeOffsetConverterTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/RangeOffsetConverterTest.java @@ -23,7 +23,8 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.sonar.scanner.protocol.output.ScannerReport; -import org.sonar.server.computation.task.projectanalysis.source.RangeOffsetConverter.RangeOffsetConverterException; +import org.sonar.server.computation.task.projectanalysis.source.linereader.RangeOffsetConverter; +import org.sonar.server.computation.task.projectanalysis.source.linereader.RangeOffsetConverter.RangeOffsetConverterException; import static org.assertj.core.api.Assertions.assertThat; diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/ScmLineReaderTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/ScmLineReaderTest.java deleted file mode 100644 index 1abd95527f9..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/ScmLineReaderTest.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program 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. - * - * This program 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.task.projectanalysis.source; - -import com.google.common.collect.ImmutableMap; -import java.util.Collections; -import java.util.Map; -import org.junit.Test; -import org.sonar.db.protobuf.DbFileSources; -import org.sonar.server.computation.task.projectanalysis.scm.Changeset; -import org.sonar.server.computation.task.projectanalysis.scm.ScmInfo; -import org.sonar.server.computation.task.projectanalysis.scm.ScmInfoImpl; - -import static org.assertj.core.api.Assertions.assertThat; - -public class ScmLineReaderTest { - - @Test - public void set_scm() { - ScmInfo scmInfo = new ScmInfoImpl(Collections.singletonMap(1, - Changeset.newChangesetBuilder() - .setAuthor("john") - .setDate(123_456_789L) - .setRevision("rev-1") - .build())); - - ScmLineReader lineScm = new ScmLineReader(scmInfo); - - DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1); - lineScm.read(lineBuilder); - - assertThat(lineBuilder.getScmAuthor()).isEqualTo("john"); - assertThat(lineBuilder.getScmDate()).isEqualTo(123_456_789L); - assertThat(lineBuilder.getScmRevision()).isEqualTo("rev-1"); - } - - @Test - public void set_scm_with_minim_fields() { - ScmInfo scmInfo = new ScmInfoImpl(Collections.singletonMap(1, - Changeset.newChangesetBuilder() - .setDate(123456789L) - .build())); - - ScmLineReader lineScm = new ScmLineReader(scmInfo); - - DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1); - lineScm.read(lineBuilder); - - assertThat(lineBuilder.hasScmAuthor()).isFalse(); - assertThat(lineBuilder.getScmDate()).isEqualTo(123456789L); - assertThat(lineBuilder.hasScmRevision()).isFalse(); - - } - - @Test - public void getLatestChange_returns_changeset_with_highest_date_of_read_lines() { - long refDate = 123_456_789L; - Changeset changeset0 = Changeset.newChangesetBuilder().setDate(refDate - 636).setRevision("rev-1").build(); - Changeset changeset1 = Changeset.newChangesetBuilder().setDate(refDate + 1).setRevision("rev-2").build(); - Changeset changeset2 = Changeset.newChangesetBuilder().setDate(refDate + 2).build(); - ScmInfo scmInfo = new ScmInfoImpl(setup8LinesChangeset(changeset0, changeset1, changeset2)); - - ScmLineReader lineScm = new ScmLineReader(scmInfo); - - // before any line is read, the latest changes are null - assertThat(lineScm.getLatestChange()).isNull(); - assertThat(lineScm.getLatestChangeWithRevision()).isNull(); - - // read line 1, only one changeset => 0 - readLineAndAssertLatestChanges(lineScm, 1, changeset0, changeset0); - - // read line 2, latest changeset is 1 - readLineAndAssertLatestChanges(lineScm, 2, changeset1, changeset1); - - // read line 3, latest changeset is still 1 - readLineAndAssertLatestChanges(lineScm, 3, changeset1, changeset1); - - // read line 4, latest changeset is now 2 - readLineAndAssertLatestChanges(lineScm, 4, changeset2, changeset1); - - // read line 5 to 8, there will never be any changeset more recent than 2 - readLineAndAssertLatestChanges(lineScm, 5, changeset2, changeset1); - readLineAndAssertLatestChanges(lineScm, 6, changeset2, changeset1); - readLineAndAssertLatestChanges(lineScm, 7, changeset2, changeset1); - readLineAndAssertLatestChanges(lineScm, 8, changeset2, changeset1); - } - - private static Map setup8LinesChangeset(Changeset changeset0, Changeset changeset1, Changeset changeset2) { - return ImmutableMap.builder() - .put(1, changeset0) - .put(2, changeset1) - .put(3, changeset1) - .put(4, changeset2) - .put(5, changeset0) - .put(6, changeset1) - .put(7, changeset0) - .put(8, changeset0).build(); - } - - private void readLineAndAssertLatestChanges(ScmLineReader lineScm, int line, Changeset expectedChangeset, Changeset expectedChangesetWithRevision) { - DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(line); - lineScm.read(lineBuilder); - assertThat(lineScm.getLatestChange()).isSameAs(expectedChangeset); - assertThat(lineScm.getLatestChangeWithRevision()).isSameAs(expectedChangesetWithRevision); - - } - -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesDiffFinderTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesDiffFinderTest.java index 99394391a66..7fe94520fa5 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesDiffFinderTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesDiffFinderTest.java @@ -43,7 +43,7 @@ public class SourceLinesDiffFinderTest { report.add("line - 3"); report.add("line - 4"); - int[] diff = new SourceLinesDiffFinder(database, report).findMatchingLines(); + int[] diff = new SourceLinesDiffFinder().findMatchingLines(database, report); assertThat(diff).containsExactly(1, 2, 3, 4, 5); } @@ -74,7 +74,7 @@ public class SourceLinesDiffFinderTest { report.add(" }\n"); report.add("}\n"); - int[] diff = new SourceLinesDiffFinder(database, report).findMatchingLines(); + int[] diff = new SourceLinesDiffFinder().findMatchingLines(database, report); assertThat(diff).containsExactly(1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 5, 6, 7); } @@ -92,7 +92,7 @@ public class SourceLinesDiffFinderTest { report.add("line - 2"); report.add("line - 3"); - int[] diff = new SourceLinesDiffFinder(database, report).findMatchingLines(); + int[] diff = new SourceLinesDiffFinder().findMatchingLines(database, report); assertThat(diff).containsExactly(0, 0, 3, 4); } @@ -111,7 +111,7 @@ public class SourceLinesDiffFinderTest { report.add("line - 2 - modified"); report.add("line - 3 - modified"); - int[] diff = new SourceLinesDiffFinder(database, report).findMatchingLines(); + int[] diff = new SourceLinesDiffFinder().findMatchingLines(database, report); assertThat(diff).containsExactly(1, 2, 0, 0); } @@ -134,7 +134,7 @@ public class SourceLinesDiffFinderTest { report.add("line - 4"); report.add("line - 5"); - int[] diff = new SourceLinesDiffFinder(database, report).findMatchingLines(); + int[] diff = new SourceLinesDiffFinder().findMatchingLines(database, report); assertThat(diff).containsExactly(1, 2, 0, 0, 5, 6); } @@ -153,7 +153,7 @@ public class SourceLinesDiffFinderTest { report.add("line - 1"); report.add("line - 2"); - int[] diff = new SourceLinesDiffFinder(database, report).findMatchingLines(); + int[] diff = new SourceLinesDiffFinder().findMatchingLines(database, report); assertThat(diff).containsExactly(0, 0, 1, 2, 3); } @@ -174,7 +174,7 @@ public class SourceLinesDiffFinderTest { report.add("line - 2"); report.add("line - 3"); - int[] diff = new SourceLinesDiffFinder(database, report).findMatchingLines(); + int[] diff = new SourceLinesDiffFinder().findMatchingLines(database, report); assertThat(diff).containsExactly(1, 2, 0, 0, 3, 4); } @@ -193,7 +193,7 @@ public class SourceLinesDiffFinderTest { report.add("line - new"); report.add("line - new"); - int[] diff = new SourceLinesDiffFinder(database, report).findMatchingLines(); + int[] diff = new SourceLinesDiffFinder().findMatchingLines(database, report); assertThat(diff).containsExactly(1, 2, 3, 0, 0); } @@ -212,7 +212,7 @@ public class SourceLinesDiffFinderTest { report.add("line - 1"); report.add("line - 2"); - int[] diff = new SourceLinesDiffFinder(database, report).findMatchingLines(); + int[] diff = new SourceLinesDiffFinder().findMatchingLines(database, report); assertThat(diff).containsExactly(1, 2, 3); } @@ -233,7 +233,7 @@ public class SourceLinesDiffFinderTest { report.add("line - 4"); report.add("line - 5"); - int[] diff = new SourceLinesDiffFinder(database, report).findMatchingLines(); + int[] diff = new SourceLinesDiffFinder().findMatchingLines(database, report); assertThat(diff).containsExactly(1, 2, 5, 6); } @@ -250,7 +250,7 @@ public class SourceLinesDiffFinderTest { report.add("line - 2"); report.add("line - 3"); - int[] diff = new SourceLinesDiffFinder(database, report).findMatchingLines(); + int[] diff = new SourceLinesDiffFinder().findMatchingLines(database, report); assertThat(diff).containsExactly(3, 4); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesDiffImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesDiffImplTest.java index 1bd89561ef5..c692c663686 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesDiffImplTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesDiffImplTest.java @@ -82,7 +82,7 @@ public class SourceLinesDiffImplTest { setFileContentInReport(FILE_REF, CONTENT); Component component = fileComponent(FILE_REF); - assertThat(underTest.getMatchingLines(component)).containsExactly(1, 2, 3, 4, 5, 6, 7); + assertThat(underTest.computeMatchingLines(component)).containsExactly(1, 2, 3, 4, 5, 6, 7); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesRepositoryImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesRepositoryImplTest.java index c59a7100820..1dacd1fe8de 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesRepositoryImplTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/SourceLinesRepositoryImplTest.java @@ -92,7 +92,7 @@ public class SourceLinesRepositoryImplTest { @Test public void fail_with_NPE_to_read_lines_on_null_component() { thrown.expect(NullPointerException.class); - thrown.expectMessage("Component should not be bull"); + thrown.expectMessage("Component should not be null"); underTest.readLines(null); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/SymbolsLineReaderTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/SymbolsLineReaderTest.java deleted file mode 100644 index e9b69da16c8..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/SymbolsLineReaderTest.java +++ /dev/null @@ -1,335 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program 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. - * - * This program 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.task.projectanalysis.source; - -import java.util.Arrays; -import org.junit.Rule; -import org.junit.Test; -import org.sonar.api.utils.log.LogTester; -import org.sonar.db.protobuf.DbFileSources; -import org.sonar.scanner.protocol.output.ScannerReport; -import org.sonar.scanner.protocol.output.ScannerReport.TextRange; -import org.sonar.server.computation.task.projectanalysis.component.Component; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import static org.sonar.api.utils.log.LoggerLevel.WARN; -import static org.sonar.server.computation.task.projectanalysis.component.ReportComponent.builder; - -public class SymbolsLineReaderTest { - - @Rule - public LogTester logTester = new LogTester(); - - private static final Component FILE = builder(Component.Type.FILE, 1).setUuid("FILE_UUID").setKey("FILE_KEY").build(); - - private static final int DEFAULT_LINE_LENGTH = 5; - - private static final int LINE_1 = 1; - private static final int LINE_2 = 2; - private static final int LINE_3 = 3; - private static final int LINE_4 = 4; - - private static final int OFFSET_0 = 0; - private static final int OFFSET_1 = 1; - private static final int OFFSET_2 = 2; - private static final int OFFSET_3 = 3; - private static final int OFFSET_4 = 4; - - private static final String RANGE_LABEL_1 = "1,2"; - private static final String RANGE_LABEL_2 = "2,3"; - private static final String RANGE_LABEL_3 = "3,4"; - private static final String RANGE_LABEL_4 = "0,2"; - - private RangeOffsetConverter rangeOffsetConverter = mock(RangeOffsetConverter.class); - - private DbFileSources.Data.Builder sourceData = DbFileSources.Data.newBuilder(); - private DbFileSources.Line.Builder line1 = sourceData.addLinesBuilder().setSource("line1").setLine(1); - private DbFileSources.Line.Builder line2 = sourceData.addLinesBuilder().setSource("line2").setLine(2); - private DbFileSources.Line.Builder line3 = sourceData.addLinesBuilder().setSource("line3").setLine(3); - private DbFileSources.Line.Builder line4 = sourceData.addLinesBuilder().setSource("line4").setLine(4); - - @Test - public void read_nothing() { - SymbolsLineReader symbolsLineReader = newReader(); - - symbolsLineReader.read(line1); - - assertThat(line1.getSymbols()).isEmpty(); - } - - @Test - public void read_symbols() { - SymbolsLineReader symbolsLineReader = newReader(newSymbol( - newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_2, OFFSET_4, RANGE_LABEL_1), - newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_1, OFFSET_3, RANGE_LABEL_2) - )); - - symbolsLineReader.read(line1); - symbolsLineReader.read(line2); - symbolsLineReader.read(line3); - - assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1"); - assertThat(line2.getSymbols()).isEmpty(); - assertThat(line3.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1"); - } - - @Test - public void read_symbols_with_reference_on_same_line() { - SymbolsLineReader symbolsLineReader = newReader(newSymbol( - newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_0, OFFSET_1, RANGE_LABEL_1), - newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_2, OFFSET_3, RANGE_LABEL_2) - )); - - symbolsLineReader.read(line1); - - assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1;" + RANGE_LABEL_2 + ",1"); - } - - @Test - public void read_symbols_with_two_references() { - SymbolsLineReader symbolsLineReader = newReader(newSymbol( - newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_2, OFFSET_4, RANGE_LABEL_1), - newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_1, OFFSET_3, RANGE_LABEL_2), - newSingleLineTextRangeWithExpectedLabel(LINE_2, OFFSET_0, OFFSET_2, RANGE_LABEL_3) - )); - - symbolsLineReader.read(line1); - symbolsLineReader.read(line2); - symbolsLineReader.read(line3); - - assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1"); - assertThat(line2.getSymbols()).isEqualTo(RANGE_LABEL_3 + ",1"); - assertThat(line3.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1"); - } - - @Test - public void read_symbols_with_two_references_on_the_same_line() { - SymbolsLineReader symbolsLineReader = newReader(newSymbol( - newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_2, OFFSET_3, RANGE_LABEL_1), - newSingleLineTextRangeWithExpectedLabel(LINE_2, OFFSET_0, OFFSET_1, RANGE_LABEL_2), - newSingleLineTextRangeWithExpectedLabel(LINE_2, OFFSET_2, OFFSET_3, RANGE_LABEL_3) - )); - - symbolsLineReader.read(line1); - symbolsLineReader.read(line2); - - assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1"); - assertThat(line2.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1;" + RANGE_LABEL_3 + ",1"); - } - - @Test - public void read_symbols_when_reference_line_is_before_declaration_line() { - SymbolsLineReader symbolsLineReader = newReader(newSymbol( - newSingleLineTextRangeWithExpectedLabel(LINE_2, OFFSET_3, OFFSET_4, RANGE_LABEL_1), - newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_1, OFFSET_2, RANGE_LABEL_2) - )); - - symbolsLineReader.read(line1); - symbolsLineReader.read(line2); - - assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1"); - assertThat(line2.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1"); - } - - @Test - public void read_many_symbols_on_lines() { - SymbolsLineReader symbolsLineReader = newReader( - newSymbol( - newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_1, OFFSET_2, RANGE_LABEL_1), - newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_2, OFFSET_3, RANGE_LABEL_2)), - newSymbol( - newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_3, OFFSET_4, RANGE_LABEL_3), - newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_0, OFFSET_1, RANGE_LABEL_4) - )); - - symbolsLineReader.read(line1); - symbolsLineReader.read(line2); - symbolsLineReader.read(line3); - - assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1;" + RANGE_LABEL_3 + ",2"); - assertThat(line2.getSymbols()).isEmpty(); - assertThat(line3.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1;" + RANGE_LABEL_4 + ",2"); - } - - @Test - public void symbol_declaration_should_be_sorted_by_offset() { - SymbolsLineReader symbolsLineReader = newReader( - newSymbol( - // This symbol begins after the second symbol, it should appear in second place - newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_2, OFFSET_3, RANGE_LABEL_1), - newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_2, OFFSET_3, RANGE_LABEL_1)), - newSymbol( - newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_0, OFFSET_1, RANGE_LABEL_2), - newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_0, OFFSET_1, RANGE_LABEL_2) - )); - - symbolsLineReader.read(line1); - symbolsLineReader.read(line2); - symbolsLineReader.read(line3); - - assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1;" + RANGE_LABEL_1 + ",2"); - assertThat(line2.getSymbols()).isEmpty(); - assertThat(line3.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1;" + RANGE_LABEL_1 + ",2"); - } - - @Test - public void symbol_declaration_should_be_sorted_by_line() { - SymbolsLineReader symbolsLineReader = newReader( - newSymbol( - newSingleLineTextRangeWithExpectedLabel(LINE_2, OFFSET_0, OFFSET_1, RANGE_LABEL_1), - newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_2, OFFSET_3, RANGE_LABEL_2)), - newSymbol( - newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_0, OFFSET_1, RANGE_LABEL_1), - newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_0, OFFSET_1, RANGE_LABEL_1) - )); - - symbolsLineReader.read(line1); - symbolsLineReader.read(line2); - symbolsLineReader.read(line3); - - assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1"); - assertThat(line2.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",2"); - assertThat(line3.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1;" + RANGE_LABEL_2 + ",2"); - } - - @Test - public void read_symbols_defined_on_many_lines() { - TextRange declaration = newTextRange(LINE_1, LINE_2, OFFSET_1, OFFSET_3); - when(rangeOffsetConverter.offsetToString(declaration, LINE_1, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_1); - when(rangeOffsetConverter.offsetToString(declaration, LINE_2, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_2); - - TextRange reference = newTextRange(LINE_3, LINE_4, OFFSET_1, OFFSET_3); - when(rangeOffsetConverter.offsetToString(reference, LINE_3, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_1); - when(rangeOffsetConverter.offsetToString(reference, LINE_4, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_2); - - SymbolsLineReader symbolsLineReader = newReader(newSymbol(declaration, reference)); - - symbolsLineReader.read(line1); - symbolsLineReader.read(line2); - symbolsLineReader.read(line3); - symbolsLineReader.read(line4); - - assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1"); - assertThat(line2.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1"); - assertThat(line3.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1"); - assertThat(line4.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1"); - } - - @Test - public void read_symbols_declared_on_a_whole_line() { - TextRange declaration = newTextRange(LINE_1, LINE_2, OFFSET_0, OFFSET_0); - when(rangeOffsetConverter.offsetToString(declaration, LINE_1, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_1); - when(rangeOffsetConverter.offsetToString(declaration, LINE_2, DEFAULT_LINE_LENGTH)).thenReturn(""); - TextRange reference = newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_1, OFFSET_3, RANGE_LABEL_2); - - SymbolsLineReader symbolsLineReader = newReader(newSymbol(declaration, reference)); - - symbolsLineReader.read(line1); - symbolsLineReader.read(line2); - symbolsLineReader.read(line3); - symbolsLineReader.read(line4); - - assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1"); - assertThat(line2.getSymbols()).isEmpty(); - assertThat(line3.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1"); - assertThat(line4.getSymbols()).isEmpty(); - } - - @Test - public void not_fail_and_stop_processing_when_range_offset_converter_throw_RangeOffsetConverterException() { - TextRange declaration = newTextRange(LINE_1, LINE_1, OFFSET_1, OFFSET_3); - doThrow(RangeOffsetConverter.RangeOffsetConverterException.class).when(rangeOffsetConverter).offsetToString(declaration, LINE_1, DEFAULT_LINE_LENGTH); - - TextRange reference = newSingleLineTextRangeWithExpectedLabel(LINE_2, OFFSET_1, OFFSET_3, RANGE_LABEL_2); - - SymbolsLineReader symbolsLineReader = newReader(newSymbol(declaration, reference)); - - symbolsLineReader.read(line1); - symbolsLineReader.read(line2); - - assertNoSymbol(); - assertThat(logTester.logs(WARN)).isNotEmpty(); - } - - @Test - public void keep_existing_processed_symbols_when_range_offset_converter_throw_RangeOffsetConverterException() { - TextRange declaration = newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_1, OFFSET_3, RANGE_LABEL_2); - - TextRange reference = newTextRange(LINE_2, LINE_2, OFFSET_1, OFFSET_3); - doThrow(RangeOffsetConverter.RangeOffsetConverterException.class).when(rangeOffsetConverter).offsetToString(reference, LINE_2, DEFAULT_LINE_LENGTH); - - SymbolsLineReader symbolsLineReader = newReader(newSymbol(declaration, reference)); - - symbolsLineReader.read(line1); - symbolsLineReader.read(line2); - - assertThat(line1.hasSymbols()).isTrue(); - assertThat(line2.hasSymbols()).isFalse(); - assertThat(logTester.logs(WARN)).isNotEmpty(); - } - - @Test - public void display_file_key_in_warning_when_range_offset_converter_throw_RangeOffsetConverterException() { - TextRange declaration = newTextRange(LINE_1, LINE_1, OFFSET_1, OFFSET_3); - doThrow(RangeOffsetConverter.RangeOffsetConverterException.class).when(rangeOffsetConverter).offsetToString(declaration, LINE_1, DEFAULT_LINE_LENGTH); - SymbolsLineReader symbolsLineReader = newReader(newSymbol(declaration, newSingleLineTextRangeWithExpectedLabel(LINE_2, OFFSET_1, OFFSET_3, RANGE_LABEL_2))); - - symbolsLineReader.read(line1); - - assertThat(logTester.logs(WARN)).containsOnly("Inconsistency detected in Symbols data. Symbols will be ignored for file 'FILE_KEY'"); - } - - private ScannerReport.Symbol newSymbol(TextRange declaration, TextRange... references) { - ScannerReport.Symbol.Builder builder = ScannerReport.Symbol.newBuilder() - .setDeclaration(declaration); - for (TextRange reference : references) { - builder.addReference(reference); - } - return builder.build(); - } - - private SymbolsLineReader newReader(ScannerReport.Symbol... symbols) { - return new SymbolsLineReader(FILE, Arrays.asList(symbols).iterator(), rangeOffsetConverter); - } - - private TextRange newSingleLineTextRangeWithExpectedLabel(int line, int startOffset, int endOffset, String rangeLabel) { - TextRange textRange = newTextRange(line, line, startOffset, endOffset); - when(rangeOffsetConverter.offsetToString(textRange, line, DEFAULT_LINE_LENGTH)).thenReturn(rangeLabel); - return textRange; - } - - private static TextRange newTextRange(int startLine, int endLine, int startOffset, int endOffset) { - return TextRange.newBuilder() - .setStartLine(startLine).setEndLine(endLine) - .setStartOffset(startOffset).setEndOffset(endOffset) - .build(); - } - - private void assertNoSymbol() { - assertThat(line1.hasSymbols()).isFalse(); - assertThat(line2.hasSymbols()).isFalse(); - assertThat(line3.hasSymbols()).isFalse(); - assertThat(line4.hasSymbols()).isFalse(); - } - -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/linereader/CoverageLineReaderTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/linereader/CoverageLineReaderTest.java new file mode 100644 index 00000000000..54b9f15a073 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/linereader/CoverageLineReaderTest.java @@ -0,0 +1,158 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program 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.task.projectanalysis.source.linereader; + +import java.util.Collections; +import org.junit.Test; +import org.sonar.db.protobuf.DbFileSources; +import org.sonar.scanner.protocol.output.ScannerReport; +import org.sonar.server.computation.task.projectanalysis.source.linereader.CoverageLineReader; + +import static com.google.common.collect.Lists.newArrayList; +import static org.assertj.core.api.Assertions.assertThat; + +public class CoverageLineReaderTest { + + @Test + public void set_coverage() { + CoverageLineReader computeCoverageLine = new CoverageLineReader(newArrayList(ScannerReport.LineCoverage.newBuilder() + .setLine(1) + .setConditions(10) + .setHits(true) + .setCoveredConditions(2) + .build()).iterator()); + + DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1); + computeCoverageLine.read(lineBuilder); + + assertThat(lineBuilder.getLineHits()).isEqualTo(1); + assertThat(lineBuilder.getConditions()).isEqualTo(10); + assertThat(lineBuilder.getCoveredConditions()).isEqualTo(2); + } + + // Some tools are only able to report condition coverage + @Test + public void set_coverage_only_conditions() { + CoverageLineReader computeCoverageLine = new CoverageLineReader(newArrayList(ScannerReport.LineCoverage.newBuilder() + .setLine(1) + .setConditions(10) + .setCoveredConditions(2) + .build()).iterator()); + + DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1); + computeCoverageLine.read(lineBuilder); + + assertThat(lineBuilder.hasLineHits()).isFalse(); + assertThat(lineBuilder.getConditions()).isEqualTo(10); + } + + @Test + public void set_coverage_on_uncovered_lines() { + CoverageLineReader computeCoverageLine = new CoverageLineReader(newArrayList(ScannerReport.LineCoverage.newBuilder() + .setLine(1) + .setHits(false) + .build()).iterator()); + + DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1); + computeCoverageLine.read(lineBuilder); + + assertThat(lineBuilder.hasLineHits()).isTrue(); + assertThat(lineBuilder.getLineHits()).isEqualTo(0); + } + + @Test + public void nothing_to_do_when_no_coverage_info() { + CoverageLineReader computeCoverageLine = new CoverageLineReader(Collections.emptyList().iterator()); + + DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1); + computeCoverageLine.read(lineBuilder); + + assertThat(lineBuilder.hasLineHits()).isFalse(); + assertThat(lineBuilder.hasConditions()).isFalse(); + assertThat(lineBuilder.hasCoveredConditions()).isFalse(); + } + + @Test + public void nothing_to_do_when_no_coverage_info_for_current_line() { + CoverageLineReader computeCoverageLine = new CoverageLineReader(newArrayList( + ScannerReport.LineCoverage.newBuilder() + .setLine(1) + .setConditions(10) + .setHits(true) + .setCoveredConditions(2) + .build() + // No coverage info on line 2 + ).iterator()); + + DbFileSources.Line.Builder line2Builder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(2); + computeCoverageLine.read(line2Builder); + + assertThat(line2Builder.hasLineHits()).isFalse(); + assertThat(line2Builder.hasConditions()).isFalse(); + assertThat(line2Builder.hasCoveredConditions()).isFalse(); + } + + @Test + public void nothing_to_do_when_no_coverage_info_for_next_line() { + CoverageLineReader computeCoverageLine = new CoverageLineReader(newArrayList( + ScannerReport.LineCoverage.newBuilder() + .setLine(1) + .setConditions(10) + .setHits(true) + .setCoveredConditions(2) + .build() + // No coverage info on line 2 + ).iterator()); + + DbFileSources.Data.Builder fileSourceBuilder = DbFileSources.Data.newBuilder(); + DbFileSources.Line.Builder line1Builder = fileSourceBuilder.addLinesBuilder().setLine(1); + DbFileSources.Line.Builder line2Builder = fileSourceBuilder.addLinesBuilder().setLine(2); + computeCoverageLine.read(line1Builder); + computeCoverageLine.read(line2Builder); + + assertThat(line2Builder.hasLineHits()).isFalse(); + assertThat(line2Builder.hasConditions()).isFalse(); + assertThat(line2Builder.hasCoveredConditions()).isFalse(); + } + + @Test + public void does_not_set_deprecated_coverage_fields() { + CoverageLineReader computeCoverageLine = new CoverageLineReader(newArrayList(ScannerReport.LineCoverage.newBuilder() + .setLine(1) + .setConditions(10) + .setHits(true) + .setCoveredConditions(2) + .build()).iterator()); + + DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1); + computeCoverageLine.read(lineBuilder); + + assertThat(lineBuilder.hasDeprecatedUtLineHits()).isFalse(); + assertThat(lineBuilder.hasDeprecatedUtConditions()).isFalse(); + assertThat(lineBuilder.hasDeprecatedUtCoveredConditions()).isFalse(); + assertThat(lineBuilder.hasDeprecatedOverallLineHits()).isFalse(); + assertThat(lineBuilder.hasDeprecatedOverallConditions()).isFalse(); + assertThat(lineBuilder.hasDeprecatedOverallCoveredConditions()).isFalse(); + assertThat(lineBuilder.hasDeprecatedItLineHits()).isFalse(); + assertThat(lineBuilder.hasDeprecatedItConditions()).isFalse(); + assertThat(lineBuilder.hasDeprecatedItCoveredConditions()).isFalse(); + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/linereader/DuplicationLineReaderTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/linereader/DuplicationLineReaderTest.java new file mode 100644 index 00000000000..8fb5c00537f --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/linereader/DuplicationLineReaderTest.java @@ -0,0 +1,190 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program 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.task.projectanalysis.source.linereader; + +import com.google.common.collect.ImmutableSet; +import java.util.Arrays; +import java.util.Collections; +import org.junit.Test; +import org.sonar.db.protobuf.DbFileSources; +import org.sonar.server.computation.task.projectanalysis.component.Component; +import org.sonar.server.computation.task.projectanalysis.component.ReportComponent; +import org.sonar.server.computation.task.projectanalysis.duplication.CrossProjectDuplicate; +import org.sonar.server.computation.task.projectanalysis.duplication.Duplicate; +import org.sonar.server.computation.task.projectanalysis.duplication.Duplication; +import org.sonar.server.computation.task.projectanalysis.duplication.InProjectDuplicate; +import org.sonar.server.computation.task.projectanalysis.duplication.InnerDuplicate; +import org.sonar.server.computation.task.projectanalysis.duplication.TextBlock; +import org.sonar.server.computation.task.projectanalysis.source.linereader.DuplicationLineReader; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DuplicationLineReaderTest { + + DbFileSources.Data.Builder sourceData = DbFileSources.Data.newBuilder(); + DbFileSources.Line.Builder line1 = sourceData.addLinesBuilder().setSource("line1").setLine(1); + DbFileSources.Line.Builder line2 = sourceData.addLinesBuilder().setSource("line2").setLine(2); + DbFileSources.Line.Builder line3 = sourceData.addLinesBuilder().setSource("line3").setLine(3); + DbFileSources.Line.Builder line4 = sourceData.addLinesBuilder().setSource("line4").setLine(4); + + @Test + public void read_nothing() { + DuplicationLineReader reader = new DuplicationLineReader(Collections.emptySet()); + + reader.read(line1); + + assertThat(line1.getDuplicationList()).isEmpty(); + } + + @Test + public void read_duplication_with_duplicates_on_same_file() { + DuplicationLineReader reader = duplicationLineReader(duplication(1, 2, innerDuplicate(3, 4))); + + reader.read(line1); + reader.read(line2); + reader.read(line3); + reader.read(line4); + + assertThat(line1.getDuplicationList()).containsExactly(1); + assertThat(line2.getDuplicationList()).containsExactly(1); + assertThat(line3.getDuplicationList()).containsExactly(2); + assertThat(line4.getDuplicationList()).containsExactly(2); + } + + @Test + public void read_duplication_with_duplicates_on_other_file() { + DuplicationLineReader reader = duplicationLineReader( + duplication( + 1, 2, + new InProjectDuplicate(fileComponent(1).build(), new TextBlock(3, 4)))); + + reader.read(line1); + reader.read(line2); + reader.read(line3); + reader.read(line4); + + assertThat(line1.getDuplicationList()).containsExactly(1); + assertThat(line2.getDuplicationList()).containsExactly(1); + assertThat(line3.getDuplicationList()).isEmpty(); + assertThat(line4.getDuplicationList()).isEmpty(); + } + + @Test + public void read_duplication_with_duplicates_on_other_file_from_other_project() { + DuplicationLineReader reader = duplicationLineReader( + duplication( + 1, 2, + new CrossProjectDuplicate("other-component-key-from-another-project", new TextBlock(3, 4)))); + + reader.read(line1); + reader.read(line2); + reader.read(line3); + reader.read(line4); + + assertThat(line1.getDuplicationList()).containsExactly(1); + assertThat(line2.getDuplicationList()).containsExactly(1); + assertThat(line3.getDuplicationList()).isEmpty(); + assertThat(line4.getDuplicationList()).isEmpty(); + } + + @Test + public void read_many_duplications() { + DuplicationLineReader reader = duplicationLineReader( + duplication( + 1, 1, + innerDuplicate(2, 2)), + duplication( + 1, 2, + innerDuplicate(3, 4)) + ); + + reader.read(line1); + reader.read(line2); + reader.read(line3); + reader.read(line4); + + assertThat(line1.getDuplicationList()).containsExactly(1, 2); + assertThat(line2.getDuplicationList()).containsExactly(2, 3); + assertThat(line3.getDuplicationList()).containsExactly(4); + assertThat(line4.getDuplicationList()).containsExactly(4); + } + + @Test + public void should_be_sorted_by_line_block() { + DuplicationLineReader reader = duplicationLineReader( + duplication( + 2, 2, + innerDuplicate(4, 4)), + duplication( + 1, 1, + innerDuplicate(3, 3)) + ); + + reader.read(line1); + reader.read(line2); + reader.read(line3); + reader.read(line4); + + assertThat(line1.getDuplicationList()).containsExactly(1); + assertThat(line2.getDuplicationList()).containsExactly(2); + assertThat(line3.getDuplicationList()).containsExactly(3); + assertThat(line4.getDuplicationList()).containsExactly(4); + } + + @Test + public void should_be_sorted_by_line_length() { + DuplicationLineReader reader = duplicationLineReader( + duplication( + 1, 2, + innerDuplicate(3, 4)), + duplication( + 1, 1, + innerDuplicate(4, 4) + ) + ); + + reader.read(line1); + reader.read(line2); + reader.read(line3); + reader.read(line4); + + assertThat(line1.getDuplicationList()).containsExactly(1, 2); + assertThat(line2.getDuplicationList()).containsExactly(2); + assertThat(line3.getDuplicationList()).containsExactly(3); + assertThat(line4.getDuplicationList()).containsExactly(3, 4); + } + + private static ReportComponent.Builder fileComponent(int ref) { + return ReportComponent.builder(Component.Type.FILE, ref); + } + + private static DuplicationLineReader duplicationLineReader(Duplication... duplications) { + return new DuplicationLineReader(ImmutableSet.copyOf(Arrays.asList(duplications))); + } + + private static Duplication duplication(int originalStart, int originalEnd, Duplicate... duplicates) { + return new Duplication(new TextBlock(originalStart, originalEnd), Arrays.asList(duplicates)); + } + + private static InnerDuplicate innerDuplicate(int start, int end) { + return new InnerDuplicate(new TextBlock(start, end)); + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/linereader/HighlightingLineReaderTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/linereader/HighlightingLineReaderTest.java new file mode 100644 index 00000000000..99f9778fc70 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/linereader/HighlightingLineReaderTest.java @@ -0,0 +1,326 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program 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.task.projectanalysis.source.linereader; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Random; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.utils.log.LogTester; +import org.sonar.db.protobuf.DbFileSources; +import org.sonar.scanner.protocol.output.ScannerReport; +import org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType; +import org.sonar.scanner.protocol.output.ScannerReport.TextRange; +import org.sonar.server.computation.task.projectanalysis.component.Component; +import org.sonar.server.computation.task.projectanalysis.source.linereader.HighlightingLineReader; +import org.sonar.server.computation.task.projectanalysis.source.linereader.RangeOffsetConverter; +import org.sonar.server.computation.task.projectanalysis.source.linereader.RangeOffsetConverter.RangeOffsetConverterException; + +import static com.google.common.collect.ImmutableMap.of; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.sonar.api.utils.log.LoggerLevel.WARN; +import static org.sonar.db.protobuf.DbFileSources.Data.newBuilder; +import static org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType.ANNOTATION; +import static org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType.COMMENT; +import static org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType.CONSTANT; +import static org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType.CPP_DOC; +import static org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType.HIGHLIGHTING_STRING; +import static org.sonar.scanner.protocol.output.ScannerReport.SyntaxHighlightingRule.HighlightingType.KEYWORD; +import static org.sonar.server.computation.task.projectanalysis.component.ReportComponent.builder; + +public class HighlightingLineReaderTest { + + @Rule + public LogTester logTester = new LogTester(); + + private static final Component FILE = builder(Component.Type.FILE, 1).setUuid("FILE_UUID").setKey("FILE_KEY").build(); + + private static final int DEFAULT_LINE_LENGTH = 5; + + private static final int LINE_1 = 1; + private static final int LINE_2 = 2; + private static final int LINE_3 = 3; + private static final int LINE_4 = 4; + + private static final String RANGE_LABEL_1 = "1,2"; + private static final String RANGE_LABEL_2 = "2,3"; + private static final String RANGE_LABEL_3 = "3,4"; + private static final String RANGE_LABEL_4 = "0,2"; + private static final String RANGE_LABEL_5 = "0,3"; + + private RangeOffsetConverter rangeOffsetConverter = mock(RangeOffsetConverter.class); + + private DbFileSources.Data.Builder sourceData = newBuilder(); + private DbFileSources.Line.Builder line1 = sourceData.addLinesBuilder().setSource("line1").setLine(1); + private DbFileSources.Line.Builder line2 = sourceData.addLinesBuilder().setSource("line2").setLine(2); + private DbFileSources.Line.Builder line3 = sourceData.addLinesBuilder().setSource("line3").setLine(3); + private DbFileSources.Line.Builder line4 = sourceData.addLinesBuilder().setSource("line4").setLine(4); + + @Test + public void nothing_to_read() { + HighlightingLineReader highlightingLineReader = newReader(Collections.emptyMap()); + + DbFileSources.Line.Builder lineBuilder = newBuilder().addLinesBuilder().setLine(1); + highlightingLineReader.read(lineBuilder); + + assertThat(lineBuilder.hasHighlighting()).isFalse(); + } + + @Test + public void read_one_line() { + HighlightingLineReader highlightingLineReader = newReader(of( + newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_1), ANNOTATION)); + + highlightingLineReader.read(line1); + + assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a"); + } + + @Test + public void read_many_lines() { + HighlightingLineReader highlightingLineReader = newReader(of( + newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_1), ANNOTATION, + newSingleLineTextRangeWithExpectingLabel(LINE_2, RANGE_LABEL_2), COMMENT, + newSingleLineTextRangeWithExpectingLabel(LINE_4, RANGE_LABEL_3), CONSTANT)); + + highlightingLineReader.read(line1); + highlightingLineReader.read(line2); + highlightingLineReader.read(line3); + highlightingLineReader.read(line4); + + assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a"); + assertThat(line2.getHighlighting()).isEqualTo(RANGE_LABEL_2 + ",cd"); + assertThat(line4.getHighlighting()).isEqualTo(RANGE_LABEL_3 + ",c"); + } + + @Test + public void supports_highlighting_over_multiple_lines_including_an_empty_one() { + List syntaxHighlightingList = new ArrayList<>(); + addHighlighting(syntaxHighlightingList, 1, 0, 1, 7, KEYWORD); // package + addHighlighting(syntaxHighlightingList, 2, 0, 4, 6, CPP_DOC); // comment over 3 lines + addHighlighting(syntaxHighlightingList, 5, 0, 5, 6, KEYWORD); // public + addHighlighting(syntaxHighlightingList, 5, 7, 5, 12, KEYWORD); // class + HighlightingLineReader highlightingLineReader = new HighlightingLineReader(FILE, syntaxHighlightingList.iterator(), new RangeOffsetConverter()); + + DbFileSources.Line.Builder[] builders = new DbFileSources.Line.Builder[] { + addSourceLine(highlightingLineReader, 1, "package example;"), + addSourceLine(highlightingLineReader, 2, "/*"), + addSourceLine(highlightingLineReader, 3, ""), + addSourceLine(highlightingLineReader, 4, " foo*/"), + addSourceLine(highlightingLineReader, 5, "public class One {"), + addSourceLine(highlightingLineReader, 6, "}") + }; + + assertThat(builders) + .extracting("highlighting") + .containsExactly( + "0,7,k", + "0,2,cppd", + "", + "0,6,cppd", + "0,6,k;7,12,k", + ""); + } + + private DbFileSources.Line.Builder addSourceLine(HighlightingLineReader highlightingLineReader, int line, String source) { + DbFileSources.Line.Builder lineBuilder = sourceData.addLinesBuilder().setSource(source).setLine(line); + highlightingLineReader.read(lineBuilder); + return lineBuilder; + } + + private void addHighlighting(List syntaxHighlightingList, + int startLine, int startOffset, + int endLine, int endOffset, + HighlightingType type) { + TextRange.Builder textRangeBuilder = TextRange.newBuilder(); + ScannerReport.SyntaxHighlightingRule.Builder ruleBuilder = ScannerReport.SyntaxHighlightingRule.newBuilder(); + syntaxHighlightingList.add(ruleBuilder + .setRange(textRangeBuilder + .setStartLine(startLine).setEndLine(endLine) + .setStartOffset(startOffset).setEndOffset(endOffset) + .build()) + .setType(type) + .build()); + } + + @Test + public void read_many_syntax_highlighting_on_same_line() { + HighlightingLineReader highlightingLineReader = newReader(of( + newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_1), ANNOTATION, + newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_2), COMMENT)); + + highlightingLineReader.read(line1); + + assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a;" + RANGE_LABEL_2 + ",cd"); + } + + @Test + public void read_one_syntax_highlighting_on_many_lines() { + // This highlighting begin on line 1 and finish on line 3 + TextRange textRange = newTextRange(LINE_1, LINE_3); + when(rangeOffsetConverter.offsetToString(textRange, LINE_1, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_1); + when(rangeOffsetConverter.offsetToString(textRange, LINE_2, 6)).thenReturn(RANGE_LABEL_2); + when(rangeOffsetConverter.offsetToString(textRange, LINE_3, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_3); + + HighlightingLineReader highlightingLineReader = newReader(of(textRange, ANNOTATION)); + + highlightingLineReader.read(line1); + DbFileSources.Line.Builder line2 = sourceData.addLinesBuilder().setSource("line 2").setLine(2); + highlightingLineReader.read(line2); + highlightingLineReader.read(line3); + + assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a"); + assertThat(line2.getHighlighting()).isEqualTo(RANGE_LABEL_2 + ",a"); + assertThat(line3.getHighlighting()).isEqualTo(RANGE_LABEL_3 + ",a"); + } + + @Test + public void read_many_syntax_highlighting_on_many_lines() { + TextRange textRange1 = newTextRange(LINE_1, LINE_3); + when(rangeOffsetConverter.offsetToString(textRange1, LINE_1, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_1); + when(rangeOffsetConverter.offsetToString(textRange1, LINE_2, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_2); + when(rangeOffsetConverter.offsetToString(textRange1, LINE_3, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_3); + + TextRange textRange2 = newTextRange(LINE_2, LINE_4); + when(rangeOffsetConverter.offsetToString(textRange2, LINE_2, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_2); + when(rangeOffsetConverter.offsetToString(textRange2, LINE_3, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_2); + when(rangeOffsetConverter.offsetToString(textRange2, LINE_4, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_4); + + TextRange textRange3 = newTextRange(LINE_2, LINE_2); + when(rangeOffsetConverter.offsetToString(textRange3, LINE_2, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_5); + + HighlightingLineReader highlightingLineReader = newReader(of( + textRange1, ANNOTATION, + textRange2, HIGHLIGHTING_STRING, + textRange3, COMMENT)); + + highlightingLineReader.read(line1); + highlightingLineReader.read(line2); + highlightingLineReader.read(line3); + highlightingLineReader.read(line4); + + assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a"); + assertThat(line2.getHighlighting()).isEqualTo(RANGE_LABEL_2 + ",a;" + RANGE_LABEL_2 + ",s;" + RANGE_LABEL_5 + ",cd"); + assertThat(line3.getHighlighting()).isEqualTo(RANGE_LABEL_3 + ",a;" + RANGE_LABEL_2 + ",s"); + assertThat(line4.getHighlighting()).isEqualTo(RANGE_LABEL_4 + ",s"); + } + + @Test + public void read_highlighting_declared_on_a_whole_line() { + TextRange textRange = newTextRange(LINE_1, LINE_2); + when(rangeOffsetConverter.offsetToString(textRange, LINE_1, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_1); + when(rangeOffsetConverter.offsetToString(textRange, LINE_2, DEFAULT_LINE_LENGTH)).thenReturn(""); + + HighlightingLineReader highlightingLineReader = newReader(of(textRange, ANNOTATION)); + + highlightingLineReader.read(line1); + highlightingLineReader.read(line2); + highlightingLineReader.read(line3); + + assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a"); + // Nothing should be set on line 2 + assertThat(line2.getHighlighting()).isEmpty(); + assertThat(line3.getHighlighting()).isEmpty(); + } + + @Test + public void not_fail_and_stop_processing_when_range_offset_converter_throw_RangeOffsetConverterException() { + TextRange textRange1 = newTextRange(LINE_1, LINE_1); + doThrow(RangeOffsetConverterException.class).when(rangeOffsetConverter).offsetToString(textRange1, LINE_1, DEFAULT_LINE_LENGTH); + + HighlightingLineReader highlightingLineReader = newReader(of( + textRange1, HighlightingType.ANNOTATION, + newSingleLineTextRangeWithExpectingLabel(LINE_2, RANGE_LABEL_1), HIGHLIGHTING_STRING)); + + highlightingLineReader.read(line1); + highlightingLineReader.read(line2); + + assertNoHighlighting(); + assertThat(logTester.logs(WARN)).isNotEmpty(); + } + + @Test + public void keep_existing_processed_highlighting_when_range_offset_converter_throw_RangeOffsetConverterException() { + TextRange textRange2 = newTextRange(LINE_2, LINE_2); + doThrow(RangeOffsetConverterException.class).when(rangeOffsetConverter).offsetToString(textRange2, LINE_2, DEFAULT_LINE_LENGTH); + + HighlightingLineReader highlightingLineReader = newReader(of( + newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_1), ANNOTATION, + textRange2, HIGHLIGHTING_STRING)); + + highlightingLineReader.read(line1); + highlightingLineReader.read(line2); + + assertThat(line1.hasHighlighting()).isTrue(); + assertThat(line2.hasHighlighting()).isFalse(); + assertThat(logTester.logs(WARN)).isNotEmpty(); + } + + @Test + public void display_file_key_in_warning_when_range_offset_converter_throw_RangeOffsetConverterException() { + TextRange textRange1 = newTextRange(LINE_1, LINE_1); + doThrow(RangeOffsetConverterException.class).when(rangeOffsetConverter).offsetToString(textRange1, LINE_1, DEFAULT_LINE_LENGTH); + HighlightingLineReader highlightingLineReader = newReader(of(textRange1, ANNOTATION)); + + highlightingLineReader.read(line1); + + assertThat(logTester.logs(WARN)).containsOnly("Inconsistency detected in Highlighting data. Highlighting will be ignored for file 'FILE_KEY'"); + } + + private HighlightingLineReader newReader(Map textRangeByType) { + List syntaxHighlightingList = new ArrayList<>(); + for (Map.Entry entry : textRangeByType.entrySet()) { + syntaxHighlightingList.add(ScannerReport.SyntaxHighlightingRule.newBuilder() + .setRange(entry.getKey()) + .setType(entry.getValue()) + .build()); + } + return new HighlightingLineReader(FILE, syntaxHighlightingList.iterator(), rangeOffsetConverter); + } + + private static TextRange newTextRange(int startLine, int enLine) { + Random random = new Random(); + return TextRange.newBuilder() + .setStartLine(startLine).setEndLine(enLine) + // Offsets are not used by the reader + .setStartOffset(random.nextInt()).setEndOffset(random.nextInt()) + .build(); + } + + private TextRange newSingleLineTextRangeWithExpectingLabel(int line, String rangeLabel) { + TextRange textRange = newTextRange(line, line); + when(rangeOffsetConverter.offsetToString(textRange, line, DEFAULT_LINE_LENGTH)).thenReturn(rangeLabel); + return textRange; + } + + private void assertNoHighlighting() { + assertThat(line1.hasHighlighting()).isFalse(); + assertThat(line2.hasHighlighting()).isFalse(); + assertThat(line3.hasHighlighting()).isFalse(); + assertThat(line4.hasHighlighting()).isFalse(); + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/linereader/ScmLineReaderTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/linereader/ScmLineReaderTest.java new file mode 100644 index 00000000000..ce3eea15611 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/linereader/ScmLineReaderTest.java @@ -0,0 +1,126 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program 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.task.projectanalysis.source.linereader; + +import com.google.common.collect.ImmutableMap; +import java.util.Collections; +import java.util.Map; +import org.junit.Test; +import org.sonar.db.protobuf.DbFileSources; +import org.sonar.server.computation.task.projectanalysis.scm.Changeset; +import org.sonar.server.computation.task.projectanalysis.scm.ScmInfo; +import org.sonar.server.computation.task.projectanalysis.scm.ScmInfoImpl; +import org.sonar.server.computation.task.projectanalysis.source.linereader.ScmLineReader; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ScmLineReaderTest { + + @Test + public void set_scm() { + ScmInfo scmInfo = new ScmInfoImpl(Collections.singletonMap(1, + Changeset.newChangesetBuilder() + .setAuthor("john") + .setDate(123_456_789L) + .setRevision("rev-1") + .build())); + + ScmLineReader lineScm = new ScmLineReader(scmInfo); + + DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1); + lineScm.read(lineBuilder); + + assertThat(lineBuilder.getScmAuthor()).isEqualTo("john"); + assertThat(lineBuilder.getScmDate()).isEqualTo(123_456_789L); + assertThat(lineBuilder.getScmRevision()).isEqualTo("rev-1"); + } + + @Test + public void set_scm_with_minim_fields() { + ScmInfo scmInfo = new ScmInfoImpl(Collections.singletonMap(1, + Changeset.newChangesetBuilder() + .setDate(123456789L) + .build())); + + ScmLineReader lineScm = new ScmLineReader(scmInfo); + + DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1); + lineScm.read(lineBuilder); + + assertThat(lineBuilder.hasScmAuthor()).isFalse(); + assertThat(lineBuilder.getScmDate()).isEqualTo(123456789L); + assertThat(lineBuilder.hasScmRevision()).isFalse(); + + } + + @Test + public void getLatestChange_returns_changeset_with_highest_date_of_read_lines() { + long refDate = 123_456_789L; + Changeset changeset0 = Changeset.newChangesetBuilder().setDate(refDate - 636).setRevision("rev-1").build(); + Changeset changeset1 = Changeset.newChangesetBuilder().setDate(refDate + 1).setRevision("rev-2").build(); + Changeset changeset2 = Changeset.newChangesetBuilder().setDate(refDate + 2).build(); + ScmInfo scmInfo = new ScmInfoImpl(setup8LinesChangeset(changeset0, changeset1, changeset2)); + + ScmLineReader lineScm = new ScmLineReader(scmInfo); + + // before any line is read, the latest changes are null + assertThat(lineScm.getLatestChange()).isNull(); + assertThat(lineScm.getLatestChangeWithRevision()).isNull(); + + // read line 1, only one changeset => 0 + readLineAndAssertLatestChanges(lineScm, 1, changeset0, changeset0); + + // read line 2, latest changeset is 1 + readLineAndAssertLatestChanges(lineScm, 2, changeset1, changeset1); + + // read line 3, latest changeset is still 1 + readLineAndAssertLatestChanges(lineScm, 3, changeset1, changeset1); + + // read line 4, latest changeset is now 2 + readLineAndAssertLatestChanges(lineScm, 4, changeset2, changeset1); + + // read line 5 to 8, there will never be any changeset more recent than 2 + readLineAndAssertLatestChanges(lineScm, 5, changeset2, changeset1); + readLineAndAssertLatestChanges(lineScm, 6, changeset2, changeset1); + readLineAndAssertLatestChanges(lineScm, 7, changeset2, changeset1); + readLineAndAssertLatestChanges(lineScm, 8, changeset2, changeset1); + } + + private static Map setup8LinesChangeset(Changeset changeset0, Changeset changeset1, Changeset changeset2) { + return ImmutableMap.builder() + .put(1, changeset0) + .put(2, changeset1) + .put(3, changeset1) + .put(4, changeset2) + .put(5, changeset0) + .put(6, changeset1) + .put(7, changeset0) + .put(8, changeset0).build(); + } + + private void readLineAndAssertLatestChanges(ScmLineReader lineScm, int line, Changeset expectedChangeset, Changeset expectedChangesetWithRevision) { + DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(line); + lineScm.read(lineBuilder); + assertThat(lineScm.getLatestChange()).isSameAs(expectedChangeset); + assertThat(lineScm.getLatestChangeWithRevision()).isSameAs(expectedChangesetWithRevision); + + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/linereader/SymbolsLineReaderTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/linereader/SymbolsLineReaderTest.java new file mode 100644 index 00000000000..ade3730e62e --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/source/linereader/SymbolsLineReaderTest.java @@ -0,0 +1,337 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program 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.task.projectanalysis.source.linereader; + +import java.util.Arrays; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.utils.log.LogTester; +import org.sonar.db.protobuf.DbFileSources; +import org.sonar.scanner.protocol.output.ScannerReport; +import org.sonar.scanner.protocol.output.ScannerReport.TextRange; +import org.sonar.server.computation.task.projectanalysis.component.Component; +import org.sonar.server.computation.task.projectanalysis.source.linereader.RangeOffsetConverter; +import org.sonar.server.computation.task.projectanalysis.source.linereader.SymbolsLineReader; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.sonar.api.utils.log.LoggerLevel.WARN; +import static org.sonar.server.computation.task.projectanalysis.component.ReportComponent.builder; + +public class SymbolsLineReaderTest { + + @Rule + public LogTester logTester = new LogTester(); + + private static final Component FILE = builder(Component.Type.FILE, 1).setUuid("FILE_UUID").setKey("FILE_KEY").build(); + + private static final int DEFAULT_LINE_LENGTH = 5; + + private static final int LINE_1 = 1; + private static final int LINE_2 = 2; + private static final int LINE_3 = 3; + private static final int LINE_4 = 4; + + private static final int OFFSET_0 = 0; + private static final int OFFSET_1 = 1; + private static final int OFFSET_2 = 2; + private static final int OFFSET_3 = 3; + private static final int OFFSET_4 = 4; + + private static final String RANGE_LABEL_1 = "1,2"; + private static final String RANGE_LABEL_2 = "2,3"; + private static final String RANGE_LABEL_3 = "3,4"; + private static final String RANGE_LABEL_4 = "0,2"; + + private RangeOffsetConverter rangeOffsetConverter = mock(RangeOffsetConverter.class); + + private DbFileSources.Data.Builder sourceData = DbFileSources.Data.newBuilder(); + private DbFileSources.Line.Builder line1 = sourceData.addLinesBuilder().setSource("line1").setLine(1); + private DbFileSources.Line.Builder line2 = sourceData.addLinesBuilder().setSource("line2").setLine(2); + private DbFileSources.Line.Builder line3 = sourceData.addLinesBuilder().setSource("line3").setLine(3); + private DbFileSources.Line.Builder line4 = sourceData.addLinesBuilder().setSource("line4").setLine(4); + + @Test + public void read_nothing() { + SymbolsLineReader symbolsLineReader = newReader(); + + symbolsLineReader.read(line1); + + assertThat(line1.getSymbols()).isEmpty(); + } + + @Test + public void read_symbols() { + SymbolsLineReader symbolsLineReader = newReader(newSymbol( + newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_2, OFFSET_4, RANGE_LABEL_1), + newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_1, OFFSET_3, RANGE_LABEL_2) + )); + + symbolsLineReader.read(line1); + symbolsLineReader.read(line2); + symbolsLineReader.read(line3); + + assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1"); + assertThat(line2.getSymbols()).isEmpty(); + assertThat(line3.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1"); + } + + @Test + public void read_symbols_with_reference_on_same_line() { + SymbolsLineReader symbolsLineReader = newReader(newSymbol( + newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_0, OFFSET_1, RANGE_LABEL_1), + newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_2, OFFSET_3, RANGE_LABEL_2) + )); + + symbolsLineReader.read(line1); + + assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1;" + RANGE_LABEL_2 + ",1"); + } + + @Test + public void read_symbols_with_two_references() { + SymbolsLineReader symbolsLineReader = newReader(newSymbol( + newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_2, OFFSET_4, RANGE_LABEL_1), + newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_1, OFFSET_3, RANGE_LABEL_2), + newSingleLineTextRangeWithExpectedLabel(LINE_2, OFFSET_0, OFFSET_2, RANGE_LABEL_3) + )); + + symbolsLineReader.read(line1); + symbolsLineReader.read(line2); + symbolsLineReader.read(line3); + + assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1"); + assertThat(line2.getSymbols()).isEqualTo(RANGE_LABEL_3 + ",1"); + assertThat(line3.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1"); + } + + @Test + public void read_symbols_with_two_references_on_the_same_line() { + SymbolsLineReader symbolsLineReader = newReader(newSymbol( + newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_2, OFFSET_3, RANGE_LABEL_1), + newSingleLineTextRangeWithExpectedLabel(LINE_2, OFFSET_0, OFFSET_1, RANGE_LABEL_2), + newSingleLineTextRangeWithExpectedLabel(LINE_2, OFFSET_2, OFFSET_3, RANGE_LABEL_3) + )); + + symbolsLineReader.read(line1); + symbolsLineReader.read(line2); + + assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1"); + assertThat(line2.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1;" + RANGE_LABEL_3 + ",1"); + } + + @Test + public void read_symbols_when_reference_line_is_before_declaration_line() { + SymbolsLineReader symbolsLineReader = newReader(newSymbol( + newSingleLineTextRangeWithExpectedLabel(LINE_2, OFFSET_3, OFFSET_4, RANGE_LABEL_1), + newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_1, OFFSET_2, RANGE_LABEL_2) + )); + + symbolsLineReader.read(line1); + symbolsLineReader.read(line2); + + assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1"); + assertThat(line2.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1"); + } + + @Test + public void read_many_symbols_on_lines() { + SymbolsLineReader symbolsLineReader = newReader( + newSymbol( + newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_1, OFFSET_2, RANGE_LABEL_1), + newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_2, OFFSET_3, RANGE_LABEL_2)), + newSymbol( + newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_3, OFFSET_4, RANGE_LABEL_3), + newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_0, OFFSET_1, RANGE_LABEL_4) + )); + + symbolsLineReader.read(line1); + symbolsLineReader.read(line2); + symbolsLineReader.read(line3); + + assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1;" + RANGE_LABEL_3 + ",2"); + assertThat(line2.getSymbols()).isEmpty(); + assertThat(line3.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1;" + RANGE_LABEL_4 + ",2"); + } + + @Test + public void symbol_declaration_should_be_sorted_by_offset() { + SymbolsLineReader symbolsLineReader = newReader( + newSymbol( + // This symbol begins after the second symbol, it should appear in second place + newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_2, OFFSET_3, RANGE_LABEL_1), + newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_2, OFFSET_3, RANGE_LABEL_1)), + newSymbol( + newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_0, OFFSET_1, RANGE_LABEL_2), + newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_0, OFFSET_1, RANGE_LABEL_2) + )); + + symbolsLineReader.read(line1); + symbolsLineReader.read(line2); + symbolsLineReader.read(line3); + + assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1;" + RANGE_LABEL_1 + ",2"); + assertThat(line2.getSymbols()).isEmpty(); + assertThat(line3.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1;" + RANGE_LABEL_1 + ",2"); + } + + @Test + public void symbol_declaration_should_be_sorted_by_line() { + SymbolsLineReader symbolsLineReader = newReader( + newSymbol( + newSingleLineTextRangeWithExpectedLabel(LINE_2, OFFSET_0, OFFSET_1, RANGE_LABEL_1), + newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_2, OFFSET_3, RANGE_LABEL_2)), + newSymbol( + newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_0, OFFSET_1, RANGE_LABEL_1), + newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_0, OFFSET_1, RANGE_LABEL_1) + )); + + symbolsLineReader.read(line1); + symbolsLineReader.read(line2); + symbolsLineReader.read(line3); + + assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1"); + assertThat(line2.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",2"); + assertThat(line3.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1;" + RANGE_LABEL_2 + ",2"); + } + + @Test + public void read_symbols_defined_on_many_lines() { + TextRange declaration = newTextRange(LINE_1, LINE_2, OFFSET_1, OFFSET_3); + when(rangeOffsetConverter.offsetToString(declaration, LINE_1, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_1); + when(rangeOffsetConverter.offsetToString(declaration, LINE_2, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_2); + + TextRange reference = newTextRange(LINE_3, LINE_4, OFFSET_1, OFFSET_3); + when(rangeOffsetConverter.offsetToString(reference, LINE_3, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_1); + when(rangeOffsetConverter.offsetToString(reference, LINE_4, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_2); + + SymbolsLineReader symbolsLineReader = newReader(newSymbol(declaration, reference)); + + symbolsLineReader.read(line1); + symbolsLineReader.read(line2); + symbolsLineReader.read(line3); + symbolsLineReader.read(line4); + + assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1"); + assertThat(line2.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1"); + assertThat(line3.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1"); + assertThat(line4.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1"); + } + + @Test + public void read_symbols_declared_on_a_whole_line() { + TextRange declaration = newTextRange(LINE_1, LINE_2, OFFSET_0, OFFSET_0); + when(rangeOffsetConverter.offsetToString(declaration, LINE_1, DEFAULT_LINE_LENGTH)).thenReturn(RANGE_LABEL_1); + when(rangeOffsetConverter.offsetToString(declaration, LINE_2, DEFAULT_LINE_LENGTH)).thenReturn(""); + TextRange reference = newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_1, OFFSET_3, RANGE_LABEL_2); + + SymbolsLineReader symbolsLineReader = newReader(newSymbol(declaration, reference)); + + symbolsLineReader.read(line1); + symbolsLineReader.read(line2); + symbolsLineReader.read(line3); + symbolsLineReader.read(line4); + + assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1"); + assertThat(line2.getSymbols()).isEmpty(); + assertThat(line3.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1"); + assertThat(line4.getSymbols()).isEmpty(); + } + + @Test + public void not_fail_and_stop_processing_when_range_offset_converter_throw_RangeOffsetConverterException() { + TextRange declaration = newTextRange(LINE_1, LINE_1, OFFSET_1, OFFSET_3); + doThrow(RangeOffsetConverter.RangeOffsetConverterException.class).when(rangeOffsetConverter).offsetToString(declaration, LINE_1, DEFAULT_LINE_LENGTH); + + TextRange reference = newSingleLineTextRangeWithExpectedLabel(LINE_2, OFFSET_1, OFFSET_3, RANGE_LABEL_2); + + SymbolsLineReader symbolsLineReader = newReader(newSymbol(declaration, reference)); + + symbolsLineReader.read(line1); + symbolsLineReader.read(line2); + + assertNoSymbol(); + assertThat(logTester.logs(WARN)).isNotEmpty(); + } + + @Test + public void keep_existing_processed_symbols_when_range_offset_converter_throw_RangeOffsetConverterException() { + TextRange declaration = newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_1, OFFSET_3, RANGE_LABEL_2); + + TextRange reference = newTextRange(LINE_2, LINE_2, OFFSET_1, OFFSET_3); + doThrow(RangeOffsetConverter.RangeOffsetConverterException.class).when(rangeOffsetConverter).offsetToString(reference, LINE_2, DEFAULT_LINE_LENGTH); + + SymbolsLineReader symbolsLineReader = newReader(newSymbol(declaration, reference)); + + symbolsLineReader.read(line1); + symbolsLineReader.read(line2); + + assertThat(line1.hasSymbols()).isTrue(); + assertThat(line2.hasSymbols()).isFalse(); + assertThat(logTester.logs(WARN)).isNotEmpty(); + } + + @Test + public void display_file_key_in_warning_when_range_offset_converter_throw_RangeOffsetConverterException() { + TextRange declaration = newTextRange(LINE_1, LINE_1, OFFSET_1, OFFSET_3); + doThrow(RangeOffsetConverter.RangeOffsetConverterException.class).when(rangeOffsetConverter).offsetToString(declaration, LINE_1, DEFAULT_LINE_LENGTH); + SymbolsLineReader symbolsLineReader = newReader(newSymbol(declaration, newSingleLineTextRangeWithExpectedLabel(LINE_2, OFFSET_1, OFFSET_3, RANGE_LABEL_2))); + + symbolsLineReader.read(line1); + + assertThat(logTester.logs(WARN)).containsOnly("Inconsistency detected in Symbols data. Symbols will be ignored for file 'FILE_KEY'"); + } + + private ScannerReport.Symbol newSymbol(TextRange declaration, TextRange... references) { + ScannerReport.Symbol.Builder builder = ScannerReport.Symbol.newBuilder() + .setDeclaration(declaration); + for (TextRange reference : references) { + builder.addReference(reference); + } + return builder.build(); + } + + private SymbolsLineReader newReader(ScannerReport.Symbol... symbols) { + return new SymbolsLineReader(FILE, Arrays.asList(symbols).iterator(), rangeOffsetConverter); + } + + private TextRange newSingleLineTextRangeWithExpectedLabel(int line, int startOffset, int endOffset, String rangeLabel) { + TextRange textRange = newTextRange(line, line, startOffset, endOffset); + when(rangeOffsetConverter.offsetToString(textRange, line, DEFAULT_LINE_LENGTH)).thenReturn(rangeLabel); + return textRange; + } + + private static TextRange newTextRange(int startLine, int endLine, int startOffset, int endOffset) { + return TextRange.newBuilder() + .setStartLine(startLine).setEndLine(endLine) + .setStartOffset(startOffset).setEndOffset(endOffset) + .build(); + } + + private void assertNoSymbol() { + assertThat(line1.hasSymbols()).isFalse(); + assertThat(line2.hasSymbols()).isFalse(); + assertThat(line3.hasSymbols()).isFalse(); + assertThat(line4.hasSymbols()).isFalse(); + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistTestsStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistTestsStepTest.java index 523ea9c0346..921552352bb 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistTestsStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistTestsStepTest.java @@ -104,7 +104,7 @@ public class PersistTestsStepTest extends BaseStepTest { public void no_test_in_database_and_batch_report() { underTest.execute(); - assertThat(dbClient.fileSourceDao().selectTest(db.getSession(), TEST_FILE_UUID_1)).isNull(); + assertThat(dbClient.fileSourceDao().selectTestByFileUuid(db.getSession(), TEST_FILE_UUID_1)).isNull(); assertThat(log.logs()).isEmpty(); } @@ -121,7 +121,7 @@ public class PersistTestsStepTest extends BaseStepTest { assertThat(db.countRowsOfTable("file_sources")).isEqualTo(1); - FileSourceDto dto = dbClient.fileSourceDao().selectTest(db.getSession(), TEST_FILE_UUID_1); + FileSourceDto dto = dbClient.fileSourceDao().selectTestByFileUuid(db.getSession(), TEST_FILE_UUID_1); assertThat(dto.getCreatedAt()).isEqualTo(now); assertThat(dto.getUpdatedAt()).isEqualTo(now); assertThat(dto.getProjectUuid()).isEqualTo(PROJECT_UUID); @@ -142,7 +142,7 @@ public class PersistTestsStepTest extends BaseStepTest { underTest.execute(); - FileSourceDto dto = dbClient.fileSourceDao().selectTest(db.getSession(), TEST_FILE_UUID_1); + FileSourceDto dto = dbClient.fileSourceDao().selectTestByFileUuid(db.getSession(), TEST_FILE_UUID_1); assertThat(dto.getCreatedAt()).isEqualTo(now); assertThat(dto.getUpdatedAt()).isEqualTo(now); assertThat(dto.getProjectUuid()).isEqualTo(PROJECT_UUID); @@ -168,7 +168,7 @@ public class PersistTestsStepTest extends BaseStepTest { underTest.execute(); - FileSourceDto dto = dbClient.fileSourceDao().selectTest(db.getSession(), TEST_FILE_UUID_1); + FileSourceDto dto = dbClient.fileSourceDao().selectTestByFileUuid(db.getSession(), TEST_FILE_UUID_1); assertThat(dto.getFileUuid()).isEqualTo(TEST_FILE_UUID_1); List tests = dto.getTestData(); assertThat(tests).hasSize(1); @@ -203,7 +203,7 @@ public class PersistTestsStepTest extends BaseStepTest { underTest.execute(); - FileSourceDto dto = dbClient.fileSourceDao().selectTest(db.getSession(), TEST_FILE_UUID_1); + FileSourceDto dto = dbClient.fileSourceDao().selectTestByFileUuid(db.getSession(), TEST_FILE_UUID_1); List coveredLines = dto.getTestData().get(0).getCoveredFile(0).getCoveredLineList(); assertThat(coveredLines).containsOnly(1, 2, 3, 4); } @@ -225,7 +225,7 @@ public class PersistTestsStepTest extends BaseStepTest { .setCreatedAt(100_000) .setUpdatedAt(100_000)); db.getSession().commit(); - assertThat(dbClient.fileSourceDao().selectTest(db.getSession(), TEST_FILE_UUID_1)).isNotNull(); + assertThat(dbClient.fileSourceDao().selectTestByFileUuid(db.getSession(), TEST_FILE_UUID_1)).isNotNull(); ScannerReport.Test newBatchTest = newTest(1); reportReader.putTests(TEST_FILE_REF_1, Arrays.asList(newBatchTest)); @@ -237,7 +237,7 @@ public class PersistTestsStepTest extends BaseStepTest { underTest.execute(); // ASSERT - FileSourceDto dto = dbClient.fileSourceDao().selectTest(db.getSession(), TEST_FILE_UUID_1); + FileSourceDto dto = dbClient.fileSourceDao().selectTestByFileUuid(db.getSession(), TEST_FILE_UUID_1); assertThat(dto.getCreatedAt()).isEqualTo(100_000); assertThat(dto.getUpdatedAt()).isEqualTo(now); assertThat(dto.getTestData()).hasSize(1); diff --git a/sonar-core/src/main/java/org/sonar/core/hash/SourceLinesHashesComputer.java b/sonar-core/src/main/java/org/sonar/core/hash/SourceLinesHashesComputer.java index b85906bd733..b64413f0687 100644 --- a/sonar-core/src/main/java/org/sonar/core/hash/SourceLinesHashesComputer.java +++ b/sonar-core/src/main/java/org/sonar/core/hash/SourceLinesHashesComputer.java @@ -19,9 +19,9 @@ */ package org.sonar.core.hash; -import com.google.common.collect.ImmutableList; import java.security.MessageDigest; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.apache.commons.codec.binary.Hex; import org.apache.commons.codec.digest.DigestUtils; @@ -52,7 +52,7 @@ public class SourceLinesHashesComputer { } public List getLineHashes() { - return ImmutableList.copyOf(lineHashes); + return Collections.unmodifiableList(lineHashes); } private String computeHash(String line) { diff --git a/sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/output/ScannerReportReader.java b/sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/output/ScannerReportReader.java index 2fb837b2558..d5e66955ab3 100644 --- a/sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/output/ScannerReportReader.java +++ b/sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/output/ScannerReportReader.java @@ -120,6 +120,19 @@ public class ScannerReportReader { return file.exists(); } + public CloseableIterator readComponentSignificantCode(int fileRef) { + File file = fileStructure.fileFor(FileStructure.Domain.SGNIFICANT_CODE, fileRef); + if (fileExists(file)) { + return Protobuf.readStream(file, ScannerReport.LineSgnificantCode.parser()); + } + return emptyCloseableIterator(); + } + + public boolean hasSignificantCode(int fileRef) { + File file = fileStructure.fileFor(FileStructure.Domain.SGNIFICANT_CODE, fileRef); + return fileExists(file); + } + public CloseableIterator readComponentSyntaxHighlighting(int fileRef) { File file = fileStructure.fileFor(FileStructure.Domain.SYNTAX_HIGHLIGHTINGS, fileRef); if (fileExists(file)) {