import javax.annotation.Nullable;
import org.sonar.ce.task.CeTask;
import org.sonar.ce.task.container.TaskContainer;
+import org.sonar.ce.task.log.CeTaskMessagesImpl;
import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderImpl;
import org.sonar.ce.task.projectanalysis.api.posttask.PostProjectAnalysisTasksExecutor;
import org.sonar.ce.task.projectanalysis.batch.BatchReportDirectoryHolderImpl;
import org.sonar.ce.task.projectanalysis.scm.ScmInfoRepositoryImpl;
import org.sonar.ce.task.projectanalysis.source.DbLineHashVersion;
import org.sonar.ce.task.projectanalysis.source.FileSourceDataComputer;
+import org.sonar.ce.task.projectanalysis.source.FileSourceDataWarnings;
import org.sonar.ce.task.projectanalysis.source.LastCommitVisitor;
import org.sonar.ce.task.projectanalysis.source.NewLinesRepository;
import org.sonar.ce.task.projectanalysis.source.SignificantCodeRepository;
PostProjectAnalysisTasksExecutor.class,
ComputationStepExecutor.class,
+ // messages/warnings
+ CeTaskMessagesImpl.class,
+ FileSourceDataWarnings.class,
+
// File System
new ComputationTempFolderProvider(),
this.sourceHashComputer = new SourceHashComputer();
}
- public Data compute(Component file) {
+ public Data compute(Component file, FileSourceDataWarnings fileSourceDataWarnings) {
try (CloseableIterator<String> linesIterator = sourceLinesRepository.readLines(file);
SourceLineReadersFactory.LineReaders lineReaders = sourceLineReadersFactory.getLineReaders(file)) {
SourceLinesHashRepositoryImpl.LineHashesComputer lineHashesComputer = sourceLinesHash.getLineHashesComputerToPersist(file);
while (linesIterator.hasNext()) {
currentLine++;
- read(fileSourceBuilder, lineHashesComputer, lineReaders, currentLine, linesIterator.next(), linesIterator.hasNext());
+ String lineSource = linesIterator.next();
+ boolean hasNextLine = linesIterator.hasNext();
+
+ sourceHashComputer.addLine(lineSource, hasNextLine);
+ lineHashesComputer.addLine(lineSource);
+
+ DbFileSources.Line.Builder lineBuilder = fileSourceBuilder
+ .addLinesBuilder()
+ .setSource(lineSource)
+ .setLine(currentLine);
+ lineReaders.read(lineBuilder, readError -> fileSourceDataWarnings.addWarning(file, readError));
}
Changeset latestChangeWithRevision = lineReaders.getLatestChangeWithRevision();
}
}
- private void read(DbFileSources.Data.Builder fileSourceBuilder, SourceLinesHashRepositoryImpl.LineHashesComputer lineHashesComputer,
- SourceLineReadersFactory.LineReaders lineReaders, int currentLine, String lineSource, boolean hasNextLine) {
- sourceHashComputer.addLine(lineSource, hasNextLine);
- lineHashesComputer.addLine(lineSource);
-
- DbFileSources.Line.Builder lineBuilder = fileSourceBuilder
- .addLinesBuilder()
- .setSource(lineSource)
- .setLine(currentLine);
-
- lineReaders.read(lineBuilder);
- }
-
public static class Data {
private final DbFileSources.Data fileSourceData;
private final List<String> lineHashes;
--- /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.ce.task.projectanalysis.source;
+
+import java.util.Comparator;
+import java.util.EnumMap;
+import java.util.HashSet;
+import java.util.Set;
+import org.sonar.api.utils.System2;
+import org.sonar.ce.task.log.CeTaskMessages;
+import org.sonar.ce.task.projectanalysis.component.Component;
+import org.sonar.ce.task.projectanalysis.source.linereader.LineReader;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.lang.String.format;
+import static java.util.Objects.requireNonNull;
+import static java.util.stream.Collectors.joining;
+
+public class FileSourceDataWarnings {
+ private static final Comparator<Component> COMPONENT_COMPARATOR = Comparator.comparingInt(t -> t.getReportAttributes().getRef());
+
+ private final CeTaskMessages taskMessages;
+ private final System2 system2;
+ private final EnumMap<LineReader.Data, Set<Component>> fileErrorsPerData = new EnumMap<>(LineReader.Data.class);
+ private boolean closed = false;
+
+ public FileSourceDataWarnings(CeTaskMessages taskMessages, System2 system2) {
+ this.taskMessages = taskMessages;
+ this.system2 = system2;
+ }
+
+ public void addWarning(Component file, LineReader.ReadError readError) {
+ checkNotCommitted();
+ requireNonNull(file, "file can't be null");
+ requireNonNull(readError, "readError can't be null");
+
+ fileErrorsPerData.compute(readError.getData(), (data, existingList) -> {
+ Set<Component> res = existingList == null ? new HashSet<>() : existingList;
+ res.add(file);
+ return res;
+ });
+ }
+
+ public void commitWarnings() {
+ checkNotCommitted();
+ this.closed = true;
+ Set<Component> filesWithHighlightingErrors = fileErrorsPerData.get(LineReader.Data.HIGHLIGHTING);
+ if (filesWithHighlightingErrors == null) {
+ return;
+ }
+
+ taskMessages.add(new CeTaskMessages.Message(computeMessage(filesWithHighlightingErrors), system2.now()));
+ }
+
+ private static String computeMessage(Set<Component> filesWithHighlightingErrors) {
+ if (filesWithHighlightingErrors.size() == 1) {
+ Component file = filesWithHighlightingErrors.iterator().next();
+ return format("Inconsistent highlighting data detected on file '%s'. " +
+ "File source may have been modified while analysis was running.",
+ file.getReportAttributes().getPath());
+ }
+ String lineHeader = "\n ° ";
+ return format("Inconsistent highlighting data detected on some files (%s in total). " +
+ "File source may have been modified while analysis was running.", filesWithHighlightingErrors.size())
+ + filesWithHighlightingErrors.stream()
+ .sorted(COMPONENT_COMPARATOR)
+ .limit(5)
+ .map(component -> component.getReportAttributes().getPath())
+ .collect(joining(lineHeader, lineHeader, ""));
+ }
+
+ private void checkNotCommitted() {
+ checkState(!closed, "warnings already commit");
+ }
+
+}
*/
package org.sonar.ce.task.projectanalysis.source;
+import com.google.common.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
+import java.util.function.Consumer;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonar.ce.task.projectanalysis.batch.BatchReportReader;
readers.add(new DuplicationLineReader(duplicationRepository.getDuplications(component)));
readers.add(new IsNewLineReader(newLinesRepository, component));
- return new LineReaders(readers, scmLineReader, closeables);
+ return new LineReadersImpl(readers, scmLineReader, closeables);
}
- static class LineReaders implements AutoCloseable, LineReader {
+ interface LineReaders extends AutoCloseable {
+ void read(DbFileSources.Line.Builder lineBuilder, Consumer<LineReader.ReadError> readErrorConsumer);
+
+ @CheckForNull
+ Changeset getLatestChangeWithRevision();
+
+ @Override
+ void close();
+ }
+
+ @VisibleForTesting
+ static final class LineReadersImpl implements LineReaders {
final List<LineReader> readers;
@Nullable
final ScmLineReader scmLineReader;
final List<CloseableIterator<?>> closeables;
- LineReaders(List<LineReader> readers, @Nullable ScmLineReader scmLineReader, List<CloseableIterator<?>> closeables) {
+ LineReadersImpl(List<LineReader> readers, @Nullable ScmLineReader scmLineReader, List<CloseableIterator<?>> closeables) {
this.readers = readers;
this.scmLineReader = scmLineReader;
this.closeables = closeables;
}
- @Override public void close() {
+ @Override
+ public void close() {
for (CloseableIterator<?> reportIterator : closeables) {
reportIterator.close();
}
}
- @Override public void read(DbFileSources.Line.Builder lineBuilder) {
+ public void read(DbFileSources.Line.Builder lineBuilder, Consumer<LineReader.ReadError> readErrorConsumer) {
for (LineReader r : readers) {
- r.read(lineBuilder);
+ r.read(lineBuilder)
+ .ifPresent(readErrorConsumer);
}
}
+ @Override
@CheckForNull
public Changeset getLatestChangeWithRevision() {
return scmLineReader == null ? null : scmLineReader.getLatestChangeWithRevision();
package org.sonar.ce.task.projectanalysis.source.linereader;
import java.util.Iterator;
+import java.util.Optional;
import javax.annotation.CheckForNull;
import org.sonar.db.protobuf.DbFileSources;
import org.sonar.scanner.protocol.output.ScannerReport;
}
@Override
- public void read(DbFileSources.Line.Builder lineBuilder) {
+ public Optional<ReadError> read(DbFileSources.Line.Builder lineBuilder) {
ScannerReport.LineCoverage reportCoverage = getNextLineCoverageIfMatchLine(lineBuilder.getLine());
if (reportCoverage != null) {
processCoverage(lineBuilder, reportCoverage);
coverage = null;
}
+ return Optional.empty();
}
private static void processCoverage(DbFileSources.Line.Builder lineBuilder, ScannerReport.LineCoverage reportCoverage) {
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.sonar.ce.task.projectanalysis.duplication.Duplicate;
}
@Override
- public void read(DbFileSources.Line.Builder lineBuilder) {
+ public Optional<ReadError> read(DbFileSources.Line.Builder lineBuilder) {
Predicate<Map.Entry<TextBlock, Integer>> containsLine = new TextBlockContainsLine(lineBuilder.getLine());
for (Integer textBlockIndex : from(duplicatedTextBlockIndexByTextBlock.entrySet())
.filter(containsLine)
.toSortedList(Ordering.natural())) {
lineBuilder.addDuplication(textBlockIndex);
}
+ return Optional.empty();
}
/**
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import javax.annotation.CheckForNull;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import static com.google.common.collect.Lists.newArrayList;
import static java.lang.String.format;
+import static org.sonar.ce.task.projectanalysis.source.linereader.LineReader.Data.HIGHLIGHTING;
import static org.sonar.ce.task.projectanalysis.source.linereader.RangeOffsetConverter.OFFSET_SEPARATOR;
import static org.sonar.ce.task.projectanalysis.source.linereader.RangeOffsetConverter.SYMBOLS_SEPARATOR;
private static final Logger LOG = Loggers.get(HighlightingLineReader.class);
- private boolean isHighlightingValid = true;
+ private ReadError readError = null;
private static final Map<HighlightingType, String> cssClassByType = ImmutableMap.<HighlightingType, String>builder()
.put(HighlightingType.ANNOTATION, "a")
this.highlightingList = newArrayList();
}
+ /**
+ * Stops reading at first encountered error, which implies the same
+ * {@link org.sonar.ce.task.projectanalysis.source.linereader.LineReader.ReadError} will be returned once an error
+ * has been encountered and for any then provided {@link DbFileSources.Line.Builder lineBuilder}.
+ */
@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.getDbKey()), e);
+ public Optional<ReadError> read(DbFileSources.Line.Builder lineBuilder) {
+ if (readError == null) {
+ try {
+ processHighlightings(lineBuilder);
+ } catch (RangeOffsetConverterException e) {
+ readError = new ReadError(HIGHLIGHTING, lineBuilder.getLine());
+ LOG.warn(format("Inconsistency detected in Highlighting data. Highlighting will be ignored for file '%s'", file.getDbKey()), e);
+ }
}
+ return Optional.ofNullable(readError);
}
private void processHighlightings(DbFileSources.Line.Builder lineBuilder) {
package org.sonar.ce.task.projectanalysis.source.linereader;
import java.util.Collections;
+import java.util.Optional;
import java.util.Set;
import org.sonar.ce.task.projectanalysis.component.Component;
import org.sonar.ce.task.projectanalysis.source.NewLinesRepository;
this.newLines = newLinesRepository.getNewLines(file).orElse(Collections.emptySet());
}
- @Override public void read(DbFileSources.Line.Builder lineBuilder) {
+ @Override
+ public Optional<ReadError> read(DbFileSources.Line.Builder lineBuilder) {
lineBuilder.setIsNewLine(newLines.contains(lineBuilder.getLine()));
+ return Optional.empty();
}
}
*/
package org.sonar.ce.task.projectanalysis.source.linereader;
+import java.util.Objects;
+import java.util.Optional;
+import javax.annotation.concurrent.Immutable;
import org.sonar.db.protobuf.DbFileSources;
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
@FunctionalInterface
public interface LineReader {
- void read(DbFileSources.Line.Builder lineBuilder);
+ Optional<ReadError> read(DbFileSources.Line.Builder lineBuilder);
+
+ enum Data {
+ COVERAGE, DUPLICATION, HIGHLIGHTING, SCM, SYMBOLS
+ }
+
+ @Immutable
+ final class ReadError {
+ private final Data data;
+ private final int line;
+
+ public ReadError(Data data, int line) {
+ requireNonNull(data);
+ checkArgument(line >= 0);
+ this.data = data;
+ this.line = line;
+ }
+
+ public Data getData() {
+ return data;
+ }
+
+ public int getLine() {
+ return line;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ReadError readError = (ReadError) o;
+ return line == readError.line &&
+ data == readError.data;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(data, line);
+ }
+ @Override
+ public String toString() {
+ return "ReadError{" +
+ "data=" + data +
+ ", line=" + line +
+ '}';
+ }
+ }
}
*/
package org.sonar.ce.task.projectanalysis.source.linereader;
+import java.util.Optional;
import javax.annotation.CheckForNull;
-import org.sonar.db.protobuf.DbFileSources;
import org.sonar.ce.task.projectanalysis.scm.Changeset;
import org.sonar.ce.task.projectanalysis.scm.ScmInfo;
+import org.sonar.db.protobuf.DbFileSources;
public class ScmLineReader implements LineReader {
}
@Override
- public void read(DbFileSources.Line.Builder lineBuilder) {
+ public Optional<ReadError> read(DbFileSources.Line.Builder lineBuilder) {
if (scmReport.hasChangesetForLine(lineBuilder.getLine())) {
Changeset changeset = scmReport.getChangesetForLine(lineBuilder.getLine());
String author = changeset.getAuthor();
updateLatestChangeWithRevision(changeset);
}
}
+ return Optional.empty();
}
private void updateLatestChange(Changeset newChangeSet) {
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 java.util.Optional;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.ce.task.projectanalysis.component.Component;
private final Map<ScannerReport.Symbol, Integer> idsBySymbol;
private final SetMultimap<Integer, ScannerReport.Symbol> symbolsPerLine;
- private boolean areSymbolsValid = true;
+ private ReadError readError = null;
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);
+ symbols.sort(SymbolsComparator.INSTANCE);
this.idsBySymbol = createIdsBySymbolMap(symbols);
this.symbolsPerLine = buildSymbolsPerLine(symbols);
}
+ /**
+ * Stops reading at first encountered error, which implies the same
+ * {@link org.sonar.ce.task.projectanalysis.source.linereader.LineReader.ReadError} will be returned once an error
+ * has been encountered and for any then provided {@link DbFileSources.Line.Builder lineBuilder}.
+ */
@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.getDbKey()), e);
+ public Optional<ReadError> read(DbFileSources.Line.Builder lineBuilder) {
+ if (readError == null) {
+ try {
+ processSymbols(lineBuilder);
+ } catch (RangeOffsetConverter.RangeOffsetConverterException e) {
+ readError = new ReadError(Data.SYMBOLS, lineBuilder.getLine());
+ LOG.warn(format("Inconsistency detected in Symbols data. Symbols will be ignored for file '%s'", file.getDbKey()), e);
+ }
}
+ return Optional.ofNullable(readError);
}
private void processSymbols(DbFileSources.Line.Builder lineBuilder) {
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);
+ lineSymbols.sort(SymbolsComparator.INSTANCE);
StringBuilder symbolString = new StringBuilder();
for (ScannerReport.Symbol lineSymbol : lineSymbols) {
import org.sonar.ce.task.projectanalysis.component.TypeAwareVisitorAdapter;
import org.sonar.ce.task.projectanalysis.scm.Changeset;
import org.sonar.ce.task.projectanalysis.source.FileSourceDataComputer;
+import org.sonar.ce.task.projectanalysis.source.FileSourceDataWarnings;
import org.sonar.ce.task.projectanalysis.source.SourceLinesHashRepository;
import org.sonar.ce.task.step.ComputationStep;
import org.sonar.db.DbClient;
private final TreeRootHolder treeRootHolder;
private final SourceLinesHashRepository sourceLinesHash;
private final FileSourceDataComputer fileSourceDataComputer;
+ private final FileSourceDataWarnings fileSourceDataWarnings;
public PersistFileSourcesStep(DbClient dbClient, System2 system2, TreeRootHolder treeRootHolder,
- SourceLinesHashRepository sourceLinesHash, FileSourceDataComputer fileSourceDataComputer) {
+ SourceLinesHashRepository sourceLinesHash, FileSourceDataComputer fileSourceDataComputer,
+ FileSourceDataWarnings fileSourceDataWarnings) {
this.dbClient = dbClient;
this.system2 = system2;
this.treeRootHolder = treeRootHolder;
this.sourceLinesHash = sourceLinesHash;
this.fileSourceDataComputer = fileSourceDataComputer;
+ this.fileSourceDataWarnings = fileSourceDataWarnings;
}
@Override
try (DbSession dbSession = dbClient.openSession(false)) {
new DepthTraversalTypeAwareCrawler(new FileSourceVisitor(dbSession))
.visit(treeRootHolder.getRoot());
+ } finally {
+ fileSourceDataWarnings.commitWarnings();
}
}
@Override
public void visitFile(Component file) {
try {
- FileSourceDataComputer.Data fileSourceData = fileSourceDataComputer.compute(file);
+ FileSourceDataComputer.Data fileSourceData = fileSourceDataComputer.compute(file, fileSourceDataWarnings);
persistSource(fileSourceData, file);
} catch (Exception e) {
throw new IllegalStateException(String.format("Cannot persist sources of %s", file.getDbKey()), e);
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
+import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
+import java.util.stream.Collectors;
import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
import org.junit.Test;
import org.picocontainer.DefaultPicoContainer;
import org.picocontainer.PicoContainer;
+import org.reflections.Reflections;
import org.sonar.ce.task.CeTask;
import org.sonar.ce.task.container.TaskContainer;
import org.sonar.ce.task.projectanalysis.step.PersistComponentsStep;
import org.sonar.ce.task.step.ComputationStep;
-import org.sonar.ce.task.step.StepsExplorer;
import org.sonar.core.platform.ComponentContainer;
+import org.sonar.core.util.stream.MoreCollectors;
-import static com.google.common.base.Predicates.notNull;
import static com.google.common.collect.FluentIterable.from;
import static com.google.common.collect.Sets.difference;
import static org.assertj.core.api.Assertions.assertThat;
AddedObjectsRecorderTaskContainer container = new AddedObjectsRecorderTaskContainer();
underTest.populateContainer(container);
- Set<String> computationStepClassNames = from(container.added)
- .transform(new Function<Object, Class<?>>() {
- @Nullable
- @Override
- public Class<?> apply(Object input) {
- if (input instanceof Class) {
- return (Class<?>) input;
- }
- return null;
+ Set<String> computationStepClassNames = container.added.stream()
+ .map(s -> {
+ if (s instanceof Class) {
+ return (Class<?>) s;
}
+ return null;
})
- .filter(notNull())
- .filter(IsComputationStep.INSTANCE)
- .transform(StepsExplorer.toCanonicalName())
- .toSet();
+ .filter(Objects::nonNull)
+ .filter(ComputationStep.class::isAssignableFrom)
+ .map(Class::getCanonicalName)
+ .collect(MoreCollectors.toSet());
+
+ assertThat(difference(retrieveStepPackageStepsCanonicalNames(PROJECTANALYSIS_STEP_PACKAGE), computationStepClassNames)).isEmpty();
+ }
- assertThat(difference(StepsExplorer.retrieveStepPackageStepsCanonicalNames(PROJECTANALYSIS_STEP_PACKAGE), computationStepClassNames)).isEmpty();
+ /**
+ * Compute set of canonical names of classes implementing ComputationStep in the specified package using reflection.
+ */
+ private static Set<Object> retrieveStepPackageStepsCanonicalNames(String packageName) {
+ Reflections reflections = new Reflections(packageName);
+
+ return reflections.getSubTypesOf(ComputationStep.class).stream()
+ .filter(input -> !Modifier.isAbstract(input.getModifiers()))
+ .map(Class::getCanonicalName)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toSet());
}
@Test
}
- private enum IsComputationStep implements Predicate<Class<?>> {
- INSTANCE;
-
- @Override
- public boolean apply(Class<?> input) {
- return ComputationStep.class.isAssignableFrom(input);
- }
- }
-
private static class AddedObjectsRecorderTaskContainer implements TaskContainer {
private static final DefaultPicoContainer SOME_EMPTY_PICO_CONTAINER = new DefaultPicoContainer();
*/
package org.sonar.ce.task.projectanalysis.source;
-import java.util.Arrays;
-import java.util.Collections;
+import java.util.Iterator;
import java.util.List;
+import java.util.Random;
+import java.util.function.Consumer;
+import java.util.stream.IntStream;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
import org.sonar.ce.task.projectanalysis.component.Component;
import org.sonar.ce.task.projectanalysis.component.ReportComponent;
+import org.sonar.ce.task.projectanalysis.scm.Changeset;
import org.sonar.ce.task.projectanalysis.source.SourceLinesHashRepositoryImpl.LineHashesComputer;
import org.sonar.ce.task.projectanalysis.source.linereader.LineReader;
-import org.sonar.ce.task.projectanalysis.source.linereader.ScmLineReader;
+import org.sonar.core.hash.SourceHashComputer;
import org.sonar.core.util.CloseableIterator;
+import org.sonar.db.protobuf.DbFileSources;
+import static java.util.stream.Collectors.toList;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.same;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
public class FileSourceDataComputerTest {
private LineHashesComputer lineHashesComputer = mock(LineHashesComputer.class);
private SourceLineReadersFactory sourceLineReadersFactory = mock(SourceLineReadersFactory.class);
private SourceLinesHashRepository sourceLinesHashRepository = mock(SourceLinesHashRepository.class);
- private ScmLineReader scmLineReader = mock(ScmLineReader.class);
- private CloseableIterator closeableIterator = mock(CloseableIterator.class);
- private FileSourceDataComputer fileSourceDataComputer;
+ private FileSourceDataComputer underTest = new FileSourceDataComputer(sourceLinesRepository, sourceLineReadersFactory, sourceLinesHashRepository);
+
+ private FileSourceDataWarnings fileSourceDataWarnings = mock(FileSourceDataWarnings.class);
+ private SourceLineReadersFactory.LineReaders lineReaders = mock(SourceLineReadersFactory.LineReaders.class);
@Before
public void before() {
when(sourceLinesHashRepository.getLineHashesComputerToPersist(FILE)).thenReturn(lineHashesComputer);
- LineReader reader = line -> line.setHighlighting("h-" + line.getLine());
- SourceLineReadersFactory.LineReaders lineReaders = new SourceLineReadersFactory.LineReaders(Collections.singletonList(reader), scmLineReader,
- Collections.singletonList(closeableIterator));
- when(sourceLineReadersFactory.getLineReaders(FILE)).thenReturn(lineReaders);
-
- fileSourceDataComputer = new FileSourceDataComputer(sourceLinesRepository, sourceLineReadersFactory, sourceLinesHashRepository);
+ when(sourceLineReadersFactory.getLineReaders(FILE)).thenReturn(mock(SourceLineReadersFactory.LineReaders.class));
}
@Test
- public void compute_one_line() {
- List<String> lineHashes = Collections.singletonList("lineHash");
- when(sourceLinesRepository.readLines(FILE)).thenReturn(CloseableIterator.from(Collections.singletonList("line1").iterator()));
- when(lineHashesComputer.getResult()).thenReturn(lineHashes);
-
- FileSourceDataComputer.Data data = fileSourceDataComputer.compute(FILE);
-
- assertThat(data.getLineHashes()).isEqualTo(lineHashes);
- assertThat(data.getSrcHash()).isEqualTo("137f72c3708c6bd0de00a0e5a69c699b");
- assertThat(data.getLineData().getLinesList()).hasSize(1);
- assertThat(data.getLineData().getLines(0).getHighlighting()).isEqualTo("h-1");
-
- verify(lineHashesComputer).addLine("line1");
- verify(lineHashesComputer).getResult();
- verify(closeableIterator).close();
- verifyNoMoreInteractions(lineHashesComputer);
+ public void compute_calls_read_for_each_line_and_passe_read_error_to_fileSourceDataWarnings() {
+ int lineCount = 1 + new Random().nextInt(10);
+ List<String> lines = IntStream.range(0, lineCount).mapToObj(i -> "line" + i).collect(toList());
+ when(sourceLinesRepository.readLines(FILE)).thenReturn(CloseableIterator.from(lines.iterator()));
+ when(sourceLineReadersFactory.getLineReaders(FILE)).thenReturn(lineReaders);
+ when(sourceLinesHashRepository.getLineHashesComputerToPersist(FILE)).thenReturn(lineHashesComputer);
+ // mock an implementation that will call the ReadErrorConsumer in order to verify that the provided consumer is
+ // doing what we expect: pass readError to fileSourceDataWarnings
+ int randomStartPoint = new Random().nextInt(500);
+ doAnswer(new Answer() {
+ int i = randomStartPoint;
+
+ @Override
+ public Object answer(InvocationOnMock invocation) {
+ Consumer<LineReader.ReadError> readErrorConsumer = invocation.getArgument(1);
+ readErrorConsumer.accept(new LineReader.ReadError(LineReader.Data.SYMBOLS, i++));
+ return null;
+ }
+ }).when(lineReaders).read(any(), any());
+
+ underTest.compute(FILE, fileSourceDataWarnings);
+
+ ArgumentCaptor<DbFileSources.Line.Builder> lineBuilderCaptor = ArgumentCaptor.forClass(DbFileSources.Line.Builder.class);
+ verify(lineReaders, times(lineCount)).read(lineBuilderCaptor.capture(), any());
+ assertThat(lineBuilderCaptor.getAllValues())
+ .extracting(DbFileSources.Line.Builder::getSource)
+ .containsOnlyElementsOf(lines);
+ assertThat(lineBuilderCaptor.getAllValues())
+ .extracting(DbFileSources.Line.Builder::getLine)
+ .containsExactly(IntStream.range(1, lineCount + 1).boxed().toArray(Integer[]::new));
+ ArgumentCaptor<LineReader.ReadError> readErrorCaptor = ArgumentCaptor.forClass(LineReader.ReadError.class);
+ verify(fileSourceDataWarnings, times(lineCount)).addWarning(same(FILE), readErrorCaptor.capture());
+ assertThat(readErrorCaptor.getAllValues())
+ .extracting(LineReader.ReadError::getLine)
+ .containsExactly(IntStream.range(randomStartPoint, randomStartPoint + lineCount).boxed().toArray(Integer[]::new));
}
@Test
- public void compute_two_lines() {
- List<String> lineHashes = Arrays.asList("137f72c3708c6bd0de00a0e5a69c699b", "e6251bcf1a7dc3ba5e7933e325bbe605");
- when(sourceLinesRepository.readLines(FILE)).thenReturn(CloseableIterator.from(Arrays.asList("line1", "line2").iterator()));
- when(lineHashesComputer.getResult()).thenReturn(lineHashes);
-
- FileSourceDataComputer.Data data = fileSourceDataComputer.compute(FILE);
-
- assertThat(data.getLineHashes()).isEqualTo(lineHashes);
- assertThat(data.getSrcHash()).isEqualTo("ee5a58024a155466b43bc559d953e018");
- assertThat(data.getLineData().getLinesList()).hasSize(2);
- assertThat(data.getLineData().getLines(0).getHighlighting()).isEqualTo("h-1");
- assertThat(data.getLineData().getLines(1).getHighlighting()).isEqualTo("h-2");
-
- verify(lineHashesComputer).addLine("line1");
- verify(lineHashesComputer).addLine("line2");
- verify(lineHashesComputer).getResult();
- verify(closeableIterator).close();
- verifyNoMoreInteractions(lineHashesComputer);
+ public void compute_builds_data_object_from_lines() {
+ int lineCount = 1 + new Random().nextInt(10);
+ int randomStartPoint = new Random().nextInt(500);
+ List<String> lines = IntStream.range(0, lineCount).mapToObj(i -> "line" + i).collect(toList());
+ List<String> expectedLineHashes = IntStream.range(0, 1 + new Random().nextInt(12)).mapToObj(i -> "str_" + i).collect(toList());
+ Changeset expectedChangeset = Changeset.newChangesetBuilder().setDate((long) new Random().nextInt(9_999)).build();
+ String expectedSrcHash = computeSrcHash(lines);
+ CloseableIterator<String> lineIterator = spy(CloseableIterator.from(lines.iterator()));
+ DbFileSources.Data.Builder expectedLineDataBuilder = DbFileSources.Data.newBuilder();
+ for (int i = 0; i < lines.size(); i++) {
+ expectedLineDataBuilder.addLinesBuilder()
+ .setSource(lines.get(i))
+ .setLine(i + 1)
+ // scmAuthor will be set with specific value by our mock implementation of LinesReaders.read()
+ .setScmAuthor("reader_called_" + (randomStartPoint + i));
+ }
+ when(sourceLinesRepository.readLines(FILE)).thenReturn(lineIterator);
+ when(sourceLineReadersFactory.getLineReaders(FILE)).thenReturn(lineReaders);
+ when(sourceLinesHashRepository.getLineHashesComputerToPersist(FILE)).thenReturn(lineHashesComputer);
+ when(lineHashesComputer.getResult()).thenReturn(expectedLineHashes);
+ when(lineReaders.getLatestChangeWithRevision()).thenReturn(expectedChangeset);
+ // mocked implementation of LineReader.read to ensure changes done by it to the lineBuilder argument actually end
+ // up in the FileSourceDataComputer.Data object returned
+ doAnswer(new Answer() {
+ int i = 0;
+
+ @Override
+ public Object answer(InvocationOnMock invocation) {
+ DbFileSources.Line.Builder lineBuilder = invocation.getArgument(0);
+ lineBuilder.setScmAuthor("reader_called_" + (randomStartPoint + i++));
+ return null;
+ }
+ }).when(lineReaders).read(any(), any());
+
+ FileSourceDataComputer.Data data = underTest.compute(FILE, fileSourceDataWarnings);
+
+ assertThat(data.getLineHashes()).isEqualTo(expectedLineHashes);
+ assertThat(data.getSrcHash()).isEqualTo(expectedSrcHash);
+ assertThat(data.getLatestChangeWithRevision()).isSameAs(expectedChangeset);
+ assertThat(data.getLineData()).isEqualTo(expectedLineDataBuilder.build());
+
+ verify(lineIterator).close();
+ verify(lineReaders).close();
+ }
+
+ private static String computeSrcHash(List<String> lines) {
+ SourceHashComputer computer = new SourceHashComputer();
+ Iterator<String> iterator = lines.iterator();
+ while (iterator.hasNext()) {
+ computer.addLine(iterator.next(), iterator.hasNext());
+ }
+ return computer.getHash();
}
}
--- /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.ce.task.projectanalysis.source;
+
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import java.util.Arrays;
+import java.util.Random;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.sonar.api.utils.System2;
+import org.sonar.ce.task.log.CeTaskMessages;
+import org.sonar.ce.task.projectanalysis.component.Component;
+import org.sonar.ce.task.projectanalysis.component.ReportComponent;
+import org.sonar.ce.task.projectanalysis.source.linereader.LineReader;
+
+import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+import static org.sonar.ce.task.projectanalysis.source.linereader.LineReader.Data.HIGHLIGHTING;
+
+@RunWith(DataProviderRunner.class)
+public class FileSourceDataWarningsTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private CeTaskMessages taskMessages = mock(CeTaskMessages.class);
+ private System2 system2 = mock(System2.class);
+ private Random random = new Random();
+ private int line = 1 + new Random().nextInt(200);
+ private long timeStamp = 9_887L + new Random().nextInt(300);
+ private String path = randomAlphabetic(50);
+
+ private FileSourceDataWarnings underTest = new FileSourceDataWarnings(taskMessages, system2);
+
+ @Test
+ public void addWarning_fails_with_NPE_if_file_is_null() {
+ LineReader.ReadError readError = new LineReader.ReadError(HIGHLIGHTING, 2);
+
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("file can't be null");
+
+ underTest.addWarning(null, readError);
+ }
+
+ @Test
+ public void addWarning_fails_with_NPE_if_readError_is_null() {
+ Component component = mock(Component.class);
+
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("readError can't be null");
+
+ underTest.addWarning(component, null);
+ }
+
+ @Test
+ public void addWarnings_fails_with_ISE_if_called_after_commitWarnings() {
+ underTest.commitWarnings();
+
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("warnings already commit");
+
+ underTest.addWarning(null /*doesn't matter*/, null /*doesn't matter*/);
+ }
+
+ @Test
+ public void commitWarnings_fails_with_ISE_if_called_after_commitWarnings() {
+ underTest.commitWarnings();
+
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("warnings already commit");
+
+ underTest.commitWarnings();
+ }
+
+ @Test
+ public void create_highlighting_warning_when_one_file_HIGHLIGHT_read_error() {
+ ReportComponent file = ReportComponent.builder(Component.Type.FILE, 1)
+ .setUuid("uuid")
+ .setPath(path)
+ .build();
+ LineReader.ReadError readError = new LineReader.ReadError(HIGHLIGHTING, line);
+ when(system2.now()).thenReturn(timeStamp);
+
+ underTest.addWarning(file, readError);
+
+ verifyZeroInteractions(taskMessages);
+
+ underTest.commitWarnings();
+
+ verify(taskMessages, times(1))
+ .add(new CeTaskMessages.Message("Inconsistent highlighting data detected on file '" + path + "'. " +
+ "File source may have been modified while analysis was running.", timeStamp));
+ }
+
+ @Test
+ public void create_highlighting_warning_when_any_number_of_read_error_for_one_file() {
+ ReportComponent file = ReportComponent.builder(Component.Type.FILE, 1)
+ .setUuid("uuid")
+ .setPath(path)
+ .build();
+ LineReader.ReadError[] readErrors = IntStream.range(0, 1 + random.nextInt(10))
+ .mapToObj(i -> new LineReader.ReadError(HIGHLIGHTING, line + i))
+ .toArray(LineReader.ReadError[]::new);
+ when(system2.now()).thenReturn(timeStamp);
+
+ Arrays.stream(readErrors).forEach(readError -> underTest.addWarning(file, readError));
+
+ verifyZeroInteractions(taskMessages);
+
+ underTest.commitWarnings();
+
+ verify(taskMessages, times(1))
+ .add(new CeTaskMessages.Message("Inconsistent highlighting data detected on file '" + path + "'. " +
+ "File source may have been modified while analysis was running.", timeStamp));
+ }
+
+ @Test
+ public void create_highlighting_warning_when_any_number_of_read_error_for_less_than_5_files() {
+ int fileCount = 2 + random.nextInt(3);
+ Component[] files = IntStream.range(0, fileCount)
+ .mapToObj(i -> ReportComponent.builder(Component.Type.FILE, i)
+ .setUuid("uuid_" + i)
+ .setPath(path + "_" + i)
+ .build())
+ .toArray(Component[]::new);
+ when(system2.now()).thenReturn(timeStamp);
+
+ Arrays.stream(files).forEach(file -> IntStream.range(0, 1 + random.nextInt(10))
+ .forEach(i -> underTest.addWarning(file, new LineReader.ReadError(HIGHLIGHTING, line + i))));
+
+ verifyZeroInteractions(taskMessages);
+
+ underTest.commitWarnings();
+
+ String expectedMessage = "Inconsistent highlighting data detected on some files (" + fileCount + " in total). " +
+ "File source may have been modified while analysis was running." +
+ Arrays.stream(files).map(f -> f.getReportAttributes().getPath()).collect(Collectors.joining("\n ° ", "\n ° ", ""));
+ verify(taskMessages, times(1))
+ .add(new CeTaskMessages.Message(expectedMessage, timeStamp));
+ }
+
+ @Test
+ public void create_highlighting_warning_when_any_number_of_read_error_for_more_than_5_files_only_the_5_first_by_ref() {
+ int fileCount = 6 + random.nextInt(4);
+ Component[] files = IntStream.range(0, fileCount)
+ .mapToObj(i -> ReportComponent.builder(Component.Type.FILE, i)
+ .setUuid("uuid_" + i)
+ .setPath(path + "_" + i)
+ .build())
+ .toArray(Component[]::new);
+ when(system2.now()).thenReturn(timeStamp);
+
+ Arrays.stream(files).forEach(file -> IntStream.range(0, 1 + random.nextInt(10))
+ .forEach(i -> underTest.addWarning(file, new LineReader.ReadError(HIGHLIGHTING, line + i))));
+
+ verifyZeroInteractions(taskMessages);
+
+ underTest.commitWarnings();
+
+ String expectedMessage = "Inconsistent highlighting data detected on some files (" + fileCount + " in total). " +
+ "File source may have been modified while analysis was running." +
+ Arrays.stream(files).limit(5).map(f -> f.getReportAttributes().getPath()).collect(Collectors.joining("\n ° ", "\n ° ", ""));
+ verify(taskMessages, times(1))
+ .add(new CeTaskMessages.Message(expectedMessage, timeStamp));
+ }
+
+ @Test
+ @UseDataProvider("anyDataButHighlight")
+ public void creates_no_warning_when_read_error_for_anything_but_highlighting(LineReader.Data data) {
+ ReportComponent file = ReportComponent.builder(Component.Type.FILE, 1)
+ .setUuid("uuid")
+ .setPath(path)
+ .build();
+ LineReader.ReadError readError = new LineReader.ReadError(data, line);
+ when(system2.now()).thenReturn(timeStamp);
+
+ underTest.addWarning(file, readError);
+
+ verifyZeroInteractions(taskMessages);
+
+ underTest.commitWarnings();
+
+ verifyZeroInteractions(taskMessages);
+ }
+
+ @DataProvider
+ public static Object[][] anyDataButHighlight() {
+ return Arrays.stream(LineReader.Data.values())
+ .filter(t -> t != HIGHLIGHTING)
+ .map(t -> new Object[] {t})
+ .toArray(Object[][]::new);
+ }
+}
--- /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.ce.task.projectanalysis.source;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.Random;
+import java.util.function.Consumer;
+import java.util.stream.IntStream;
+import org.junit.Test;
+import org.sonar.ce.task.projectanalysis.scm.Changeset;
+import org.sonar.ce.task.projectanalysis.source.linereader.LineReader;
+import org.sonar.ce.task.projectanalysis.source.linereader.LineReader.ReadError;
+import org.sonar.ce.task.projectanalysis.source.linereader.ScmLineReader;
+import org.sonar.core.util.CloseableIterator;
+import org.sonar.db.protobuf.DbFileSources;
+
+import static java.util.Arrays.asList;
+import static java.util.Arrays.stream;
+import static java.util.stream.Collectors.toList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+public class LineReadersImplTest {
+ private static final Consumer<ReadError> NOOP_READ_ERROR_CONSUMER = readError -> {
+ };
+
+ @Test
+ public void close_closes_all_closeables() {
+ LineReader r1 = mock(LineReader.class);
+ LineReader r2 = mock(LineReader.class);
+ CloseableIterator<Object> c1 = newCloseableIteratorMock();
+ CloseableIterator<Object> c2 = newCloseableIteratorMock();
+
+ SourceLineReadersFactory.LineReaders lineReaders = new SourceLineReadersFactory.LineReadersImpl(asList(r1, r2), null, asList(c1, c2));
+ lineReaders.close();
+
+ verify(c1).close();
+ verify(c2).close();
+ verifyNoMoreInteractions(c1, c2);
+ verifyZeroInteractions(r1, r2);
+ }
+
+ @Test
+ public void read_calls_all_readers() {
+ LineReader r1 = mock(LineReader.class);
+ LineReader r2 = mock(LineReader.class);
+ CloseableIterator<Object> c1 = newCloseableIteratorMock();
+ CloseableIterator<Object> c2 = newCloseableIteratorMock();
+
+ SourceLineReadersFactory.LineReadersImpl lineReaders = new SourceLineReadersFactory.LineReadersImpl(asList(r1, r2), null, asList(c1, c2));
+ DbFileSources.Line.Builder builder = DbFileSources.Line.newBuilder();
+ lineReaders.read(builder, NOOP_READ_ERROR_CONSUMER);
+
+ verify(r1).read(builder);
+ verify(r2).read(builder);
+ verifyNoMoreInteractions(r1, r2);
+ verifyZeroInteractions(c1, c2);
+ }
+
+ @Test
+ public void read_calls_ReaderError_consumer_with_each_read_error_returned_by_all_readers() {
+ int readErrorCount = 2 + 2 * new Random().nextInt(10);
+ int halfCount = readErrorCount / 2;
+ ReadError[] expectedReadErrors = IntStream.range(0, readErrorCount)
+ .mapToObj(i -> new ReadError(LineReader.Data.SYMBOLS, i))
+ .toArray(ReadError[]::new);
+ LineReader[] lineReaders = IntStream.range(0, halfCount)
+ .mapToObj(i -> {
+ LineReader lineReader = mock(LineReader.class);
+ when(lineReader.read(any()))
+ .thenReturn(Optional.of(expectedReadErrors[i]))
+ .thenReturn(Optional.of(expectedReadErrors[i + halfCount]))
+ .thenReturn(Optional.empty());
+ return lineReader;
+ })
+ .toArray(LineReader[]::new);
+ DbFileSources.Line.Builder builder = DbFileSources.Line.newBuilder();
+ SourceLineReadersFactory.LineReadersImpl underTest = new SourceLineReadersFactory.LineReadersImpl(
+ stream(lineReaders).collect(toList()), null, Collections.emptyList());
+ List<ReadError> readErrors = new ArrayList<>();
+
+ // calls first mocked result of each Reader mock => we get first half read errors
+ underTest.read(builder, readErrors::add);
+ assertThat(readErrors).contains(stream(expectedReadErrors).limit(halfCount).toArray(ReadError[]::new));
+
+ // calls first mocked result of each Reader mock => we get second half read errors
+ readErrors.clear();
+ underTest.read(builder, readErrors::add);
+ assertThat(readErrors).contains(stream(expectedReadErrors).skip(halfCount).toArray(ReadError[]::new));
+
+ // calls third mocked result of each Reader mock (empty) => consumer is never called => empty list
+ readErrors.clear();
+ underTest.read(builder, readErrors::add);
+ assertThat(readErrors).isEmpty();
+ }
+
+ @Test
+ public void getLatestChangeWithRevision_delegates_to_ScmLineReader_if_non_null() {
+ ScmLineReader scmLineReader = mock(ScmLineReader.class);
+ Changeset changeset = Changeset.newChangesetBuilder().setDate(0L).build();
+ when(scmLineReader.getLatestChangeWithRevision()).thenReturn(changeset);
+
+ SourceLineReadersFactory.LineReaders lineReaders = new SourceLineReadersFactory.LineReadersImpl(Collections.emptyList(), scmLineReader, Collections.emptyList());
+ assertThat(lineReaders.getLatestChangeWithRevision()).isEqualTo(changeset);
+
+ verify(scmLineReader).getLatestChangeWithRevision();
+ verifyNoMoreInteractions(scmLineReader);
+ }
+
+ @Test
+ public void getLatestChangeWithRevision_returns_null_if_ScmLineReader_is_null() {
+ SourceLineReadersFactory.LineReaders lineReaders = new SourceLineReadersFactory.LineReadersImpl(Collections.emptyList(), null, Collections.emptyList());
+ assertThat(lineReaders.getLatestChangeWithRevision()).isNull();
+ }
+
+ @SuppressWarnings("unchecked")
+ private static CloseableIterator<Object> newCloseableIteratorMock() {
+ return (CloseableIterator<Object>)mock(CloseableIterator.class);
+ }
+
+}
*/
package org.sonar.ce.task.projectanalysis.source;
-import java.util.Arrays;
-import java.util.Collections;
-import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.ce.task.projectanalysis.batch.BatchReportReaderRule;
import org.sonar.ce.task.projectanalysis.component.ReportComponent;
import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
import org.sonar.ce.task.projectanalysis.duplication.DuplicationRepositoryRule;
-import org.sonar.ce.task.projectanalysis.scm.Changeset;
import org.sonar.ce.task.projectanalysis.scm.ScmInfoRepositoryRule;
-import org.sonar.ce.task.projectanalysis.source.linereader.LineReader;
-import org.sonar.ce.task.projectanalysis.source.linereader.ScmLineReader;
-import org.sonar.core.util.CloseableIterator;
-import org.sonar.db.protobuf.DbFileSources;
+import org.sonar.ce.task.projectanalysis.source.SourceLineReadersFactory.LineReadersImpl;
import org.sonar.scanner.protocol.output.ScannerReport;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
public class SourceLineReadersFactoryTest {
private static final int FILE1_REF = 3;
private static final String PROJECT_UUID = "PROJECT";
private static final String PROJECT_KEY = "PROJECT_KEY";
private static final String FILE1_UUID = "FILE1";
- private static final long NOW = 123456789L;
@Rule
public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
public ScmInfoRepositoryRule scmInfoRepository = new ScmInfoRepositoryRule();
@Rule
public DuplicationRepositoryRule duplicationRepository = DuplicationRepositoryRule.create(treeRootHolder);
- private NewLinesRepository newLinesRepository = mock(NewLinesRepository.class);
- private SourceLineReadersFactory underTest;
+ private NewLinesRepository newLinesRepository = mock(NewLinesRepository.class);
- @Before
- public void setUp() {
- underTest = new SourceLineReadersFactory(reportReader, scmInfoRepository, duplicationRepository, newLinesRepository);
- }
+ private SourceLineReadersFactory underTest = new SourceLineReadersFactory(reportReader, scmInfoRepository, duplicationRepository, newLinesRepository);
@Test
public void should_create_readers() {
initBasicReport(10);
- SourceLineReadersFactory.LineReaders lineReaders = underTest.getLineReaders(fileComponent());
+ LineReadersImpl lineReaders = (LineReadersImpl) underTest.getLineReaders(fileComponent());
assertThat(lineReaders).isNotNull();
assertThat(lineReaders.closeables).hasSize(3);
assertThat(lineReaders.readers).hasSize(5);
}
- @Test
- public void line_readers_should_close_all_closeables() {
- LineReader r1 = mock(LineReader.class);
- LineReader r2 = mock(LineReader.class);
- CloseableIterator c1 = mock(CloseableIterator.class);
- CloseableIterator c2 = mock(CloseableIterator.class);
-
- SourceLineReadersFactory.LineReaders lineReaders = new SourceLineReadersFactory.LineReaders(Arrays.asList(r1, r2), null, Arrays.asList(c1, c2));
- lineReaders.close();
-
- verify(c1).close();
- verify(c2).close();
- verifyNoMoreInteractions(c1, c2);
- verifyZeroInteractions(r1, r2);
- }
-
- @Test
- public void line_readers_should_call_all_readers() {
- LineReader r1 = mock(LineReader.class);
- LineReader r2 = mock(LineReader.class);
- CloseableIterator c1 = mock(CloseableIterator.class);
- CloseableIterator c2 = mock(CloseableIterator.class);
-
- SourceLineReadersFactory.LineReaders lineReaders = new SourceLineReadersFactory.LineReaders(Arrays.asList(r1, r2), null, Arrays.asList(c1, c2));
- DbFileSources.Line.Builder builder = DbFileSources.Line.newBuilder();
- lineReaders.read(builder);
-
- verify(r1).read(builder);
- verify(r2).read(builder);
- verifyNoMoreInteractions(r1, r2);
- verifyZeroInteractions(c1, c2);
- }
-
- @Test
- public void should_delegate_latest_changeset() {
- ScmLineReader scmLineReader = mock(ScmLineReader.class);
- Changeset changeset = Changeset.newChangesetBuilder().setDate(0L).build();
- when(scmLineReader.getLatestChangeWithRevision()).thenReturn(changeset);
- SourceLineReadersFactory.LineReaders lineReaders = new SourceLineReadersFactory.LineReaders(Collections.emptyList(), scmLineReader, Collections.emptyList());
- assertThat(lineReaders.getLatestChangeWithRevision()).isEqualTo(changeset);
- }
-
- @Test
- public void should_not_delegate_latest_changeset() {
- SourceLineReadersFactory.LineReaders lineReaders = new SourceLineReadersFactory.LineReaders(Collections.emptyList(), null, Collections.emptyList());
- assertThat(lineReaders.getLatestChangeWithRevision()).isNull();
- }
-
private Component fileComponent() {
return ReportComponent.builder(Component.Type.FILE, FILE1_REF).build();
}
.setType(ScannerReport.Component.ComponentType.FILE)
.setLines(numberOfLines)
.build());
-
- // for (int i = 1; i <= numberOfLines; i++) {
- // fileSourceRepository.addLine(FILE1_REF, "line" + i);
- // }
}
}
.build()).iterator());
DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1);
- computeCoverageLine.read(lineBuilder);
+ assertThat(computeCoverageLine.read(lineBuilder)).isEmpty();
assertThat(lineBuilder.getLineHits()).isEqualTo(1);
assertThat(lineBuilder.getConditions()).isEqualTo(10);
.build()).iterator());
DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1);
- computeCoverageLine.read(lineBuilder);
+ assertThat(computeCoverageLine.read(lineBuilder)).isEmpty();
assertThat(lineBuilder.hasLineHits()).isFalse();
assertThat(lineBuilder.getConditions()).isEqualTo(10);
.build()).iterator());
DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1);
- computeCoverageLine.read(lineBuilder);
+ assertThat(computeCoverageLine.read(lineBuilder)).isEmpty();
assertThat(lineBuilder.hasLineHits()).isTrue();
assertThat(lineBuilder.getLineHits()).isEqualTo(0);
CoverageLineReader computeCoverageLine = new CoverageLineReader(Collections.<ScannerReport.LineCoverage>emptyList().iterator());
DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1);
- computeCoverageLine.read(lineBuilder);
+ assertThat(computeCoverageLine.read(lineBuilder)).isEmpty();
assertThat(lineBuilder.hasLineHits()).isFalse();
assertThat(lineBuilder.hasConditions()).isFalse();
.build()).iterator());
DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1);
- computeCoverageLine.read(lineBuilder);
+ assertThat(computeCoverageLine.read(lineBuilder)).isEmpty();
assertThat(lineBuilder.hasDeprecatedUtLineHits()).isFalse();
assertThat(lineBuilder.hasDeprecatedUtConditions()).isFalse();
public void read_nothing() {
DuplicationLineReader reader = new DuplicationLineReader(Collections.emptySet());
- reader.read(line1);
+ assertThat(reader.read(line1)).isEmpty();
assertThat(line1.getDuplicationList()).isEmpty();
}
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(reader.read(line1)).isEmpty();
+ assertThat(reader.read(line2)).isEmpty();
+ assertThat(reader.read(line3)).isEmpty();
+ assertThat(reader.read(line4)).isEmpty();
assertThat(line1.getDuplicationList()).containsExactly(1);
assertThat(line2.getDuplicationList()).containsExactly(1);
1, 2,
new InProjectDuplicate(fileComponent(1).build(), new TextBlock(3, 4))));
- reader.read(line1);
- reader.read(line2);
- reader.read(line3);
- reader.read(line4);
+ assertThat(reader.read(line1)).isEmpty();
+ assertThat(reader.read(line2)).isEmpty();
+ assertThat(reader.read(line3)).isEmpty();
+ assertThat(reader.read(line4)).isEmpty();
assertThat(line1.getDuplicationList()).containsExactly(1);
assertThat(line2.getDuplicationList()).containsExactly(1);
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(reader.read(line1)).isEmpty();
+ assertThat(reader.read(line2)).isEmpty();
+ assertThat(reader.read(line3)).isEmpty();
+ assertThat(reader.read(line4)).isEmpty();
assertThat(line1.getDuplicationList()).containsExactly(1);
assertThat(line2.getDuplicationList()).containsExactly(1);
1, 2,
innerDuplicate(3, 4)));
- reader.read(line1);
- reader.read(line2);
- reader.read(line3);
- reader.read(line4);
+ assertThat(reader.read(line1)).isEmpty();
+ assertThat(reader.read(line2)).isEmpty();
+ assertThat(reader.read(line3)).isEmpty();
+ assertThat(reader.read(line4)).isEmpty();
assertThat(line1.getDuplicationList()).containsExactly(1, 2);
assertThat(line2.getDuplicationList()).containsExactly(2, 3);
1, 1,
innerDuplicate(3, 3)));
- reader.read(line1);
- reader.read(line2);
- reader.read(line3);
- reader.read(line4);
+ assertThat(reader.read(line1)).isEmpty();
+ assertThat(reader.read(line2)).isEmpty();
+ assertThat(reader.read(line3)).isEmpty();
+ assertThat(reader.read(line4)).isEmpty();
assertThat(line1.getDuplicationList()).containsExactly(1);
assertThat(line2.getDuplicationList()).containsExactly(2);
1, 1,
innerDuplicate(4, 4)));
- reader.read(line1);
- reader.read(line2);
- reader.read(line3);
- reader.read(line4);
+ assertThat(reader.read(line1)).isEmpty();
+ assertThat(reader.read(line2)).isEmpty();
+ assertThat(reader.read(line3)).isEmpty();
+ assertThat(reader.read(line4)).isEmpty();
assertThat(line1.getDuplicationList()).containsExactly(1, 2);
assertThat(line2.getDuplicationList()).containsExactly(2);
import static org.mockito.Mockito.when;
import static org.sonar.api.utils.log.LoggerLevel.WARN;
import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
+import static org.sonar.ce.task.projectanalysis.source.linereader.LineReader.Data.HIGHLIGHTING;
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;
HighlightingLineReader highlightingLineReader = newReader(Collections.emptyMap());
DbFileSources.Line.Builder lineBuilder = newBuilder().addLinesBuilder().setLine(1);
- highlightingLineReader.read(lineBuilder);
+ assertThat(highlightingLineReader.read(lineBuilder)).isEmpty();
assertThat(lineBuilder.hasHighlighting()).isFalse();
}
HighlightingLineReader highlightingLineReader = newReader(of(
newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_1), ANNOTATION));
- highlightingLineReader.read(line1);
+ assertThat(highlightingLineReader.read(line1)).isEmpty();
assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a");
}
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(highlightingLineReader.read(line1)).isEmpty();
+ assertThat(highlightingLineReader.read(line2)).isEmpty();
+ assertThat(highlightingLineReader.read(line3)).isEmpty();
+ assertThat(highlightingLineReader.read(line4)).isEmpty();
assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a");
assertThat(line2.getHighlighting()).isEqualTo(RANGE_LABEL_2 + ",cd");
private DbFileSources.Line.Builder addSourceLine(HighlightingLineReader highlightingLineReader, int line, String source) {
DbFileSources.Line.Builder lineBuilder = sourceData.addLinesBuilder().setSource(source).setLine(line);
- highlightingLineReader.read(lineBuilder);
+ assertThat(highlightingLineReader.read(lineBuilder)).isEmpty();
return lineBuilder;
}
newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_1), ANNOTATION,
newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_2), COMMENT));
- highlightingLineReader.read(line1);
+ assertThat(highlightingLineReader.read(line1)).isEmpty();
assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a;" + RANGE_LABEL_2 + ",cd");
}
HighlightingLineReader highlightingLineReader = newReader(of(textRange, ANNOTATION));
- highlightingLineReader.read(line1);
+ assertThat(highlightingLineReader.read(line1)).isEmpty();
DbFileSources.Line.Builder line2 = sourceData.addLinesBuilder().setSource("line 2").setLine(2);
- highlightingLineReader.read(line2);
- highlightingLineReader.read(line3);
+ assertThat(highlightingLineReader.read(line2)).isEmpty();
+ assertThat(highlightingLineReader.read(line3)).isEmpty();
assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a");
assertThat(line2.getHighlighting()).isEqualTo(RANGE_LABEL_2 + ",a");
textRange2, HIGHLIGHTING_STRING,
textRange3, COMMENT));
- highlightingLineReader.read(line1);
- highlightingLineReader.read(line2);
- highlightingLineReader.read(line3);
- highlightingLineReader.read(line4);
+ assertThat(highlightingLineReader.read(line1)).isEmpty();
+ assertThat(highlightingLineReader.read(line2)).isEmpty();
+ assertThat(highlightingLineReader.read(line3)).isEmpty();
+ assertThat(highlightingLineReader.read(line4)).isEmpty();
assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a");
assertThat(line2.getHighlighting()).isEqualTo(RANGE_LABEL_2 + ",a;" + RANGE_LABEL_2 + ",s;" + RANGE_LABEL_5 + ",cd");
HighlightingLineReader highlightingLineReader = newReader(of(textRange, ANNOTATION));
- highlightingLineReader.read(line1);
- highlightingLineReader.read(line2);
- highlightingLineReader.read(line3);
+ assertThat(highlightingLineReader.read(line1)).isEmpty();
+ assertThat(highlightingLineReader.read(line2)).isEmpty();
+ assertThat(highlightingLineReader.read(line3)).isEmpty();
assertThat(line1.getHighlighting()).isEqualTo(RANGE_LABEL_1 + ",a");
// Nothing should be set on line 2
textRange1, HighlightingType.ANNOTATION,
newSingleLineTextRangeWithExpectingLabel(LINE_2, RANGE_LABEL_1), HIGHLIGHTING_STRING));
- highlightingLineReader.read(line1);
- highlightingLineReader.read(line2);
+ LineReader.ReadError readErrorLine1 = new LineReader.ReadError(HIGHLIGHTING, LINE_1);
+ assertThat(highlightingLineReader.read(line1)).contains(readErrorLine1);
+ assertThat(highlightingLineReader.read(line2)).contains(readErrorLine1);
assertNoHighlighting();
assertThat(logTester.logs(WARN)).isNotEmpty();
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);
+ TextRange textRange3 = newTextRange(LINE_3, LINE_3);
HighlightingLineReader highlightingLineReader = newReader(of(
newSingleLineTextRangeWithExpectingLabel(LINE_1, RANGE_LABEL_1), ANNOTATION,
- textRange2, HIGHLIGHTING_STRING));
+ textRange2, HIGHLIGHTING_STRING,
+ textRange3, COMMENT));
- highlightingLineReader.read(line1);
- highlightingLineReader.read(line2);
+ assertThat(highlightingLineReader.read(line1)).isEmpty();
+ LineReader.ReadError readErrorLine2 = new LineReader.ReadError(HIGHLIGHTING, LINE_2);
+ assertThat(highlightingLineReader.read(line2)).contains(readErrorLine2);
+ assertThat(highlightingLineReader.read(line3)).contains(readErrorLine2);
assertThat(line1.hasHighlighting()).isTrue();
assertThat(line2.hasHighlighting()).isFalse();
+ assertThat(line3.hasHighlighting()).isFalse();
assertThat(logTester.logs(WARN)).isNotEmpty();
}
doThrow(RangeOffsetConverterException.class).when(rangeOffsetConverter).offsetToString(textRange1, LINE_1, DEFAULT_LINE_LENGTH);
HighlightingLineReader highlightingLineReader = newReader(of(textRange1, ANNOTATION));
- highlightingLineReader.read(line1);
+ assertThat(highlightingLineReader.read(line1))
+ .contains(new LineReader.ReadError(HIGHLIGHTING, 1));
assertThat(logTester.logs(WARN)).containsOnly("Inconsistency detected in Highlighting data. Highlighting will be ignored for file 'FILE_KEY'");
}
ScmLineReader lineScm = new ScmLineReader(scmInfo);
DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1);
- lineScm.read(lineBuilder);
+ assertThat(lineScm.read(lineBuilder)).isEmpty();
assertThat(lineBuilder.getScmAuthor()).isEqualTo("john");
assertThat(lineBuilder.getScmDate()).isEqualTo(123_456_789L);
ScmLineReader lineScm = new ScmLineReader(scmInfo);
DbFileSources.Line.Builder lineBuilder = DbFileSources.Data.newBuilder().addLinesBuilder().setLine(1);
- lineScm.read(lineBuilder);
+ assertThat(lineScm.read(lineBuilder)).isEmpty();
assertThat(lineBuilder.hasScmAuthor()).isFalse();
assertThat(lineBuilder.getScmDate()).isEqualTo(123456789L);
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.read(lineBuilder)).isEmpty();
assertThat(lineScm.getLatestChange()).isSameAs(expectedChangeset);
assertThat(lineScm.getLatestChangeWithRevision()).isSameAs(expectedChangesetWithRevision);
import static org.mockito.Mockito.when;
import static org.sonar.api.utils.log.LoggerLevel.WARN;
import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
+import static org.sonar.ce.task.projectanalysis.source.linereader.LineReader.Data.SYMBOLS;
public class SymbolsLineReaderTest {
public void read_nothing() {
SymbolsLineReader symbolsLineReader = newReader();
- symbolsLineReader.read(line1);
+ assertThat(symbolsLineReader.read(line1)).isEmpty();
assertThat(line1.getSymbols()).isEmpty();
}
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(symbolsLineReader.read(line1)).isEmpty();
+ assertThat(symbolsLineReader.read(line2)).isEmpty();
+ assertThat(symbolsLineReader.read(line3)).isEmpty();
assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1");
assertThat(line2.getSymbols()).isEmpty();
newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_0, OFFSET_1, RANGE_LABEL_1),
newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_2, OFFSET_3, RANGE_LABEL_2)));
- symbolsLineReader.read(line1);
+ assertThat(symbolsLineReader.read(line1)).isEmpty();
assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1;" + RANGE_LABEL_2 + ",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(symbolsLineReader.read(line1)).isEmpty();
+ assertThat(symbolsLineReader.read(line2)).isEmpty();
+ assertThat(symbolsLineReader.read(line3)).isEmpty();
assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1");
assertThat(line2.getSymbols()).isEqualTo(RANGE_LABEL_3 + ",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(symbolsLineReader.read(line1)).isEmpty();
+ assertThat(symbolsLineReader.read(line2)).isEmpty();
assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1");
assertThat(line2.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1;" + RANGE_LABEL_3 + ",1");
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(symbolsLineReader.read(line1)).isEmpty();
+ assertThat(symbolsLineReader.read(line2)).isEmpty();
assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1");
assertThat(line2.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1");
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(symbolsLineReader.read(line1)).isEmpty();
+ assertThat(symbolsLineReader.read(line2)).isEmpty();
+ assertThat(symbolsLineReader.read(line3)).isEmpty();
assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1;" + RANGE_LABEL_3 + ",2");
assertThat(line2.getSymbols()).isEmpty();
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(symbolsLineReader.read(line1)).isEmpty();
+ assertThat(symbolsLineReader.read(line2)).isEmpty();
+ assertThat(symbolsLineReader.read(line3)).isEmpty();
assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1;" + RANGE_LABEL_1 + ",2");
assertThat(line2.getSymbols()).isEmpty();
newSingleLineTextRangeWithExpectedLabel(LINE_1, OFFSET_0, OFFSET_1, RANGE_LABEL_1),
newSingleLineTextRangeWithExpectedLabel(LINE_3, OFFSET_0, OFFSET_1, RANGE_LABEL_1)));
- symbolsLineReader.read(line1);
+ assertThat(symbolsLineReader.read(line1)).isEmpty();
symbolsLineReader.read(line2);
symbolsLineReader.read(line3);
SymbolsLineReader symbolsLineReader = newReader(newSymbol(declaration, reference));
- symbolsLineReader.read(line1);
- symbolsLineReader.read(line2);
- symbolsLineReader.read(line3);
- symbolsLineReader.read(line4);
+ assertThat(symbolsLineReader.read(line1)).isEmpty();
+ assertThat(symbolsLineReader.read(line2)).isEmpty();
+ assertThat(symbolsLineReader.read(line3)).isEmpty();
+ assertThat(symbolsLineReader.read(line4)).isEmpty();
assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1");
assertThat(line2.getSymbols()).isEqualTo(RANGE_LABEL_2 + ",1");
SymbolsLineReader symbolsLineReader = newReader(newSymbol(declaration, reference));
- symbolsLineReader.read(line1);
- symbolsLineReader.read(line2);
- symbolsLineReader.read(line3);
- symbolsLineReader.read(line4);
+ assertThat(symbolsLineReader.read(line1)).isEmpty();
+ assertThat(symbolsLineReader.read(line2)).isEmpty();
+ assertThat(symbolsLineReader.read(line3)).isEmpty();
+ assertThat(symbolsLineReader.read(line4)).isEmpty();
assertThat(line1.getSymbols()).isEqualTo(RANGE_LABEL_1 + ",1");
assertThat(line2.getSymbols()).isEmpty();
SymbolsLineReader symbolsLineReader = newReader(newSymbol(declaration, reference));
- symbolsLineReader.read(line1);
- symbolsLineReader.read(line2);
+ LineReader.ReadError readErrorLine1 = new LineReader.ReadError(SYMBOLS, LINE_1);
+ assertThat(symbolsLineReader.read(line1)).contains(readErrorLine1);
+ assertThat(symbolsLineReader.read(line2)).contains(readErrorLine1);
assertNoSymbol();
assertThat(logTester.logs(WARN)).isNotEmpty();
SymbolsLineReader symbolsLineReader = newReader(newSymbol(declaration, reference));
- symbolsLineReader.read(line1);
- symbolsLineReader.read(line2);
+ assertThat(symbolsLineReader.read(line1)).isEmpty();
+ assertThat(symbolsLineReader.read(line2)).contains(new LineReader.ReadError(SYMBOLS, LINE_2));
assertThat(line1.hasSymbols()).isTrue();
assertThat(line2.hasSymbols()).isFalse();
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(symbolsLineReader.read(line1))
+ .contains(new LineReader.ReadError(SYMBOLS, LINE_1));
assertThat(logTester.logs(WARN)).containsOnly("Inconsistency detected in Symbols data. Symbols will be ignored for file 'FILE_KEY'");
}
import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
import org.sonar.ce.task.projectanalysis.scm.Changeset;
import org.sonar.ce.task.projectanalysis.source.FileSourceDataComputer;
+import org.sonar.ce.task.projectanalysis.source.FileSourceDataWarnings;
import org.sonar.ce.task.projectanalysis.source.SourceLinesHashRepository;
import org.sonar.ce.task.projectanalysis.source.SourceLinesHashRepositoryImpl;
+import org.sonar.ce.task.projectanalysis.source.linereader.LineReader;
import org.sonar.ce.task.step.ComputationStep;
import org.sonar.ce.task.step.TestComputationStepContext;
import org.sonar.db.DbClient;
import org.sonar.db.source.FileSourceDto;
import org.sonar.db.source.FileSourceDto.Type;
import org.sonar.db.source.LineHashVersion;
+import org.sonar.scanner.protocol.output.ScannerReport;
+import static com.google.common.collect.Lists.newArrayList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.sonar.ce.task.projectanalysis.source.linereader.LineReader.Data.HIGHLIGHTING;
+import static org.sonar.ce.task.projectanalysis.source.linereader.LineReader.Data.SYMBOLS;
public class PersistFileSourcesStepTest extends BaseStepTest {
private SourceLinesHashRepository sourceLinesHashRepository = mock(SourceLinesHashRepository.class);
private SourceLinesHashRepositoryImpl.LineHashesComputer lineHashesComputer = mock(SourceLinesHashRepositoryImpl.LineHashesComputer.class);
-
private FileSourceDataComputer fileSourceDataComputer = mock(FileSourceDataComputer.class);
+ private FileSourceDataWarnings fileSourceDataWarnings = mock(FileSourceDataWarnings.class);
+
private DbClient dbClient = dbTester.getDbClient();
private DbSession session = dbTester.getSession();
public void setup() {
when(system2.now()).thenReturn(NOW);
when(sourceLinesHashRepository.getLineHashesComputerToPersist(Mockito.any(Component.class))).thenReturn(lineHashesComputer);
- underTest = new PersistFileSourcesStep(dbClient, system2, treeRootHolder, sourceLinesHashRepository, fileSourceDataComputer);
+ underTest = new PersistFileSourcesStep(dbClient, system2, treeRootHolder, sourceLinesHashRepository, fileSourceDataComputer, fileSourceDataWarnings);
initBasicReport(1);
}
DbFileSources.Line.newBuilder().setSource("line2").setLine(2).build()
))
.build();
- when(fileSourceDataComputer.compute(fileComponent())).thenReturn(new FileSourceDataComputer.Data(fileSourceData, lineHashes, sourceHash, null));
+ when(fileSourceDataComputer.compute(fileComponent(), fileSourceDataWarnings)).thenReturn(new FileSourceDataComputer.Data(fileSourceData, lineHashes, sourceHash, null));
underTest.execute(new TestComputationStepContext());
assertThat(data.getLines(0).getSource()).isEqualTo("line1");
assertThat(data.getLines(1).getLine()).isEqualTo(2);
assertThat(data.getLines(1).getSource()).isEqualTo("line2");
+ verify(fileSourceDataWarnings).commitWarnings();
}
@Test
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSourceByFileUuid(session, FILE1_UUID);
assertThat(fileSourceDto.getLineHashes()).containsExactly("137f72c3708c6bd0de00a0e5a69c699b", "e6251bcf1a7dc3ba5e7933e325bbe605");
assertThat(fileSourceDto.getSrcHash()).isEqualTo("ee5a58024a155466b43bc559d953e018");
+ verify(fileSourceDataWarnings).commitWarnings();
}
@Test
assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1);
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSourceByFileUuid(session, FILE1_UUID);
assertThat(fileSourceDto.getSourceData()).isEqualTo(dbData);
+ verify(fileSourceDataWarnings).commitWarnings();
}
private Component fileComponent() {
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSourceByFileUuid(session, FILE1_UUID);
assertThat(fileSourceDto.getSourceData()).isEqualTo(dbData);
assertThat(fileSourceDto.getRevision()).isNull();
+ verify(fileSourceDataWarnings).commitWarnings();
}
@Test
assertThat(data.getLines(2).getScmAuthor()).isEmpty();
assertThat(data.getLines(2).getScmDate()).isEqualTo(0);
assertThat(data.getLines(2).getScmRevision()).isEmpty();
+ verify(fileSourceDataWarnings).commitWarnings();
}
@Test
assertThat(data).isEqualTo(dbData);
assertThat(data.getLinesList()).hasSize(1);
assertThat(data.getLines(0).getHighlighting()).isEqualTo("2,4,a");
+ verify(fileSourceDataWarnings).commitWarnings();
}
@Test
assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1);
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSourceByFileUuid(session, FILE1_UUID);
assertThat(fileSourceDto.getSourceData()).isEqualTo(dbData);
+ verify(fileSourceDataWarnings).commitWarnings();
}
@Test
assertThat(dbTester.countRowsOfTable("file_sources")).isEqualTo(1);
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSourceByFileUuid(session, FILE1_UUID);
assertThat(fileSourceDto.getSourceData()).isEqualTo(dbData);
+ verify(fileSourceDataWarnings).commitWarnings();
}
@Test
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSourceByFileUuid(session, FILE1_UUID);
assertThat(fileSourceDto.getRevision()).isEqualTo("rev-1");
+ verify(fileSourceDataWarnings).commitWarnings();
}
@Test
FileSourceDto fileSourceDto = dbClient.fileSourceDao().selectSourceByFileUuid(session, FILE1_UUID);
assertThat(fileSourceDto.getRevision()).isNull();
+ verify(fileSourceDataWarnings).commitWarnings();
}
@Test
assertThat(fileSourceDto.getLineHashes()).isEqualTo(Collections.singletonList("lineHash"));
assertThat(fileSourceDto.getCreatedAt()).isEqualTo(PAST);
assertThat(fileSourceDto.getUpdatedAt()).isEqualTo(PAST);
+ verify(fileSourceDataWarnings).commitWarnings();
}
@Test
assertThat(fileSourceDto.getCreatedAt()).isEqualTo(past);
assertThat(fileSourceDto.getUpdatedAt()).isEqualTo(NOW);
assertThat(fileSourceDto.getRevision()).isEqualTo("rev-1");
+ verify(fileSourceDataWarnings).commitWarnings();
}
@Test
assertThat(fileSourceDto.getCreatedAt()).isEqualTo(PAST);
assertThat(fileSourceDto.getUpdatedAt()).isEqualTo(NOW);
assertThat(fileSourceDto.getSrcHash()).isEqualTo("newSourceHash");
+ verify(fileSourceDataWarnings).commitWarnings();
}
@Test
assertThat(fileSourceDto.getCreatedAt()).isEqualTo(PAST);
assertThat(fileSourceDto.getUpdatedAt()).isEqualTo(NOW);
assertThat(fileSourceDto.getRevision()).isEqualTo("revision");
+ verify(fileSourceDataWarnings).commitWarnings();
}
private FileSourceDto createDto() {
private void setComputedData(DbFileSources.Data data, List<String> lineHashes, String sourceHash, Changeset latestChangeWithRevision) {
FileSourceDataComputer.Data computedData = new FileSourceDataComputer.Data(data, lineHashes, sourceHash, latestChangeWithRevision);
- when(fileSourceDataComputer.compute(fileComponent())).thenReturn(computedData);
+ when(fileSourceDataComputer.compute(fileComponent(), fileSourceDataWarnings)).thenReturn(computedData);
}
private void setComputedData(DbFileSources.Data data) {
FileSourceDataComputer.Data computedData = new FileSourceDataComputer.Data(data, Collections.emptyList(), "", null);
- when(fileSourceDataComputer.compute(fileComponent())).thenReturn(computedData);
+ when(fileSourceDataComputer.compute(fileComponent(), fileSourceDataWarnings)).thenReturn(computedData);
}
private void initBasicReport(int numberOfLines) {
+++ /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.ce.task.step;
-
-import com.google.common.base.Function;
-import com.google.common.base.Predicate;
-import java.lang.reflect.Modifier;
-import java.util.Set;
-import javax.annotation.Nonnull;
-import org.reflections.Reflections;
-
-import static com.google.common.base.Predicates.notNull;
-import static com.google.common.collect.FluentIterable.from;
-
-public class StepsExplorer {
- /**
- * Compute set of canonical names of classes implementing ComputationStep in the specified package using reflection.
- */
- public static Set<String> retrieveStepPackageStepsCanonicalNames(String packageName) {
- Reflections reflections = new Reflections(packageName);
-
- return from(reflections.getSubTypesOf(ComputationStep.class))
- .filter(NotAbstractClass.INSTANCE)
- .transform(ClassToCanonicalName.INSTANCE)
- // anonymous classes do not have canonical names
- .filter(notNull())
- .toSet();
- }
-
- private enum NotAbstractClass implements Predicate<Class<? extends ComputationStep>> {
- INSTANCE;
-
- @Override
- public boolean apply(Class<? extends ComputationStep> input) {
- return !Modifier.isAbstract(input.getModifiers());
- }
- }
-
- public static Function<Class<?>, String> toCanonicalName() {
- return ClassToCanonicalName.INSTANCE;
- }
-
- private enum ClassToCanonicalName implements Function<Class<?>, String> {
- INSTANCE;
-
- @Override
- public String apply(@Nonnull Class<?> input) {
- return input.getCanonicalName();
- }
- }
-}