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;
}
@CheckForNull
- public FileSourceDto selectTest(DbSession dbSession, String fileUuid) {
+ public FileSourceDto selectTestByFileUuid(DbSession dbSession, String fileUuid) {
return mapper(dbSession).select(fileUuid, Type.TEST);
}
}
}
- public <T> void readLineHashesStream(DbSession dbSession, String fileUuid, Function<Reader, T> function) {
+ public void readLineHashesStream(DbSession dbSession, String fileUuid, Consumer<Reader> consumer) {
Connection connection = dbSession.getConnection();
PreparedStatement pstmt = null;
ResultSet rs = null;
if (rs.next()) {
reader = rs.getCharacterStream(1);
if (reader != null) {
- function.apply(reader);
+ consumer.accept(reader);
}
}
} catch (SQLException e) {
}
/**
- * 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);
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;
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");
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();
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();
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();
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<Reader, Void>() {
- @Nullable
+ underTest.readLineHashesStream(dbTester.getSession(), "FILE2_UUID", new Consumer<Reader>() {
@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();
"project_uuid", "file_uuid", "data_hash", "line_hashes", "src_hash", "created_at", "updated_at", "data_type", "revision");
}
- private static class ReaderToStringFunction implements Function<Reader, String> {
+ private static class ReaderToStringConsumer implements Consumer<Reader> {
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);
}
CloseableIterator<ScannerReport.LineCoverage> 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<CloseableIterator<String>> readFileSource(int fileRef);
CloseableIterator<ScannerReport.CoverageDetail> readCoverageDetails(int testFileRef);
CloseableIterator<ScannerReport.ContextProperty> readContextProperties();
+
+ CloseableIterator<ScannerReport.LineSgnificantCode> readComponentSignificantCode(int fileRef);
}
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 {
fileInputStream.close();
}
}
+
+ @Override
+ public CloseableIterator<LineSgnificantCode> readComponentSignificantCode(int fileRef) {
+ ensureInitialized();
+ return delegate.readComponentSignificantCode(fileRef);
+ }
}
*/
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;
}
private Optional<ScmInfo> getScmInfo(Component component) {
- return toJavaUtilOptional(scmInfoRepository.getScmInfo(component));
+ return scmInfoRepository.getScmInfo(component);
}
private static Optional<Changeset> getChangeset(Component component, ScmInfo scmInfo, DefaultIssue issue) {
return;
}
- Optional<ScmInfo> scmInfoOptional = scmInfoRepository.getScmInfo(file);
+ java.util.Optional<ScmInfo> scmInfoOptional = scmInfoRepository.getScmInfo(file);
if (!scmInfoOptional.isPresent()) {
LOG.trace(String.format("No changeset for file %s. Dev cost will be zero.", file.getKey()));
return;
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) {
*/
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;
/**
}
@Override
- public com.google.common.base.Optional<ScmInfo> getScmInfo(Component component) {
+ public Optional<ScmInfo> 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<ScmInfo> toGuavaOptional(Optional<ScmInfo> scmInfo) {
- return com.google.common.base.Optional.fromNullable(scmInfo.orElse(null));
+ return scmInfoCache.computeIfAbsent(component, this::getScmInfoForComponent);
}
private Optional<ScmInfo> getScmInfoForComponent(Component component) {
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);
}
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<ScmInfo> generateAndMergeDb(Component file, boolean keepAuthorAndRevision) {
Optional<DbScmInfo> dbInfoOpt = scmInfoDbLoader.getScmInfo(file);
if (!dbInfoOpt.isPresent()) {
}
// 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));
}
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 {
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);
+++ /dev/null
-/*
- * 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<ScannerReport.LineCoverage> coverageIterator;
- private ScannerReport.LineCoverage coverage;
-
- public CoverageLineReader(Iterator<ScannerReport.LineCoverage> 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;
- }
-
-}
+++ /dev/null
-/*
- * 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<TextBlock, Integer> duplicatedTextBlockIndexByTextBlock;
-
- public DuplicationLineReader(Iterable<Duplication> duplications) {
- this.duplicatedTextBlockIndexByTextBlock = createIndexOfDuplicatedTextBlocks(duplications);
- }
-
- @Override
- public void read(DbFileSources.Line.Builder lineBuilder) {
- Predicate<Map.Entry<TextBlock, Integer>> 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);
- }
- }
-
- /**
- *
- * <p>
- * 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()}.
- * </p>
- */
- private static Map<TextBlock, Integer> createIndexOfDuplicatedTextBlocks(Iterable<Duplication> duplications) {
- List<TextBlock> 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}.
- * <p>
- * The returned list is mutable on purpose because it will be sorted.
- * </p>
- *
- * @see {@link #createIndexOfDuplicatedTextBlocks(Iterable)}
- */
- private static List<TextBlock> extractAllDuplicatedTextBlocks(Iterable<Duplication> duplications) {
- List<TextBlock> 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<Map.Entry<TextBlock, Integer>> {
- private final int line;
-
- public TextBlockContainsLine(int line) {
- this.line = line;
- }
-
- @Override
- public boolean apply(@Nonnull Map.Entry<TextBlock, Integer> 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<Map.Entry<TextBlock, Integer>, Integer> {
- INSTANCE;
-
- @Override
- @Nonnull
- public Integer apply(@Nonnull Map.Entry<TextBlock, Integer> input) {
- return input.getValue();
- }
- }
-
- private static class TextBlockIndexGenerator implements Function<TextBlock, Integer> {
- int i = 1;
-
- @Nullable
- @Override
- public Integer apply(TextBlock input) {
- return i++;
- }
- }
-}
+++ /dev/null
-/*
- * 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<HighlightingType, String> cssClassByType = ImmutableMap.<HighlightingType, String>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<ScannerReport.SyntaxHighlightingRule> lineHighlightingIterator;
- private final RangeOffsetConverter rangeOffsetConverter;
- private final List<ScannerReport.SyntaxHighlightingRule> highlightingList;
-
- private ScannerReport.SyntaxHighlightingRule currentItem;
-
- public HighlightingLineReader(Component file, Iterator<ScannerReport.SyntaxHighlightingRule> 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<ScannerReport.SyntaxHighlightingRule> syntaxHighlightingIterator = highlightingList.iterator(); syntaxHighlightingIterator.hasNext();) {
- processHighlighting(syntaxHighlightingIterator, highlighting, lineBuilder);
- }
- if (highlighting.length() > 0) {
- lineBuilder.setHighlighting(highlighting.toString());
- }
- }
-
- private void processHighlighting(Iterator<ScannerReport.SyntaxHighlightingRule> 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;
- }
-
-}
// since previous analysis (optimization to decrease execution of blame commands). In this case
// the date is loaded from database, as it did not change from previous analysis.
- Optional<ScmInfo> scmInfoOptional = scmInfoRepository.getScmInfo(file);
+ java.util.Optional<ScmInfo> scmInfoOptional = scmInfoRepository.getScmInfo(file);
if (scmInfoOptional.isPresent()) {
ScmInfo scmInfo = scmInfoOptional.get();
path.current().addDate(scmInfo.getLatestChangeset().getDate());
+++ /dev/null
-/*
- * 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);
-
-}
+++ /dev/null
-/*
- * 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);
- }
- }
-
-}
+++ /dev/null
-/*
- * 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;
- }
-}
* @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);
}
public class SourceLinesDiffFinder {
private static final Logger LOG = Loggers.get(SourceLinesDiffFinder.class);
- private final List<String> left;
- private final List<String> right;
- public SourceLinesDiffFinder(List<String> left, List<String> right) {
- this.left = left;
- this.right = right;
- }
-
- public int[] findMatchingLines() {
+ public int[] findMatchingLines(List<String> left, List<String> right) {
int[] index = new int[right.size()];
int dbLine = left.size();
*/
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;
}
@Override
- public int[] getMatchingLines(Component component) {
+ public int[] computeMatchingLines(Component component) {
- List<String> database;
+ List<String> database = getDBLines(component);
+ List<String> report = getReportLines(component);
+
+ return new SourceLinesDiffFinder().findMatchingLines(database, report);
+ }
+
+ private List<String> getDBLines(Component component) {
try (DbSession dbSession = dbClient.openSession(false)) {
- database = fileSourceDao.selectLineHashes(dbSession, component.getUuid());
+ List<String> database = fileSourceDao.selectLineHashes(dbSession, component.getUuid());
if (database == null) {
- database = new ArrayList<>();
+ return Collections.emptyList();
}
+ return database;
}
+ }
- List<String> report;
+ private List<String> getReportLines(Component component) {
SourceLinesHashesComputer linesHashesComputer = new SourceLinesHashesComputer();
try (CloseableIterator<String> lineIterator = sourceLinesRepository.readLines(component)) {
while (lineIterator.hasNext()) {
linesHashesComputer.addLine(line);
}
}
- report = linesHashesComputer.getLineHashes();
-
- return new SourceLinesDiffFinder(database, report).findMatchingLines();
-
+ return linesHashesComputer.getLineHashes();
}
}
@Override
public CloseableIterator<String> 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<CloseableIterator<String>> linesIteratorOptional = reportReader.readFileSource(file.getReportAttributes().getRef());
@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
+++ /dev/null
-/*
- * 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<ScannerReport.Symbol, Integer> idsBySymbol;
- private final SetMultimap<Integer, ScannerReport.Symbol> symbolsPerLine;
-
- private boolean areSymbolsValid = true;
-
- public SymbolsLineReader(Component file, Iterator<ScannerReport.Symbol> symbolIterator, RangeOffsetConverter rangeOffsetConverter) {
- this.file = file;
- this.rangeOffsetConverter = rangeOffsetConverter;
- List<ScannerReport.Symbol> 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<ScannerReport.Symbol> 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<ScannerReport.Symbol, Integer> createIdsBySymbolMap(List<ScannerReport.Symbol> symbols) {
- Map<ScannerReport.Symbol, Integer> map = new IdentityHashMap<>(symbols.size());
- int symbolId = 1;
- for (ScannerReport.Symbol symbol : symbols) {
- map.put(symbol, symbolId);
- symbolId++;
- }
- return map;
- }
-
- private static SetMultimap<Integer, ScannerReport.Symbol> buildSymbolsPerLine(List<ScannerReport.Symbol> symbols) {
- SetMultimap<Integer, ScannerReport.Symbol> 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<Integer, ScannerReport.Symbol> 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<ScannerReport.Symbol> {
- 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());
- }
- }
- }
-}
--- /dev/null
+/*
+ * 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<ScannerReport.LineCoverage> coverageIterator;
+ private ScannerReport.LineCoverage coverage;
+
+ public CoverageLineReader(Iterator<ScannerReport.LineCoverage> 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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<TextBlock, Integer> duplicatedTextBlockIndexByTextBlock;
+
+ public DuplicationLineReader(Iterable<Duplication> duplications) {
+ this.duplicatedTextBlockIndexByTextBlock = createIndexOfDuplicatedTextBlocks(duplications);
+ }
+
+ @Override
+ public void read(DbFileSources.Line.Builder lineBuilder) {
+ Predicate<Map.Entry<TextBlock, Integer>> 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);
+ }
+ }
+
+ /**
+ *
+ * <p>
+ * 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()}.
+ * </p>
+ */
+ private static Map<TextBlock, Integer> createIndexOfDuplicatedTextBlocks(Iterable<Duplication> duplications) {
+ List<TextBlock> 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}.
+ * <p>
+ * The returned list is mutable on purpose because it will be sorted.
+ * </p>
+ *
+ * @see {@link #createIndexOfDuplicatedTextBlocks(Iterable)}
+ */
+ private static List<TextBlock> extractAllDuplicatedTextBlocks(Iterable<Duplication> duplications) {
+ List<TextBlock> 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<Map.Entry<TextBlock, Integer>> {
+ private final int line;
+
+ public TextBlockContainsLine(int line) {
+ this.line = line;
+ }
+
+ @Override
+ public boolean apply(@Nonnull Map.Entry<TextBlock, Integer> 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<Map.Entry<TextBlock, Integer>, Integer> {
+ INSTANCE;
+
+ @Override
+ @Nonnull
+ public Integer apply(@Nonnull Map.Entry<TextBlock, Integer> input) {
+ return input.getValue();
+ }
+ }
+
+ private static class TextBlockIndexGenerator implements Function<TextBlock, Integer> {
+ int i = 1;
+
+ @Nullable
+ @Override
+ public Integer apply(TextBlock input) {
+ return i++;
+ }
+ }
+}
--- /dev/null
+/*
+ * 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<HighlightingType, String> cssClassByType = ImmutableMap.<HighlightingType, String>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<ScannerReport.SyntaxHighlightingRule> lineHighlightingIterator;
+ private final RangeOffsetConverter rangeOffsetConverter;
+ private final List<ScannerReport.SyntaxHighlightingRule> highlightingList;
+
+ private ScannerReport.SyntaxHighlightingRule currentItem;
+
+ public HighlightingLineReader(Component file, Iterator<ScannerReport.SyntaxHighlightingRule> 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<ScannerReport.SyntaxHighlightingRule> syntaxHighlightingIterator = highlightingList.iterator(); syntaxHighlightingIterator.hasNext();) {
+ processHighlighting(syntaxHighlightingIterator, highlighting, lineBuilder);
+ }
+ if (highlighting.length() > 0) {
+ lineBuilder.setHighlighting(highlighting.toString());
+ }
+ }
+
+ private void processHighlighting(Iterator<ScannerReport.SyntaxHighlightingRule> 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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<ScannerReport.Symbol, Integer> idsBySymbol;
+ private final SetMultimap<Integer, ScannerReport.Symbol> symbolsPerLine;
+
+ private boolean areSymbolsValid = true;
+
+ public SymbolsLineReader(Component file, Iterator<ScannerReport.Symbol> symbolIterator, RangeOffsetConverter rangeOffsetConverter) {
+ this.file = file;
+ this.rangeOffsetConverter = rangeOffsetConverter;
+ List<ScannerReport.Symbol> 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<ScannerReport.Symbol> 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<ScannerReport.Symbol, Integer> createIdsBySymbolMap(List<ScannerReport.Symbol> symbols) {
+ Map<ScannerReport.Symbol, Integer> map = new IdentityHashMap<>(symbols.size());
+ int symbolId = 1;
+ for (ScannerReport.Symbol symbol : symbols) {
+ map.put(symbol, symbolId);
+ symbolId++;
+ }
+ return map;
+ }
+
+ private static SetMultimap<Integer, ScannerReport.Symbol> buildSymbolsPerLine(List<ScannerReport.Symbol> symbols) {
+ SetMultimap<Integer, ScannerReport.Symbol> 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<Integer, ScannerReport.Symbol> 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<ScannerReport.Symbol> {
+ 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());
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * 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;
@Override
public void initialize(CounterInitializationContext context) {
Component fileComponent = context.getLeaf();
- Optional<ScmInfo> scmInfoOptional = scmInfoRepository.getScmInfo(fileComponent);
+ java.util.Optional<ScmInfo> scmInfoOptional = scmInfoRepository.getScmInfo(fileComponent);
if (!scmInfoOptional.isPresent() || !context.hasPeriod()) {
return;
}
@Override
public void initialize(CounterInitializationContext context) {
Component leaf = context.getLeaf();
- Optional<ScmInfo> scmInfo = scmInfoRepository.getScmInfo(leaf);
+ java.util.Optional<ScmInfo> scmInfo = scmInfoRepository.getScmInfo(leaf);
if (!scmInfo.isPresent() || !context.hasPeriod()) {
return;
}
*/
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;
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;
@CheckForNull
public Changeset getLatestChangeWithRevision() {
- if (scmLineReader == null) {
- return null;
- }
- return scmLineReader.getLatestChangeWithRevision();
+ return scmLineReader == null ? null : scmLineReader.getLatestChangeWithRevision();
}
}
private final DbClient dbClient;
private final HtmlSourceDecorator htmlDecorator;
+ private final Function<DbFileSources.Line, String> lineToHtml;
public SourceService(DbClient dbClient, HtmlSourceDecorator htmlDecorator) {
this.dbClient = dbClient;
this.htmlDecorator = htmlDecorator;
+ this.lineToHtml = lineToHtml();
}
/**
}
public Optional<Iterable<String>> getLinesAsHtml(DbSession dbSession, String fileUuid, int from, int toInclusive) {
- return getLines(dbSession, fileUuid, from, toInclusive, lineToHtml());
+ return getLines(dbSession, fileUuid, from, toInclusive, lineToHtml);
}
private <E> Optional<Iterable<E>> getLines(DbSession dbSession, String fileUuid, int from, int toInclusive, Function<DbFileSources.Line, E> function) {
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;
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();
}
}
- private static class HashFunction implements Function<Reader, Void> {
+ private static class HashConsumer implements Consumer<Reader> {
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() {
private void noScm() {
when(scmInfoRepository.getScmInfo(component))
- .thenReturn(Optional.absent());
+ .thenReturn(java.util.Optional.empty());
}
private void withScm(long blame) {
if (scmInfo == null) {
scmInfo = mock(ScmInfo.class);
when(scmInfoRepository.getScmInfo(component))
- .thenReturn(Optional.of(scmInfo));
+ .thenReturn(java.util.Optional.empty());
}
}
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
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
Optional<DbScmInfo> 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();
}
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;
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
@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);
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);
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);
}
*/
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;
public Optional<ScmInfo> 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) {
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;
+++ /dev/null
-/*
- * 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.<ScannerReport.LineCoverage>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();
- }
-
-}
+++ /dev/null
-/*
- * 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));
- }
-
-}
+++ /dev/null
-/*
- * 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<ScannerReport.SyntaxHighlightingRule> 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<ScannerReport.SyntaxHighlightingRule> 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<TextRange, HighlightingType> textRangeByType) {
- List<ScannerReport.SyntaxHighlightingRule> syntaxHighlightingList = new ArrayList<>();
- for (Map.Entry<TextRange, HighlightingType> 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();
- }
-
-}
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;
+++ /dev/null
-/*
- * 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<Integer, Changeset> setup8LinesChangeset(Changeset changeset0, Changeset changeset1, Changeset changeset2) {
- return ImmutableMap.<Integer, Changeset>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);
-
- }
-
-}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
@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);
}
+++ /dev/null
-/*
- * 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();
- }
-
-}
--- /dev/null
+/*
+ * 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.<ScannerReport.LineCoverage>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();
+ }
+
+}
--- /dev/null
+/*
+ * 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));
+ }
+
+}
--- /dev/null
+/*
+ * 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<ScannerReport.SyntaxHighlightingRule> 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<ScannerReport.SyntaxHighlightingRule> 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<TextRange, HighlightingType> textRangeByType) {
+ List<ScannerReport.SyntaxHighlightingRule> syntaxHighlightingList = new ArrayList<>();
+ for (Map.Entry<TextRange, HighlightingType> 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();
+ }
+
+}
--- /dev/null
+/*
+ * 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<Integer, Changeset> setup8LinesChangeset(Changeset changeset0, Changeset changeset1, Changeset changeset2) {
+ return ImmutableMap.<Integer, Changeset>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);
+
+ }
+
+}
--- /dev/null
+/*
+ * 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();
+ }
+
+}
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();
}
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);
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);
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<DbFileSources.Test> tests = dto.getTestData();
assertThat(tests).hasSize(1);
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<Integer> coveredLines = dto.getTestData().get(0).getCoveredFile(0).getCoveredLineList();
assertThat(coveredLines).containsOnly(1, 2, 3, 4);
}
.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));
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);
*/
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;
}
public List<String> getLineHashes() {
- return ImmutableList.copyOf(lineHashes);
+ return Collections.unmodifiableList(lineHashes);
}
private String computeHash(String line) {
return file.exists();
}
+ public CloseableIterator<ScannerReport.LineSgnificantCode> 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<ScannerReport.SyntaxHighlightingRule> readComponentSyntaxHighlighting(int fileRef) {
File file = fileStructure.fileFor(FileStructure.Domain.SYNTAX_HIGHLIGHTINGS, fileRef);
if (fileExists(file)) {