/* * SonarQube * Copyright (C) 2009-2019 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.fs.InputComponent; 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.DefaultInputComponent; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.sensor.highlighting.TypeOfText; import org.sonar.api.scanner.fs.InputProject; 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.Symbol; import org.sonar.scanner.protocol.output.ScannerReportReader; 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; public class AnalysisResult implements AnalysisObserver { private static final Logger LOG = LoggerFactory.getLogger(AnalysisResult.class); private List issues = new ArrayList<>(); private Map inputFilesByKeys = new HashMap<>(); private InputProject project; private ScannerReportReader reader; @Override public void analysisCompleted(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().toFile()); project = container.getComponentByType(InputProject.class); storeFs(container); } public ScannerReportReader getReportReader() { return reader; } private void storeFs(ProjectScanContainer container) { InputComponentStore inputFileCache = container.getComponentByType(InputComponentStore.class); for (InputFile inputPath : inputFileCache.allFiles()) { inputFilesByKeys.put(((DefaultInputFile) inputPath).getProjectRelativePath(), inputPath); } } public List trackedIssues() { return issues; } public Component getReportComponent(InputComponent inputComponent) { return getReportReader().readComponent(((DefaultInputComponent) inputComponent).scannerId()); } public Component getReportComponent(int scannerId) { return getReportReader().readComponent(scannerId); } public List issuesFor(InputComponent inputComponent) { return issuesFor(((DefaultInputComponent) inputComponent).scannerId()); } public List externalIssuesFor(InputComponent inputComponent) { return externalIssuesFor(((DefaultInputComponent) inputComponent).scannerId()); } public List issuesFor(Component reportComponent) { int ref = reportComponent.getRef(); return issuesFor(ref); } private List issuesFor(int ref) { List result = Lists.newArrayList(); try (CloseableIterator it = reader.readComponentIssues(ref)) { while (it.hasNext()) { result.add(it.next()); } } return result; } private List externalIssuesFor(int ref) { List result = Lists.newArrayList(); try (CloseableIterator it = reader.readComponentExternalIssues(ref)) { while (it.hasNext()) { result.add(it.next()); } } return result; } public InputProject project() { return project; } public Collection inputFiles() { return inputFilesByKeys.values(); } @CheckForNull public InputFile inputFile(String relativePath) { return inputFilesByKeys.get(relativePath); } public Map> allMeasures() { Map> result = new HashMap<>(); List projectMeasures = new ArrayList<>(); try (CloseableIterator it = reader.readComponentMeasures(((DefaultInputComponent) project).scannerId())) { Iterators.addAll(projectMeasures, it); } result.put(project.key(), projectMeasures); for (InputFile inputFile : inputFilesByKeys.values()) { List measures = new ArrayList<>(); try (CloseableIterator it = reader.readComponentMeasures(((DefaultInputComponent) inputFile).scannerId())) { Iterators.addAll(measures, it); } result.put(inputFile.key(), measures); } return result; } /** * Get highlighting types at a given position in an inputfile * * @param lineOffset 0-based offset in file */ public List highlightingTypeFor(InputFile file, int line, int lineOffset) { int ref = ((DefaultInputComponent) file).scannerId(); if (!reader.hasSyntaxHighlighting(ref)) { return Collections.emptyList(); } TextPointer pointer = file.newPointer(line, lineOffset); List result = new ArrayList<>(); try (CloseableIterator 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, 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 symbolReferencesFor(InputFile file, int symbolStartLine, int symbolStartLineOffset) { int ref = ((DefaultInputComponent) file).scannerId(); try (CloseableIterator 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 duplicationsFor(InputFile file) { List result = new ArrayList<>(); int ref = ((DefaultInputComponent) file).scannerId(); try (CloseableIterator it = getReportReader().readComponentDuplications(ref)) { while (it.hasNext()) { result.add(it.next()); } } catch (Exception e) { throw new IllegalStateException(e); } return result; } public List duplicationBlocksFor(InputFile file) { List result = new ArrayList<>(); int ref = ((DefaultInputComponent) file).scannerId(); try (CloseableIterator 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 = ((DefaultInputComponent) file).scannerId(); try (CloseableIterator 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 = ((DefaultInputComponent) testFile).scannerId(); 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 = ((DefaultInputComponent) testFile).scannerId(); 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; } public List adHocRules() { List result = new ArrayList<>(); try (CloseableIterator it = getReportReader().readAdHocRules()) { while (it.hasNext()) { result.add(it.next()); } } catch (Exception e) { throw new IllegalStateException(e); } return result; } }