/*
* SonarQube
* Copyright (C) 2009-2017 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.scanner.mediumtest;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.CheckForNull;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.AnalysisMode;
import org.sonar.api.batch.fs.InputComponent;
import org.sonar.api.batch.fs.InputDir;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.TextPointer;
import org.sonar.api.batch.fs.TextRange;
import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.sensor.highlighting.TypeOfText;
import org.sonar.core.util.CloseableIterator;
import org.sonar.scanner.issue.IssueCache;
import org.sonar.scanner.issue.tracking.TrackedIssue;
import org.sonar.scanner.protocol.output.ScannerReport;
import org.sonar.scanner.protocol.output.ScannerReport.Component;
import org.sonar.scanner.protocol.output.ScannerReport.Metadata;
import org.sonar.scanner.protocol.output.ScannerReport.Symbol;
import org.sonar.scanner.report.ReportPublisher;
import org.sonar.scanner.report.ScannerReportUtils;
import org.sonar.scanner.scan.ProjectScanContainer;
import org.sonar.scanner.scan.filesystem.InputComponentStore;
import org.sonar.scanner.protocol.output.ScannerReportReader;
import static org.apache.commons.lang.StringUtils.isNotEmpty;
public class TaskResult implements org.sonar.scanner.mediumtest.ScanTaskObserver {
private static final Logger LOG = LoggerFactory.getLogger(TaskResult.class);
private List<TrackedIssue> issues = new ArrayList<>();
private Map<String, InputFile> inputFiles = new HashMap<>();
private Map<String, Component> reportComponents = new HashMap<>();
private Map<String, InputDir> inputDirs = new HashMap<>();
private ScannerReportReader reader;
@Override
public void scanTaskCompleted(ProjectScanContainer container) {
LOG.info("Store analysis results in memory for later assertions in medium test");
for (TrackedIssue issue : container.getComponentByType(IssueCache.class).all()) {
issues.add(issue);
}
ReportPublisher reportPublisher = container.getComponentByType(ReportPublisher.class);
reader = new ScannerReportReader(reportPublisher.getReportDir());
if (!container.getComponentByType(AnalysisMode.class).isIssues()) {
Metadata readMetadata = getReportReader().readMetadata();
int rootComponentRef = readMetadata.getRootComponentRef();
storeReportComponents(rootComponentRef, null, readMetadata.getBranch());
}
storeFs(container);
}
private void storeReportComponents(int componentRef, String parentModuleKey, String branch) {
Component component = getReportReader().readComponent(componentRef);
if (isNotEmpty(component.getKey())) {
reportComponents.put(component.getKey(), component);
} else {
reportComponents.put(parentModuleKey + ":" + component.getPath(), component);
}
for (int childId : component.getChildRefList()) {
storeReportComponents(childId, isNotEmpty(component.getKey()) ? component.getKey() : parentModuleKey, branch);
}
}
public ScannerReportReader getReportReader() {
return reader;
}
private void storeFs(ProjectScanContainer container) {
InputComponentStore inputFileCache = container.getComponentByType(InputComponentStore.class);
for (InputFile inputPath : inputFileCache.allFiles()) {
inputFiles.put(inputPath.relativePath(), inputPath);
}
for (InputDir inputPath : inputFileCache.allDirs()) {
inputDirs.put(inputPath.relativePath(), inputPath);
}
}
public List<TrackedIssue> trackedIssues() {
return issues;
}
public Component getReportComponent(String key) {
return reportComponents.get(key);
}
public List<ScannerReport.Issue> issuesFor(InputComponent inputComponent) {
int ref = reportComponents.get(inputComponent.key()).getRef();
return issuesFor(ref);
}
public List<ScannerReport.Issue> issuesFor(Component reportComponent) {
int ref = reportComponent.getRef();
return issuesFor(ref);
}
private List<ScannerReport.Issue> issuesFor(int ref) {
List<ScannerReport.Issue> result = Lists.newArrayList();
try (CloseableIterator<ScannerReport.Issue> it = reader.readComponentIssues(ref)) {
while (it.hasNext()) {
result.add(it.next());
}
}
return result;
}
public Collection<InputFile> inputFiles() {
return inputFiles.values();
}
@CheckForNull
public InputFile inputFile(String relativePath) {
return inputFiles.get(relativePath);
}
public Collection<InputDir> inputDirs() {
return inputDirs.values();
}
@CheckForNull
public InputDir inputDir(String relativePath) {
return inputDirs.get(relativePath);
}
public Map<String, List<ScannerReport.Measure>> allMeasures() {
Map<String, List<ScannerReport.Measure>> result = new HashMap<>();
for (Map.Entry<String, Component> component : reportComponents.entrySet()) {
List<ScannerReport.Measure> measures = new ArrayList<>();
try (CloseableIterator<ScannerReport.Measure> it = reader.readComponentMeasures(component.getValue().getRef())) {
Iterators.addAll(measures, it);
}
result.put(component.getKey(), measures);
}
return result;
}
/**
* Get highlighting types at a given position in an inputfile
* @param lineOffset 0-based offset in file
*/
public List<TypeOfText> highlightingTypeFor(InputFile file, int line, int lineOffset) {
int ref = reportComponents.get(((DefaultInputFile) file).key()).getRef();
if (!reader.hasSyntaxHighlighting(ref)) {
return Collections.emptyList();
}
TextPointer pointer = file.newPointer(line, lineOffset);
List<TypeOfText> result = new ArrayList<>();
try (CloseableIterator<ScannerReport.SyntaxHighlightingRule> it = reader.readComponentSyntaxHighlighting(ref)) {
while (it.hasNext()) {
ScannerReport.SyntaxHighlightingRule rule = it.next();
TextRange ruleRange = toRange(file, rule.getRange());
if (ruleRange.start().compareTo(pointer) <= 0 && ruleRange.end().compareTo(pointer) > 0) {
result.add(ScannerReportUtils.toBatchType(rule.getType()));
}
}
} catch (Exception e) {
throw new IllegalStateException("Can't read syntax highlighting for " + file.absolutePath(), e);
}
return result;
}
private static TextRange toRange(InputFile file, ScannerReport.TextRange reportRange) {
return file.newRange(file.newPointer(reportRange.getStartLine(), reportRange.getStartOffset()), file.newPointer(reportRange.getEndLine(), reportRange.getEndOffset()));
}
/**
* Get list of all start positions of a symbol in an inputfile
* @param symbolStartLine 0-based start offset for the symbol in file
* @param symbolStartLineOffset 0-based end offset for the symbol in file
*/
@CheckForNull
public List<ScannerReport.TextRange> symbolReferencesFor(InputFile file, int symbolStartLine, int symbolStartLineOffset) {
int ref = reportComponents.get(((DefaultInputFile) file).key()).getRef();
try (CloseableIterator<Symbol> symbols = getReportReader().readComponentSymbols(ref)) {
while (symbols.hasNext()) {
Symbol symbol = symbols.next();
if (symbol.getDeclaration().getStartLine() == symbolStartLine && symbol.getDeclaration().getStartOffset() == symbolStartLineOffset) {
return symbol.getReferenceList();
}
}
}
return Collections.emptyList();
}
public List<ScannerReport.Duplication> duplicationsFor(InputFile file) {
List<ScannerReport.Duplication> result = new ArrayList<>();
int ref = reportComponents.get(((DefaultInputFile) file).key()).getRef();
try (CloseableIterator<ScannerReport.Duplication> it = getReportReader().readComponentDuplications(ref)) {
while (it.hasNext()) {
result.add(it.next());
}
} catch (Exception e) {
throw new IllegalStateException(e);
}
return result;
}
public List<ScannerReport.CpdTextBlock> duplicationBlocksFor(InputFile file) {
List<ScannerReport.CpdTextBlock> result = new ArrayList<>();
int ref = reportComponents.get(((DefaultInputFile) file).key()).getRef();
try (CloseableIterator<ScannerReport.CpdTextBlock> it = getReportReader().readCpdTextBlocks(ref)) {
while (it.hasNext()) {
result.add(it.next());
}
} catch (Exception e) {
throw new IllegalStateException(e);
}
return result;
}
@CheckForNull
public ScannerReport.LineCoverage coverageFor(InputFile file, int line) {
int ref = reportComponents.get(((DefaultInputFile) file).key()).getRef();
try (CloseableIterator<ScannerReport.LineCoverage> it = getReportReader().readComponentCoverage(ref)) {
while (it.hasNext()) {
ScannerReport.LineCoverage coverage = it.next();
if (coverage.getLine() == line) {
return coverage;
}
}
} catch (Exception e) {
throw new IllegalStateException(e);
}
return null;
}
public ScannerReport.Test firstTestExecutionForName(InputFile testFile, String testName) {
int ref = reportComponents.get(((DefaultInputFile) testFile).key()).getRef();
try (InputStream inputStream = FileUtils.openInputStream(getReportReader().readTests(ref))) {
ScannerReport.Test test = ScannerReport.Test.parser().parseDelimitedFrom(inputStream);
while (test != null) {
if (test.getName().equals(testName)) {
return test;
}
test = ScannerReport.Test.parser().parseDelimitedFrom(inputStream);
}
} catch (Exception e) {
throw new IllegalStateException(e);
}
return null;
}
public ScannerReport.CoverageDetail coveragePerTestFor(InputFile testFile, String testName) {
int ref = reportComponents.get(((DefaultInputFile) testFile).key()).getRef();
try (InputStream inputStream = FileUtils.openInputStream(getReportReader().readCoverageDetails(ref))) {
ScannerReport.CoverageDetail details = ScannerReport.CoverageDetail.parser().parseDelimitedFrom(inputStream);
while (details != null) {
if (details.getTestName().equals(testName)) {
return details;
}
details = ScannerReport.CoverageDetail.parser().parseDelimitedFrom(inputStream);
}
} catch (Exception e) {
throw new IllegalStateException(e);
}
return null;
}
}