Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

workaround for 'forceZeroCoverage' #1007

Merged
merged 1 commit into from
Dec 21, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 60 additions & 60 deletions integration-tests/features/importing_coverage.feature
Original file line number Diff line number Diff line change
Expand Up @@ -36,71 +36,71 @@ Feature: Importing coverage data
| overall_branch_coverage | 50.0 |


# Scenario: Importing coverage reports zeroing coverage for untouched files
# GIVEN the project "coverage_project"
Scenario: Importing coverage reports zeroing coverage for untouched files
GIVEN the project "coverage_project"

# WHEN I run sonar-scanner with following options:
# """
# -Dsonar.cxx.coverage.reportPath=ut-coverage.xml
# -Dsonar.cxx.coverage.itReportPath=it-coverage.xml
# -Dsonar.cxx.coverage.overallReportPath=overall-coverage.xml
# -Dsonar.cxx.coverage.forceZeroCoverage=True
# """
WHEN I run sonar-scanner with following options:
"""
-Dsonar.cxx.coverage.reportPath=ut-coverage.xml
-Dsonar.cxx.coverage.itReportPath=it-coverage.xml
-Dsonar.cxx.coverage.overallReportPath=overall-coverage.xml
-Dsonar.cxx.coverage.forceZeroCoverage=True
"""

# THEN the analysis finishes successfully
# AND the analysis in server has completed
# AND the analysis log contains no error/warning messages except those matching:
# """
# .*WARN.*Unable to get a valid mac address, will use a dummy address
# .*WARN.*cannot find the sources for '#include <iostream>'
# """
# AND the following metrics have following values:
# | metric | value |
# | coverage | 8.5 |
# | line_coverage | 5.5 |
# | branch_coverage | 50 |
# | it_coverage | 20.3 |
# | it_line_coverage | 14.8 |
# | it_branch_coverage | 50 |
# | overall_coverage | 28.1 |
# | overall_line_coverage | 22 |
# | overall_branch_coverage | 50 |
THEN the analysis finishes successfully
AND the analysis in server has completed
AND the analysis log contains no error/warning messages except those matching:
"""
.*WARN.*Unable to get a valid mac address, will use a dummy address
.*WARN.*cannot find the sources for '#include <iostream>'
"""
AND the following metrics have following values:
| metric | value |
| coverage | 5.9 |
| line_coverage | 3.1 |
| branch_coverage | 50 |
| it_coverage | 14.1 |
| it_line_coverage | 8.2 |
| it_branch_coverage | 50 |
| overall_coverage | 20.0 |
| overall_line_coverage | 12.5 |
| overall_branch_coverage | 50 |


# Scenario: Zeroing coverage measures without importing reports
Scenario: Zeroing coverage measures without importing reports

# If we don't pass coverage reports *and* request zeroing untouched
# files at the same time, all coverage measures, except the branch
# ones, should be 'zero'. The branch coverage measures remain 'None',
# since its currently ignored by the 'force zero...'
# implementation
If we don't pass coverage reports *and* request zeroing untouched
files at the same time, all coverage measures, except the branch
ones, should be 'zero'. The branch coverage measures remain 'None',
since its currently ignored by the 'force zero...'
implementation

# GIVEN the project "coverage_project"
GIVEN the project "coverage_project"

# WHEN I run sonar-scanner with following options:
# """
# -Dsonar.cxx.coverage.reportPath=dummy.xml
# -Dsonar.cxx.coverage.itReportPath=dummy.xml
# -Dsonar.cxx.coverage.overallReportPath=dummy.xml
# -Dsonar.cxx.coverage.forceZeroCoverage=True
# """
WHEN I run sonar-scanner with following options:
"""
-Dsonar.cxx.coverage.reportPath=dummy.xml
-Dsonar.cxx.coverage.itReportPath=dummy.xml
-Dsonar.cxx.coverage.overallReportPath=dummy.xml
-Dsonar.cxx.coverage.forceZeroCoverage=True
"""

# THEN the analysis finishes successfully
# AND the analysis in server has completed
# AND the analysis log contains no error/warning messages except those matching:
# """
# .*WARN.*Unable to get a valid mac address, will use a dummy address
# .*WARN.*cannot find the sources for '#include <iostream>'
# .*WARN.*Cannot find a report for '.*'
# """
# AND the following metrics have following values:
# | metric | value |
# | coverage | 0 |
# | line_coverage | 0 |
# | branch_coverage | None |
# | it_coverage | 0 |
# | it_line_coverage | 0 |
# | it_branch_coverage | None |
# | overall_coverage | 0 |
# | overall_line_coverage | 0 |
# | overall_branch_coverage | None |
THEN the analysis finishes successfully
AND the analysis in server has completed
AND the analysis log contains no error/warning messages except those matching:
"""
.*WARN.*Unable to get a valid mac address, will use a dummy address
.*WARN.*cannot find the sources for '#include <iostream>'
.*WARN.*Cannot find a report for '.*'
"""
AND the following metrics have following values:
| metric | value |
| coverage | 0 |
| line_coverage | 0 |
| branch_coverage | None |
| it_coverage | 0 |
| it_line_coverage | 0 |
| it_branch_coverage | None |
| overall_coverage | 0 |
| overall_line_coverage | 0 |
| overall_branch_coverage | None |
Original file line number Diff line number Diff line change
Expand Up @@ -343,37 +343,46 @@ private static List<PropertyDefinition> testingAndCoverageProperties() {
.onQualifiers(Qualifiers.PROJECT, Qualifiers.MODULE)
.index(3)
.build(),
PropertyDefinition.builder(CxxCoverageSensor.FORCE_ZERO_COVERAGE_KEY)
.defaultValue("False")
.name("Enable Force Zero Coverage")
.description("Set files without coverage reports to zero coverage. Default is 'False'.")
.subCategory(subcateg)
.onQualifiers(Qualifiers.PROJECT, Qualifiers.MODULE)
.type(PropertyType.BOOLEAN)
.index(4)
.build(),
PropertyDefinition.builder(CxxXunitSensor.REPORT_PATH_KEY)
.name("Unit test execution report(s)")
.description("Path to unit test execution report(s), relative to projects root."
+ " See <a href='https://github.com/SonarOpenCommunity/sonar-cxx/wiki/Get-test-execution-metrics'>here</a> for supported formats."
+ " Use <a href='https://ant.apache.org/manual/dirtasks.html'>Ant-style wildcards</a> if neccessary.")
.subCategory(subcateg)
.onQualifiers(Qualifiers.PROJECT, Qualifiers.MODULE)
.index(5)
.index(6)
.build(),
PropertyDefinition.builder(CxxXunitSensor.XSLT_URL_KEY)
.name("XSLT transformer")
.description("By default, the unit test execution reports are expected to be in the JUnitReport format."
+ " To import a report in an other format, set this property to an URL to a XSLT stylesheet which is able to perform the according transformation.")
.subCategory(subcateg)
.onQualifiers(Qualifiers.PROJECT, Qualifiers.MODULE)
.index(6)
.index(7)
.build(),
PropertyDefinition.builder(CxxUnitTestResultsProvider.VISUAL_STUDIO_TEST_RESULTS_PROPERTY_KEY)
.name("Visual Studio Test Reports Paths")
.description("Example: \"report.trx\", \"report1.trx,report2.trx\" or \"C:/report.trx\"")
.subCategory(subcateg)
.onQualifiers(Qualifiers.PROJECT, Qualifiers.MODULE)
.index(8)
.index(9)
.build(),
PropertyDefinition.builder(CxxUnitTestResultsProvider.NUNIT_TEST_RESULTS_PROPERTY_KEY)
.name("Nunit Test Reports Paths")
.description("Example: \"nunit.xml\", \"nunit1.xml,nunit2.xml\" or \"C:/nunit.xml\"")
.subCategory(subcateg)
.onQualifiers(Qualifiers.PROJECT, Qualifiers.MODULE)
.index(9)
.build()
.index(10)
.build()
));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,12 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;

import javax.xml.stream.XMLStreamException;
import org.sonar.api.batch.fs.FilePredicates;
import org.sonar.api.batch.fs.FileSystem;

import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.api.batch.fs.InputFile;
Expand All @@ -37,7 +41,6 @@
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.plugins.cxx.CxxLanguage;
import org.sonar.plugins.cxx.CxxPlugin;
import org.sonar.plugins.cxx.utils.CxxReportSensor;
import org.sonar.plugins.cxx.utils.CxxUtils;

Expand All @@ -49,6 +52,7 @@ public class CxxCoverageSensor extends CxxReportSensor {
public static final String REPORT_PATH_KEY = "sonar.cxx.coverage.reportPath";
public static final String IT_REPORT_PATH_KEY = "sonar.cxx.coverage.itReportPath";
public static final String OVERALL_REPORT_PATH_KEY = "sonar.cxx.coverage.overallReportPath";
public static final String FORCE_ZERO_COVERAGE_KEY = "sonar.cxx.coverage.forceZeroCoverage";

private final List<CoverageParser> parsers = new LinkedList<>();
private final CxxCoverageCache cache;
Expand All @@ -72,8 +76,7 @@ public void describe(SensorDescriptor descriptor) {
/**
* {@inheritDoc}
*/
@Override
public void execute(SensorContext context) {
public void execute(SensorContext context, Map<InputFile, Set<Integer>> linesOfCode) {

Map<String, CoverageMeasures> coverageMeasures = null;
Map<String, CoverageMeasures> itCoverageMeasures = null;
Expand Down Expand Up @@ -101,6 +104,66 @@ public void execute(SensorContext context) {
overallCoverageMeasures = processReports(context, overallReports, this.cache.overallCoverageCache());
saveMeasures(context, overallCoverageMeasures, CoverageType.OVERALL);
}

if (settings.getBoolean(FORCE_ZERO_COVERAGE_KEY)) {
LOG.debug("Zeroing coverage information for untouched files");
zeroMeasuresWithoutReports(context, coverageMeasures, itCoverageMeasures, overallCoverageMeasures, linesOfCode);
}
}

private void zeroMeasuresWithoutReports(
SensorContext context,
Map<String, CoverageMeasures> coverageMeasures,
Map<String, CoverageMeasures> itCoverageMeasures,
Map<String, CoverageMeasures> overallCoverageMeasures,
Map<InputFile, Set<Integer>> linesOfCode
) {
FileSystem fileSystem = context.fileSystem();
FilePredicates p = fileSystem.predicates();
Iterable<InputFile> inputFiles = fileSystem.inputFiles(p.and(p.hasType(InputFile.Type.MAIN), p.hasLanguage(CxxLanguage.KEY)));

for (InputFile inputFile : inputFiles) {
Set<Integer> linesOfCodeForFile = linesOfCode.get(inputFile);
String file = CxxUtils.normalizePath(inputFile.absolutePath());

if (coverageMeasures != null && !coverageMeasures.containsKey(file)) {
saveZeroValueForResource(inputFile, context, CoverageType.UNIT, linesOfCodeForFile);
}

if (itCoverageMeasures != null && !itCoverageMeasures.containsKey(file)) {
saveZeroValueForResource(inputFile, context, CoverageType.IT, linesOfCodeForFile);
}

if (overallCoverageMeasures != null && !overallCoverageMeasures.containsKey(file)) {
saveZeroValueForResource(inputFile, context, CoverageType.OVERALL, linesOfCodeForFile);
}
}
}

private void saveZeroValueForResource(InputFile inputFile, SensorContext context, CoverageType ctype, @Nullable Set<Integer> linesOfCode) {
if (linesOfCode != null) {
LOG.debug("Zeroing {} coverage measures for file '{}'", ctype, inputFile.relativePath());

NewCoverage newCoverage = context.newCoverage()
.onFile(inputFile)
.ofType(ctype);

for (Integer line : linesOfCode) {
try {
newCoverage.lineHits(line, 0);
} catch (Exception ex) {
LOG.error("Cannot save Line Hits for Line '{}' '{}' : '{}', ignoring measure", inputFile.relativePath(), line, ex.getMessage());
CxxUtils.ValidateRecovery(ex, settings);
}
}

try {
newCoverage.save();
} catch (Exception ex) {
LOG.error("Cannot save measure '{}' : '{}', ignoring measure", inputFile.relativePath(), ex.getMessage());
CxxUtils.ValidateRecovery(ex, settings);
}
}
}

private Map<String, CoverageMeasures> processReports(final SensorContext context, List<File> reports, Map<String, Map<String, CoverageMeasures>> cacheCov) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Sonar C++ Plugin (Community)
* Copyright (C) 2010-2016 SonarOpenCommunity
* http://github.com/SonarOpenCommunity/sonar-cxx
*
* 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.plugins.cxx.metrics;

import com.google.common.collect.Sets;
import com.sonar.sslr.api.AstAndTokenVisitor;
import com.sonar.sslr.api.AstNode;
import com.sonar.sslr.api.GenericTokenType;
import com.sonar.sslr.api.Grammar;
import com.sonar.sslr.api.Token;
import com.sonar.sslr.api.Trivia;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.FileLinesContext;
import org.sonar.api.measures.FileLinesContextFactory;
import org.sonar.cxx.api.CxxMetric;
import org.sonar.squidbridge.SquidAstVisitor;

/**
* Visitor that computes {@link CoreMetrics#NCLOC_DATA_KEY} and
* {@link CoreMetrics#COMMENT_LINES_DATA_KEY} metrics used by the DevCockpit.
*/
public class FileLinesVisitor extends SquidAstVisitor<Grammar> implements AstAndTokenVisitor {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be called filesexecutedlinesvisitor?


private final FileLinesContextFactory fileLinesContextFactory;

private Set<Integer> linesOfCode = Sets.newHashSet();
private Set<Integer> linesOfComments = Sets.newHashSet();
private final FileSystem fileSystem;
private final Map<InputFile, Set<Integer>> allLinesOfCode;

public FileLinesVisitor(FileLinesContextFactory fileLinesContextFactory, FileSystem fileSystem, Map<InputFile, Set<Integer>> linesOfCode) {
this.fileLinesContextFactory = fileLinesContextFactory;
this.fileSystem = fileSystem;
this.allLinesOfCode = linesOfCode;
}

@Override
public void visitToken(Token token) {
if (token.getType().equals(GenericTokenType.EOF)) {
return;
}

linesOfCode.add(token.getLine());

List<Trivia> trivias = token.getTrivia();
for (Trivia trivia : trivias) {
if (trivia.isComment()) {
linesOfComments.add(trivia.getToken().getLine());
}
}
}

@Override
public void leaveFile(AstNode astNode) {
InputFile inputFile = fileSystem.inputFile(fileSystem.predicates().is(getContext().getFile()));
if (inputFile == null) {
throw new IllegalStateException("InputFile is null, but it should not be.");
}
FileLinesContext fileLinesContext = fileLinesContextFactory.createFor(inputFile);

int fileLength = getContext().peekSourceCode().getInt(CxxMetric.LINES);
for (int line = 1; line <= fileLength; line++) {
if (linesOfCode.contains(line)) {
fileLinesContext.setIntValue(CoreMetrics.NCLOC_DATA_KEY, line, 1);
}
if (linesOfComments.contains(line)) {
fileLinesContext.setIntValue(CoreMetrics.COMMENT_LINES_DATA_KEY, line, 1);
}
}
fileLinesContext.save();

allLinesOfCode.put(inputFile, linesOfCode);

linesOfCode = Sets.newHashSet();
linesOfComments = Sets.newHashSet();
}

}
Loading