diff options
25 files changed, 214 insertions, 145 deletions
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java index 4312c634c04..3fe157a43e2 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java @@ -54,7 +54,8 @@ public class DefaultInputFile extends DefaultInputComponent implements InputFile private Status status; private Charset charset; private Metadata metadata; - private boolean publish; + private boolean published; + private boolean excludedForCoverage; public DefaultInputFile(DefaultIndexedFile indexedFile, Consumer<DefaultInputFile> metadataGenerator) { this(indexedFile, metadataGenerator, null); @@ -66,7 +67,8 @@ public class DefaultInputFile extends DefaultInputComponent implements InputFile this.indexedFile = indexedFile; this.metadataGenerator = metadataGenerator; this.metadata = null; - this.publish = false; + this.published = false; + this.excludedForCoverage = false; this.contents = contents; } @@ -100,19 +102,22 @@ public class DefaultInputFile extends DefaultInputComponent implements InputFile } } - /** - * @since 6.3 - */ - public DefaultInputFile setPublish(boolean publish) { - this.publish = publish; + public DefaultInputFile setPublished(boolean published) { + this.published = published; return this; } - /** - * @since 6.3 - */ - public boolean publish() { - return publish; + public boolean isPublished() { + return published; + } + + public DefaultInputFile setExcludedForCoverage(boolean excludedForCoverage) { + this.excludedForCoverage = excludedForCoverage; + return this; + } + + public boolean isExcludedForCoverage() { + return excludedForCoverage; } @Override diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TestInputFileBuilder.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TestInputFileBuilder.java index c06e1ec4c30..df2bc17d5fa 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TestInputFileBuilder.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/TestInputFileBuilder.java @@ -198,7 +198,7 @@ public class TestInputFileBuilder { contents); inputFile.setStatus(status); inputFile.setCharset(charset); - inputFile.setPublish(publish); + inputFile.setPublished(publish); return inputFile; } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/coverage/internal/DefaultCoverage.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/coverage/internal/DefaultCoverage.java index c99f2ab7de6..04625256a65 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/coverage/internal/DefaultCoverage.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/coverage/internal/DefaultCoverage.java @@ -24,6 +24,7 @@ import java.util.SortedMap; import java.util.TreeMap; import javax.annotation.Nullable; import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.sensor.coverage.CoverageType; import org.sonar.api.batch.sensor.coverage.NewCoverage; import org.sonar.api.batch.sensor.internal.DefaultStorable; @@ -74,6 +75,9 @@ public class DefaultCoverage extends DefaultStorable implements NewCoverage { @Override public NewCoverage lineHits(int line, int hits) { validateFile(); + if (isExcluded()) { + return this; + } validateLine(line); if (!hitsByLine.containsKey(line)) { @@ -97,6 +101,9 @@ public class DefaultCoverage extends DefaultStorable implements NewCoverage { @Override public NewCoverage conditions(int line, int conditions, int coveredConditions) { validateFile(); + if (isExcluded()) { + return this; + } validateLine(line); if (conditions > 0 && !conditionsByLine.containsKey(line)) { @@ -139,7 +146,13 @@ public class DefaultCoverage extends DefaultStorable implements NewCoverage { @Override public void doSave() { validateFile(); - storage.store(this); + if (!isExcluded()) { + storage.store(this); + } + } + + private boolean isExcluded() { + return ((DefaultInputFile) inputFile).isExcludedForCoverage(); } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java index edfa3645112..7e9dc824d16 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java @@ -352,6 +352,6 @@ public class SensorContextTester implements SensorContext { @Override public void markForPublishing(InputFile inputFile) { DefaultInputFile file = (DefaultInputFile) inputFile; - file.setPublish(true); + file.setPublished(true); } } diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/TestInputFileBuilderTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/TestInputFileBuilderTest.java index b6dc6a38d65..ac1c3a0805a 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/TestInputFileBuilderTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/fs/internal/TestInputFileBuilderTest.java @@ -55,7 +55,7 @@ public class TestInputFileBuilderTest { assertThat(file.type()).isEqualTo(Type.MAIN); assertThat(file.status()).isEqualTo(Status.SAME); - assertThat(file.publish()).isTrue(); + assertThat(file.isPublished()).isTrue(); assertThat(file.type()).isEqualTo(Type.MAIN); assertThat(file.relativePath()).isEqualTo("path"); assertThat(file.absolutePath()).isEqualTo(new File("baseDir", "path").toString()); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/test/TestPlanBuilder.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/test/TestPlanBuilder.java index 3f13c7d8958..f4ca4ff12c8 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/test/TestPlanBuilder.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/deprecated/test/TestPlanBuilder.java @@ -43,7 +43,7 @@ public class TestPlanBuilder extends PerspectiveBuilder<MutableTestPlan> { if (component.isFile()) { DefaultInputFile inputFile = (DefaultInputFile) component; if (inputFile.type() == Type.TEST) { - inputFile.setPublish(true); + inputFile.setPublished(true); if (!testPlanByFile.containsKey(inputFile)) { testPlanByFile.put(inputFile, new DefaultTestPlan()); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/AbstractPhaseExecutor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/AbstractPhaseExecutor.java index c759111ae38..94e992c64d8 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/AbstractPhaseExecutor.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/AbstractPhaseExecutor.java @@ -21,8 +21,11 @@ package org.sonar.scanner.phases; import org.sonar.api.batch.SensorContext; import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.batch.fs.internal.InputModuleHierarchy; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; import org.sonar.scanner.events.BatchStepEvent; import org.sonar.scanner.events.EventBus; import org.sonar.scanner.issue.ignore.scanner.IssueExclusionsLoader; @@ -32,6 +35,8 @@ import org.sonar.scanner.scan.filesystem.FileIndexer; public abstract class AbstractPhaseExecutor { + private static final Logger LOG = Loggers.get(AbstractPhaseExecutor.class); + private final EventBus eventBus; private final PostJobsExecutor postJobsExecutor; private final InitializersExecutor initializersExecutor; @@ -42,10 +47,11 @@ public abstract class AbstractPhaseExecutor { private final IssueExclusionsLoader issueExclusionsLoader; private final InputModuleHierarchy hierarchy; private final FileIndexer fileIndexer; + private final CoverageExclusions coverageExclusions; public AbstractPhaseExecutor(InitializersExecutor initializersExecutor, PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor, SensorContext sensorContext, InputModuleHierarchy hierarchy, EventBus eventBus, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier, - IssueExclusionsLoader issueExclusionsLoader, FileIndexer fileIndexer) { + IssueExclusionsLoader issueExclusionsLoader, FileIndexer fileIndexer, CoverageExclusions coverageExclusions) { this.postJobsExecutor = postJobsExecutor; this.initializersExecutor = initializersExecutor; this.sensorsExecutor = sensorsExecutor; @@ -56,6 +62,7 @@ public abstract class AbstractPhaseExecutor { this.issueExclusionsLoader = issueExclusionsLoader; this.hierarchy = hierarchy; this.fileIndexer = fileIndexer; + this.coverageExclusions = coverageExclusions; } /** @@ -66,7 +73,7 @@ public abstract class AbstractPhaseExecutor { executeInitializersPhase(); - // Index and lock the filesystem + // Index the filesystem indexFs(); // Log detected languages and their profiles after FS is indexed and languages detected @@ -75,6 +82,9 @@ public abstract class AbstractPhaseExecutor { // Initialize issue exclusions initIssueExclusions(); + // Initialize coverage exclusions + initCoverageExclusions(); + sensorsExecutor.execute(sensorContext); afterSensors(); @@ -86,6 +96,25 @@ public abstract class AbstractPhaseExecutor { eventBus.fireEvent(new ProjectAnalysisEvent(module, false)); } + private void initCoverageExclusions() { + if (coverageExclusions.shouldExecute()) { + String stepName = "Init coverage exclusions"; + eventBus.fireEvent(new BatchStepEvent(stepName, true)); + coverageExclusions.log(); + + for (InputFile inputFile : fs.inputFiles(fs.predicates().all())) { + boolean excluded = coverageExclusions.isExcluded(inputFile); + if (excluded) { + ((DefaultInputFile) inputFile).setExcludedForCoverage(true); + LOG.debug("File {} excluded for coverage", inputFile.relativePath()); + } + } + + eventBus.fireEvent(new BatchStepEvent(stepName, false)); + } + + } + protected void afterSensors() { } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/coverage/CoverageExclusions.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/CoverageExclusions.java index 5af99184414..97675a88960 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/coverage/CoverageExclusions.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/CoverageExclusions.java @@ -17,14 +17,13 @@ * 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.sensor.coverage; +package org.sonar.scanner.phases; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableList.Builder; import java.util.Collection; import java.util.Iterator; - import javax.annotation.concurrent.Immutable; - -import org.picocontainer.Startable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.CoreProperties; @@ -32,11 +31,8 @@ import org.sonar.api.batch.fs.InputFile; import org.sonar.api.config.Configuration; import org.sonar.api.utils.WildcardPattern; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableList.Builder; - @Immutable -public class CoverageExclusions implements Startable { +public class CoverageExclusions { private static final Logger LOG = LoggerFactory.getLogger(CoverageExclusions.class); private Collection<WildcardPattern> exclusionPatterns; @@ -49,17 +45,11 @@ public class CoverageExclusions implements Startable { exclusionPatterns = builder.build(); } - @Override - public void start() { + void log() { log("Excluded sources for coverage: ", exclusionPatterns); } - @Override - public void stop() { - // Nothing to do - } - - public boolean isExcluded(InputFile file) { + boolean isExcluded(InputFile file) { boolean found = false; Iterator<WildcardPattern> iterator = exclusionPatterns.iterator(); while (!found && iterator.hasNext()) { @@ -76,4 +66,8 @@ public class CoverageExclusions implements Startable { } } } + + public boolean shouldExecute() { + return !exclusionPatterns.isEmpty(); + } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/IssuesPhaseExecutor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/IssuesPhaseExecutor.java index 50ef68ab33f..4f40b8b7930 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/IssuesPhaseExecutor.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/IssuesPhaseExecutor.java @@ -42,8 +42,10 @@ public final class IssuesPhaseExecutor extends AbstractPhaseExecutor { public IssuesPhaseExecutor(InitializersExecutor initializersExecutor, PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor, SensorContext sensorContext, EventBus eventBus, IssuesReports jsonReport, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier, - IssueExclusionsLoader issueExclusionsLoader, IssueTransition localIssueTracking, InputModuleHierarchy moduleHierarchy, FileIndexer fileIndexer) { - super(initializersExecutor, postJobsExecutor, sensorsExecutor, sensorContext, moduleHierarchy, eventBus, fs, profileVerifier, issueExclusionsLoader, fileIndexer); + IssueExclusionsLoader issueExclusionsLoader, IssueTransition localIssueTracking, InputModuleHierarchy moduleHierarchy, FileIndexer fileIndexer, + CoverageExclusions coverageExclusions) { + super(initializersExecutor, postJobsExecutor, sensorsExecutor, sensorContext, moduleHierarchy, eventBus, fs, profileVerifier, issueExclusionsLoader, fileIndexer, + coverageExclusions); this.eventBus = eventBus; this.issuesReport = jsonReport; this.localIssueTracking = localIssueTracking; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PublishPhaseExecutor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PublishPhaseExecutor.java index e0c22ad936e..5deb5629bca 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PublishPhaseExecutor.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/phases/PublishPhaseExecutor.java @@ -40,8 +40,9 @@ public final class PublishPhaseExecutor extends AbstractPhaseExecutor { public PublishPhaseExecutor(InitializersExecutor initializersExecutor, PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor, SensorContext sensorContext, EventBus eventBus, ReportPublisher reportPublisher, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier, - IssueExclusionsLoader issueExclusionsLoader, CpdExecutor cpdExecutor, ScmPublisher scm, InputModuleHierarchy hierarchy, FileIndexer fileIndexer) { - super(initializersExecutor, postJobsExecutor, sensorsExecutor, sensorContext, hierarchy, eventBus, fs, profileVerifier, issueExclusionsLoader, fileIndexer); + IssueExclusionsLoader issueExclusionsLoader, CpdExecutor cpdExecutor, ScmPublisher scm, InputModuleHierarchy hierarchy, FileIndexer fileIndexer, + CoverageExclusions coverageExclusions) { + super(initializersExecutor, postJobsExecutor, sensorsExecutor, sensorContext, hierarchy, eventBus, fs, profileVerifier, issueExclusionsLoader, fileIndexer, coverageExclusions); this.eventBus = eventBus; this.reportPublisher = reportPublisher; this.cpdExecutor = cpdExecutor; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java index 395b6e4b63b..f12ce7f7186 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java @@ -140,7 +140,7 @@ public class ComponentsPublisher implements ReportPublisherStep { } else if (component instanceof DefaultInputFile) { // skip files not marked for publishing DefaultInputFile inputFile = (DefaultInputFile) component; - return !inputFile.publish(); + return !inputFile.isPublished(); } return false; } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleScanContainer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleScanContainer.java index d43666bf93f..b949f802e61 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleScanContainer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ModuleScanContainer.java @@ -47,6 +47,7 @@ import org.sonar.scanner.issue.ignore.pattern.IssueInclusionPatternInitializer; import org.sonar.scanner.issue.ignore.pattern.PatternMatcher; import org.sonar.scanner.issue.ignore.scanner.IssueExclusionsLoader; import org.sonar.scanner.phases.AbstractPhaseExecutor; +import org.sonar.scanner.phases.CoverageExclusions; import org.sonar.scanner.phases.InitializersExecutor; import org.sonar.scanner.phases.IssuesPhaseExecutor; import org.sonar.scanner.phases.PostJobsExecutor; @@ -70,7 +71,6 @@ import org.sonar.scanner.scan.report.IssuesReports; import org.sonar.scanner.sensor.DefaultSensorStorage; import org.sonar.scanner.sensor.SensorOptimizer; import org.sonar.scanner.sensor.SensorStrategy; -import org.sonar.scanner.sensor.coverage.CoverageExclusions; import org.sonar.scanner.source.HighlightableBuilder; import org.sonar.scanner.source.SymbolizableBuilder; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputComponentStore.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputComponentStore.java index ffc0b2f7a0b..c43c8b05025 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputComponentStore.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputComponentStore.java @@ -78,7 +78,7 @@ public class InputComponentStore { public Iterable<DefaultInputFile> allFilesToPublish() { return inputFileCache.values().stream() .map(f -> (DefaultInputFile) f) - .filter(DefaultInputFile::publish)::iterator; + .filter(DefaultInputFile::isPublished)::iterator; } public Iterable<InputFile> allFiles() { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputFileBuilder.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputFileBuilder.java index 7913cb711f9..1248407e2b1 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputFileBuilder.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputFileBuilder.java @@ -68,7 +68,7 @@ public class InputFileBuilder { DefaultIndexedFile indexedFile = new DefaultIndexedFile(moduleKey, moduleBaseDir, relativePath, type, language, idGenerator.get()); DefaultInputFile inputFile = new DefaultInputFile(indexedFile, f -> metadataGenerator.setMetadata(f, moduleFileSystemInitializer.defaultEncoding())); if (language != null) { - inputFile.setPublish(true); + inputFile.setPublished(true); } return inputFile; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmPublisher.java index 28d8a400b61..edfb8ce56a6 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmPublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmPublisher.java @@ -95,7 +95,7 @@ public final class ScmPublisher { List<InputFile> filesToBlame = new LinkedList<>(); for (InputFile f : componentStore.inputFiles()) { DefaultInputFile inputFile = (DefaultInputFile) f; - if (!inputFile.publish()) { + if (!inputFile.isPublished()) { continue; } if (configuration.forceReloadAll() || f.status() != Status.SAME) { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java index 1889bbaab94..af4b631149b 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java @@ -175,6 +175,6 @@ public class DefaultSensorContext implements SensorContext { @Override public void markForPublishing(InputFile inputFile) { DefaultInputFile file = (DefaultInputFile) inputFile; - file.setPublish(true); + file.setPublished(true); } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java index c6fc08c8ce4..fe00b8e2913 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java @@ -19,6 +19,51 @@ */ package org.sonar.scanner.sensor; +import com.google.common.annotations.VisibleForTesting; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.sonar.api.batch.fs.InputComponent; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.TextRange; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.measure.Metric; +import org.sonar.api.batch.measure.MetricFinder; +import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage; +import org.sonar.api.batch.sensor.cpd.internal.DefaultCpdTokens; +import org.sonar.api.batch.sensor.error.AnalysisError; +import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting; +import org.sonar.api.batch.sensor.internal.SensorStorage; +import org.sonar.api.batch.sensor.issue.Issue; +import org.sonar.api.batch.sensor.measure.Measure; +import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; +import org.sonar.api.batch.sensor.symbol.internal.DefaultSymbolTable; +import org.sonar.api.config.Configuration; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.utils.KeyValueFormat; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.core.metric.ScannerMetrics; +import org.sonar.duplications.block.Block; +import org.sonar.duplications.internal.pmd.PmdBlockChunker; +import org.sonar.scanner.cpd.deprecated.DefaultCpdBlockIndexer; +import org.sonar.scanner.cpd.index.SonarCpdBlockIndex; +import org.sonar.scanner.issue.ModuleIssues; +import org.sonar.scanner.protocol.output.FileStructure; +import org.sonar.scanner.protocol.output.ScannerReport; +import org.sonar.scanner.protocol.output.ScannerReportWriter; +import org.sonar.scanner.report.ReportPublisher; +import org.sonar.scanner.report.ScannerReportUtils; +import org.sonar.scanner.repository.ContextPropertiesCache; +import org.sonar.scanner.scan.measure.MeasureCache; + import static java.util.stream.Collectors.toList; import static org.sonar.api.measures.CoreMetrics.BRANCH_COVERAGE; import static org.sonar.api.measures.CoreMetrics.COMMENTED_OUT_CODE_LINES_KEY; @@ -66,54 +111,6 @@ import static org.sonar.api.measures.CoreMetrics.TEST_SUCCESS_DENSITY_KEY; import static org.sonar.api.measures.CoreMetrics.UNCOVERED_CONDITIONS; import static org.sonar.api.measures.CoreMetrics.UNCOVERED_LINES; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.sonar.api.batch.fs.InputComponent; -import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.fs.TextRange; -import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.api.batch.measure.Metric; -import org.sonar.api.batch.measure.MetricFinder; -import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage; -import org.sonar.api.batch.sensor.cpd.internal.DefaultCpdTokens; -import org.sonar.api.batch.sensor.error.AnalysisError; -import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting; -import org.sonar.api.batch.sensor.internal.SensorStorage; -import org.sonar.api.batch.sensor.issue.Issue; -import org.sonar.api.batch.sensor.measure.Measure; -import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; -import org.sonar.api.batch.sensor.symbol.internal.DefaultSymbolTable; -import org.sonar.api.config.Configuration; -import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.utils.KeyValueFormat; -import org.sonar.api.utils.log.Logger; -import org.sonar.api.utils.log.Loggers; -import org.sonar.core.metric.ScannerMetrics; -import org.sonar.duplications.block.Block; -import org.sonar.duplications.internal.pmd.PmdBlockChunker; -import org.sonar.scanner.cpd.deprecated.DefaultCpdBlockIndexer; -import org.sonar.scanner.cpd.index.SonarCpdBlockIndex; -import org.sonar.scanner.issue.ModuleIssues; -import org.sonar.scanner.protocol.output.FileStructure; -import org.sonar.scanner.protocol.output.ScannerReport; -import org.sonar.scanner.protocol.output.ScannerReportWriter; -import org.sonar.scanner.report.ReportPublisher; -import org.sonar.scanner.report.ScannerReportUtils; -import org.sonar.scanner.repository.ContextPropertiesCache; -import org.sonar.scanner.scan.measure.MeasureCache; -import org.sonar.scanner.sensor.coverage.CoverageExclusions; - -import com.google.common.annotations.VisibleForTesting; - public class DefaultSensorStorage implements SensorStorage { private static final Logger LOG = Loggers.get(DefaultSensorStorage.class); @@ -143,7 +140,6 @@ public class DefaultSensorStorage implements SensorStorage { private final MetricFinder metricFinder; private final ModuleIssues moduleIssues; - private final CoverageExclusions coverageExclusions; private final ReportPublisher reportPublisher; private final MeasureCache measureCache; private final SonarCpdBlockIndex index; @@ -155,13 +151,12 @@ public class DefaultSensorStorage implements SensorStorage { private final Set<Metric<?>> byLineMetrics = new HashSet<>(); private final Set<String> alreadyLogged = new HashSet<>(); - public DefaultSensorStorage(MetricFinder metricFinder, ModuleIssues moduleIssues, Configuration settings, CoverageExclusions coverageExclusions, + public DefaultSensorStorage(MetricFinder metricFinder, ModuleIssues moduleIssues, Configuration settings, ReportPublisher reportPublisher, MeasureCache measureCache, SonarCpdBlockIndex index, ContextPropertiesCache contextPropertiesCache, ScannerMetrics scannerMetrics) { this.metricFinder = metricFinder; this.moduleIssues = moduleIssues; this.settings = settings; - this.coverageExclusions = coverageExclusions; this.reportPublisher = reportPublisher; this.measureCache = measureCache; this.index = index; @@ -205,7 +200,7 @@ public class DefaultSensorStorage implements SensorStorage { @Override public void store(Measure newMeasure) { if (newMeasure.inputComponent() instanceof DefaultInputFile) { - ((DefaultInputFile) newMeasure.inputComponent()).setPublish(true); + ((DefaultInputFile) newMeasure.inputComponent()).setPublished(true); } saveMeasure(newMeasure.inputComponent(), (DefaultMeasure<?>) newMeasure); } @@ -221,7 +216,7 @@ public class DefaultSensorStorage implements SensorStorage { public void saveMeasure(InputComponent component, DefaultMeasure<?> measure) { if (component.isFile()) { - ((DefaultInputFile) component).setPublish(true); + ((DefaultInputFile) component).setPublished(true); } if (isDeprecatedMetric(measure.metric().key())) { @@ -256,7 +251,7 @@ public class DefaultSensorStorage implements SensorStorage { if (!component.isFile()) { throw new UnsupportedOperationException("Saving coverage measure is only allowed on files. Attempt to save '" + metric.key() + "' on '" + component.key() + "'"); } - if (coverageExclusions.isExcluded((InputFile) component)) { + if (((DefaultInputFile) component).isExcludedForCoverage()) { return; } saveCoverageMetricInternal((InputFile) component, metric, measureToSave); @@ -357,7 +352,7 @@ public class DefaultSensorStorage implements SensorStorage { @Override public void store(Issue issue) { if (issue.primaryLocation().inputComponent() instanceof DefaultInputFile) { - ((DefaultInputFile) issue.primaryLocation().inputComponent()).setPublish(true); + ((DefaultInputFile) issue.primaryLocation().inputComponent()).setPublished(true); } moduleIssues.initAndAddIssue(issue); } @@ -366,7 +361,7 @@ public class DefaultSensorStorage implements SensorStorage { public void store(DefaultHighlighting highlighting) { ScannerReportWriter writer = reportPublisher.getWriter(); DefaultInputFile inputFile = (DefaultInputFile) highlighting.inputFile(); - inputFile.setPublish(true); + inputFile.setPublished(true); int componentRef = inputFile.batchId(); if (writer.hasComponentData(FileStructure.Domain.SYNTAX_HIGHLIGHTINGS, componentRef)) { throw new UnsupportedOperationException("Trying to save highlighting twice for the same file is not supported: " + inputFile.absolutePath()); @@ -391,7 +386,7 @@ public class DefaultSensorStorage implements SensorStorage { public void store(DefaultSymbolTable symbolTable) { ScannerReportWriter writer = reportPublisher.getWriter(); DefaultInputFile inputFile = (DefaultInputFile) symbolTable.inputFile(); - inputFile.setPublish(true); + inputFile.setPublished(true); int componentRef = inputFile.batchId(); if (writer.hasComponentData(FileStructure.Domain.SYMBOLS, componentRef)) { throw new UnsupportedOperationException("Trying to save symbol table twice for the same file is not supported: " + symbolTable.inputFile().absolutePath()); @@ -423,10 +418,7 @@ public class DefaultSensorStorage implements SensorStorage { @Override public void store(DefaultCoverage defaultCoverage) { DefaultInputFile inputFile = (DefaultInputFile) defaultCoverage.inputFile(); - inputFile.setPublish(true); - if (coverageExclusions.isExcluded(inputFile)) { - return; - } + inputFile.setPublished(true); if (defaultCoverage.linesToCover() > 0) { saveCoverageMetricInternal(inputFile, LINES_TO_COVER, new DefaultMeasure<Integer>().forMetric(LINES_TO_COVER).withValue(defaultCoverage.linesToCover())); saveCoverageMetricInternal(inputFile, UNCOVERED_LINES, @@ -449,7 +441,7 @@ public class DefaultSensorStorage implements SensorStorage { @Override public void store(DefaultCpdTokens defaultCpdTokens) { DefaultInputFile inputFile = (DefaultInputFile) defaultCpdTokens.inputFile(); - inputFile.setPublish(true); + inputFile.setPublished(true); PmdBlockChunker blockChunker = new PmdBlockChunker(getBlockSize(inputFile.language())); List<Block> blocks = blockChunker.chunk(inputFile.key(), defaultCpdTokens.getTokenLines()); index.insert(inputFile, blocks); @@ -462,7 +454,7 @@ public class DefaultSensorStorage implements SensorStorage { @Override public void store(AnalysisError analysisError) { - ((DefaultInputFile) analysisError.inputFile()).setPublish(true); + ((DefaultInputFile) analysisError.inputFile()).setPublished(true); // no op } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/coverage/package-info.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/coverage/package-info.java deleted file mode 100644 index ffff81bed9b..00000000000 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/coverage/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * 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. - */ -@javax.annotation.ParametersAreNonnullByDefault -package org.sonar.scanner.sensor.coverage; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/source/ZeroCoverageSensor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/source/ZeroCoverageSensor.java index 47f10cabe8f..5732f0bacae 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/source/ZeroCoverageSensor.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/source/ZeroCoverageSensor.java @@ -28,6 +28,7 @@ import org.sonar.api.batch.Phase; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputFile.Type; +import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.measure.Metric; import org.sonar.api.batch.sensor.Sensor; import org.sonar.api.batch.sensor.SensorContext; @@ -39,7 +40,6 @@ import org.sonar.api.measures.CoreMetrics; import org.sonar.api.utils.KeyValueFormat; import org.sonar.core.util.stream.MoreCollectors; import org.sonar.scanner.scan.measure.MeasureCache; -import org.sonar.scanner.sensor.coverage.CoverageExclusions; @Phase(name = Phase.Name.POST) public final class ZeroCoverageSensor implements Sensor { @@ -59,11 +59,9 @@ public final class ZeroCoverageSensor implements Sensor { } private final MeasureCache measureCache; - private final CoverageExclusions coverageExclusions; - public ZeroCoverageSensor(MeasureCache measureCache, CoverageExclusions exclusions) { + public ZeroCoverageSensor(MeasureCache measureCache) { this.measureCache = measureCache; - this.coverageExclusions = exclusions; } @Override @@ -76,7 +74,7 @@ public final class ZeroCoverageSensor implements Sensor { public void execute(final SensorContext context) { FileSystem fs = context.fileSystem(); for (InputFile f : fs.inputFiles(fs.predicates().hasType(Type.MAIN))) { - if (coverageExclusions.isExcluded(f)) { + if (((DefaultInputFile) f).isExcludedForCoverage()) { continue; } if (!isCoverageMeasuresAlreadyDefined(f)) { diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/coverage/CoverageMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/coverage/CoverageMediumTest.java index e840ef3cda4..c42972232d4 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/coverage/CoverageMediumTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/coverage/CoverageMediumTest.java @@ -203,4 +203,63 @@ public class CoverageMediumTest { assertThat(allMeasures.get("com.foo.project:src/sample.xoo")).extracting("metricKey").doesNotContain(CoreMetrics.CONDITIONS_TO_COVER_KEY, CoreMetrics.UNCOVERED_CONDITIONS_KEY); } + // SONAR-9557 + @Test + public void exclusionsAndForceToZeroOnModules() throws IOException { + + File baseDir = temp.getRoot(); + File srcDir = new File(baseDir, "module1/src"); + srcDir.mkdir(); + + File xooFile1 = new File(srcDir, "sample1.xoo"); + File measuresFile1 = new File(srcDir, "sample1.xoo.measures"); + FileUtils.write(xooFile1, "function foo() {\n if (a && b) {\nalert('hello');\n}\n}"); + FileUtils.write(measuresFile1, "executable_lines_data:2=1;3=1;4=0"); + + File xooFile2 = new File(srcDir, "sample2.xoo"); + File measuresFile2 = new File(srcDir, "sample2.xoo.measures"); + FileUtils.write(xooFile2, "function foo() {\n if (a && b) {\nalert('hello');\n}\n}"); + FileUtils.write(measuresFile2, "executable_lines_data:2=1;3=1;4=0"); + + TaskResult result = tester.newTask() + .properties(ImmutableMap.<String, String>builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.modules", "module1") + .put("sonar.sources", "src") + .put("sonar.coverage.exclusions", "**/sample2.xoo") + .build()) + .execute(); + + InputFile file1 = result.inputFile("src/sample1.xoo"); + assertThat(result.coverageFor(file1, 1)).isNull(); + + assertThat(result.coverageFor(file1, 2).getHits()).isFalse(); + assertThat(result.coverageFor(file1, 2).getConditions()).isEqualTo(0); + assertThat(result.coverageFor(file1, 2).getCoveredConditions()).isEqualTo(0); + + assertThat(result.coverageFor(file1, 3).getHits()).isFalse(); + assertThat(result.coverageFor(file1, 4)).isNull(); + + InputFile file2 = result.inputFile("src/sample2.xoo"); + assertThat(result.coverageFor(file2, 1)).isNull(); + assertThat(result.coverageFor(file2, 2)).isNull(); + assertThat(result.coverageFor(file2, 3)).isNull(); + assertThat(result.coverageFor(file2, 4)).isNull(); + + Map<String, List<org.sonar.scanner.protocol.output.ScannerReport.Measure>> allMeasures = result.allMeasures(); + + assertThat(allMeasures.get("com.foo.project:module1:src/sample1.xoo")).extracting("metricKey", "intValue.value") + .contains(tuple(CoreMetrics.LINES_TO_COVER_KEY, 2), + tuple(CoreMetrics.UNCOVERED_LINES_KEY, 2)); + + assertThat(allMeasures.get("com.foo.project:module1:src/sample1.xoo")).extracting("metricKey").doesNotContain(CoreMetrics.CONDITIONS_TO_COVER_KEY, + CoreMetrics.UNCOVERED_CONDITIONS_KEY); + + assertThat(allMeasures.get("com.foo.project:module1:src/sample2.xoo")).extracting("metricKey").doesNotContain(CoreMetrics.LINES_TO_COVER_KEY, + CoreMetrics.CONDITIONS_TO_COVER_KEY, + CoreMetrics.UNCOVERED_CONDITIONS_KEY, CoreMetrics.UNCOVERED_LINES_KEY); + } + } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/fs/FileSystemMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/fs/FileSystemMediumTest.java index 765f8729610..016e8c8e554 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/fs/FileSystemMediumTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/fs/FileSystemMediumTest.java @@ -118,7 +118,7 @@ public class FileSystemMediumTest { assertThat(dir.relativePath()).isEqualTo("src"); // file and dirs were published, since language matched xoo - assertThat(file.publish()).isTrue(); + assertThat(file.isPublished()).isTrue(); assertThat(result.getReportComponent(dir.key())).isNotNull(); assertThat(result.getReportComponent(file.key())).isNotNull(); } @@ -375,7 +375,7 @@ public class FileSystemMediumTest { DefaultInputFile file = (DefaultInputFile) result.inputFile("src/sample.xoo"); InputDir dir = result.inputDir("src"); - assertThat(file.publish()).isTrue(); + assertThat(file.isPublished()).isTrue(); assertThat(result.getReportComponent(dir.key())).isNotNull(); assertThat(result.getReportComponent(file.key())).isNotNull(); } @@ -410,7 +410,7 @@ public class FileSystemMediumTest { DefaultInputFile unknownInputFile = (DefaultInputFile) result.inputFile("src/unknown/file.notanalyzed"); InputDir unknownInputDir = result.inputDir("src/unknown"); - assertThat(unknownInputFile.publish()).isFalse(); + assertThat(unknownInputFile.isPublished()).isFalse(); assertThat(result.getReportComponent(unknownInputDir.key())).isNotNull(); // no issues on empty dir diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/coverage/CoverageExclusionsTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/phases/CoverageExclusionsTest.java index e5d5d91a7a2..166964fcdc7 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/coverage/CoverageExclusionsTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/phases/CoverageExclusionsTest.java @@ -17,7 +17,7 @@ * 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.sensor.coverage; +package org.sonar.scanner.phases; import org.junit.Before; import org.junit.Test; @@ -26,6 +26,7 @@ import org.sonar.api.batch.fs.internal.TestInputFileBuilder; import org.sonar.api.config.PropertyDefinitions; import org.sonar.api.config.internal.MapSettings; import org.sonar.core.config.ExclusionProperties; +import org.sonar.scanner.phases.CoverageExclusions; import static org.assertj.core.api.Assertions.assertThat; diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/InputFileBuilderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/InputFileBuilderTest.java index d6b5ac30075..a387635db29 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/InputFileBuilderTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/filesystem/InputFileBuilderTest.java @@ -70,6 +70,6 @@ public class InputFileBuilderTest { assertThat(inputFile.moduleKey()).isEqualTo("module1"); assertThat(inputFile.absolutePath()).isEqualTo(filePath.toString().replaceAll("\\\\", "/")); assertThat(inputFile.key()).isEqualTo("module1:src/File1.xoo"); - assertThat(inputFile.publish()).isFalse(); + assertThat(inputFile.isPublished()).isFalse(); } } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/report/JSONReportTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/report/JSONReportTest.java index 65af751088f..276987ed552 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/report/JSONReportTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/report/JSONReportTest.java @@ -95,7 +95,7 @@ public class JSONReportTest { DefaultInputFile inputFile = new TestInputFileBuilder("struts", "src/main/java/org/apache/struts/Action.java") .setModuleBaseDir(projectBaseDir.toPath()).build(); inputFile.setStatus(InputFile.Status.CHANGED); - inputFile.setPublish(true); + inputFile.setPublished(true); inputComponentStore.put(inputFile); inputComponentStore.put(inputDir); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java index 11101d722ef..c97f20042b0 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java @@ -46,11 +46,9 @@ import org.sonar.scanner.protocol.output.ScannerReportWriter; import org.sonar.scanner.report.ReportPublisher; import org.sonar.scanner.repository.ContextPropertiesCache; import org.sonar.scanner.scan.measure.MeasureCache; -import org.sonar.scanner.sensor.coverage.CoverageExclusions; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.data.MapEntry.entry; -import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -77,12 +75,10 @@ public class DefaultSensorStorageTest { settings = new MapSettings(); moduleIssues = mock(ModuleIssues.class); measureCache = mock(MeasureCache.class); - CoverageExclusions coverageExclusions = mock(CoverageExclusions.class); - when(coverageExclusions.isExcluded(any(InputFile.class))).thenReturn(false); ReportPublisher reportPublisher = mock(ReportPublisher.class); when(reportPublisher.getWriter()).thenReturn(new ScannerReportWriter(temp.newFolder())); underTest = new DefaultSensorStorage(metricFinder, - moduleIssues, settings.asConfig(), coverageExclusions, reportPublisher, measureCache, + moduleIssues, settings.asConfig(), reportPublisher, measureCache, mock(SonarCpdBlockIndex.class), contextPropertiesCache, new ScannerMetrics()); } |