diff options
7 files changed, 87 insertions, 21 deletions
diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssuePerLineSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssuePerLineSensor.java index a2d03803220..0c1aa2d81e0 100644 --- a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssuePerLineSensor.java +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssuePerLineSensor.java @@ -44,7 +44,8 @@ public class OneIssuePerLineSensor implements Sensor { descriptor .name("One Issue Per Line") .onlyOnLanguages(Xoo.KEY, Xoo2.KEY) - .createIssuesForRuleRepositories(XooRulesDefinition.XOO_REPOSITORY, XooRulesDefinition.XOO2_REPOSITORY); + .createIssuesForRuleRepositories(XooRulesDefinition.XOO_REPOSITORY, XooRulesDefinition.XOO2_REPOSITORY) + .processesFilesIndependently(); } @Override diff --git a/sonar-plugin-api-impl/src/main/java/org/sonar/api/batch/sensor/internal/DefaultSensorDescriptor.java b/sonar-plugin-api-impl/src/main/java/org/sonar/api/batch/sensor/internal/DefaultSensorDescriptor.java index e0819050190..558dfe17c1f 100644 --- a/sonar-plugin-api-impl/src/main/java/org/sonar/api/batch/sensor/internal/DefaultSensorDescriptor.java +++ b/sonar-plugin-api-impl/src/main/java/org/sonar/api/batch/sensor/internal/DefaultSensorDescriptor.java @@ -27,16 +27,17 @@ import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.annotation.Nullable; + import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.sensor.SensorDescriptor; import org.sonar.api.config.Configuration; public class DefaultSensorDescriptor implements SensorDescriptor { - public static final Set<String> SENSORS_ONLY_CHANGED_IN_PR = Collections.unmodifiableSet(Stream.of( - "CSS Metrics", - "CSS Rules", - "HTML", - "XML Sensor" + public static final Set<String> HARDCODED_INDEPENDENT_FILE_SENSORS = Collections.unmodifiableSet(Stream.of( + "CSS Metrics", + "CSS Rules", + "HTML", + "XML Sensor" ).collect(Collectors.toSet())); private String name; @@ -45,7 +46,7 @@ public class DefaultSensorDescriptor implements SensorDescriptor { private String[] ruleRepositories = new String[0]; private boolean global = false; private Predicate<Configuration> configurationPredicate; - private boolean onlyChangedFilesInPullRequests = false; + private boolean processesFilesIndependently = false; public String name() { return name; @@ -72,15 +73,15 @@ public class DefaultSensorDescriptor implements SensorDescriptor { return global; } - public boolean onlyChangedFilesInPullRequest() { - return onlyChangedFilesInPullRequests; + public boolean isProcessesFilesIndependently() { + return processesFilesIndependently; } @Override public DefaultSensorDescriptor name(String name) { - // TODO: Add onlyChangedFilesInPullRequest into API and implement it at sensors - if (SENSORS_ONLY_CHANGED_IN_PR.contains(name)) { - onlyChangedFilesInPullRequests = true; + // TODO: Remove this hardcoded list once all plugins will implement the new API "processFilesIndependently" + if (HARDCODED_INDEPENDENT_FILE_SENSORS.contains(name)) { + processesFilesIndependently = true; } this.name = name; return this; @@ -126,4 +127,9 @@ public class DefaultSensorDescriptor implements SensorDescriptor { return this; } + @Override + public SensorDescriptor processesFilesIndependently() { + this.processesFilesIndependently = true; + return this; + } } diff --git a/sonar-plugin-api-impl/src/test/java/org/sonar/api/batch/sensor/internal/DefaultSensorDescriptorTest.java b/sonar-plugin-api-impl/src/test/java/org/sonar/api/batch/sensor/internal/DefaultSensorDescriptorTest.java index e34c5385cad..a5cd716a04e 100644 --- a/sonar-plugin-api-impl/src/test/java/org/sonar/api/batch/sensor/internal/DefaultSensorDescriptorTest.java +++ b/sonar-plugin-api-impl/src/test/java/org/sonar/api/batch/sensor/internal/DefaultSensorDescriptorTest.java @@ -33,6 +33,19 @@ import static org.assertj.core.api.Assertions.assertThat; public class DefaultSensorDescriptorTest { @Test + public void describe_defaults() { + DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor(); + descriptor + .name("Foo"); + + assertThat(descriptor.name()).isEqualTo("Foo"); + assertThat(descriptor.languages()).isEmpty(); + assertThat(descriptor.type()).isNull(); + assertThat(descriptor.ruleRepositories()).isEmpty(); + assertThat(descriptor.isProcessesFilesIndependently()).isFalse(); + } + + @Test public void describe() { DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor(); descriptor @@ -40,7 +53,8 @@ public class DefaultSensorDescriptorTest { .onlyOnLanguage("java") .onlyOnFileType(InputFile.Type.MAIN) .onlyWhenConfiguration(c -> c.hasKey("sonar.foo.reportPath2") && c.hasKey("sonar.foo.reportPath")) - .createIssuesForRuleRepository("squid-java"); + .createIssuesForRuleRepository("squid-java") + .processesFilesIndependently(); assertThat(descriptor.name()).isEqualTo("Foo"); assertThat(descriptor.languages()).containsOnly("java"); @@ -51,31 +65,32 @@ public class DefaultSensorDescriptorTest { settings.setProperty("sonar.foo.reportPath2", "foo"); assertThat(descriptor.configurationPredicate().test(settings.asConfig())).isTrue(); assertThat(descriptor.ruleRepositories()).containsOnly("squid-java"); + assertThat(descriptor.isProcessesFilesIndependently()).isTrue(); } @Test - @UseDataProvider("sensorsOnlyChangedInPR") + @UseDataProvider("independentFilesSensors") public void describe_with_restricted_sensor(String sensorName) { DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor(); descriptor .name(sensorName); - assertThat(descriptor.onlyChangedFilesInPullRequest()).isTrue(); + assertThat(descriptor.isProcessesFilesIndependently()).isTrue(); } @Test - @UseDataProvider("sensorsOnlyChangedInPR") + @UseDataProvider("independentFilesSensors") public void describe_with_non_restricted_sensor(String sensorName) { DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor(); descriptor .name(sensorName + "other"); - assertThat(descriptor.onlyChangedFilesInPullRequest()).isFalse(); + assertThat(descriptor.isProcessesFilesIndependently()).isFalse(); } @DataProvider - public static Object[][] sensorsOnlyChangedInPR() { - return new Object[][] {DefaultSensorDescriptor.SENSORS_ONLY_CHANGED_IN_PR.toArray()}; + public static Object[][] independentFilesSensors() { + return new Object[][] {DefaultSensorDescriptor.HARDCODED_INDEPENDENT_FILE_SENSORS.toArray()}; } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorDescriptor.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorDescriptor.java index 0117afae923..72e8ec2547c 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorDescriptor.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorDescriptor.java @@ -83,4 +83,12 @@ public interface SensorDescriptor { * @since 6.5 */ SensorDescriptor onlyWhenConfiguration(Predicate<Configuration> predicate); + + /** + * Advertise that this sensor process each file independently. It means executing the sensor twice with FileSystem=[FileA] and FileSystem=[FileB] will produce the same result + * than executing the sensor only once with FileSystem=[FileA,FileB]. + * This will allow the platform to optimize sensor execution. + * @since 9.3 + */ + SensorDescriptor processesFilesIndependently(); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/AbstractSensorWrapper.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/AbstractSensorWrapper.java index 21e5308d66e..2df1bec52c7 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/AbstractSensorWrapper.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/AbstractSensorWrapper.java @@ -56,7 +56,7 @@ public abstract class AbstractSensorWrapper<G extends ProjectSensor> { } public void analyse() { - boolean sensorIsRestricted = descriptor.onlyChangedFilesInPullRequest() && isPullRequest; + boolean sensorIsRestricted = descriptor.isProcessesFilesIndependently() && isPullRequest; if (sensorIsRestricted) { LOGGER.info("Sensor {} is restricted to changed files only", descriptor.name()); } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/branch/BranchMediumTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/branch/BranchMediumTest.java index bab0b6757ce..7f7484e6159 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/branch/BranchMediumTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/branch/BranchMediumTest.java @@ -20,11 +20,14 @@ package org.sonar.scanner.mediumtest.branch; import com.google.common.collect.ImmutableMap; + import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.util.List; + import org.apache.commons.io.FileUtils; import org.junit.Before; import org.junit.Rule; @@ -33,6 +36,7 @@ import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.fs.internal.FileMetadata; import org.sonar.api.notifications.AnalysisWarnings; +import org.sonar.api.utils.log.LogTester; import org.sonar.scanner.mediumtest.AnalysisResult; import org.sonar.scanner.mediumtest.ScannerMediumTester; import org.sonar.scanner.protocol.output.ScannerReport; @@ -50,12 +54,16 @@ public class BranchMediumTest { private static final String PROJECT_KEY = "sample"; private static final String FILE_PATH = "HelloJava.xoo"; private static final String FILE_CONTENT = "xoooo"; + public static final String ONE_ISSUE_PER_LINE_IS_RESTRICTED_TO_CHANGED_FILES_ONLY = "Sensor One Issue Per Line is restricted to changed files only"; private File baseDir; @Rule public TemporaryFolder temp = new TemporaryFolder(); @Rule + public LogTester logTester = new LogTester(); + + @Rule public ScannerMediumTester tester = new ScannerMediumTester() .registerPlugin("xoo", new XooPlugin()) .addDefaultQProfile("xoo", "Sonar Way") @@ -100,6 +108,34 @@ public class BranchMediumTest { } @Test + public void shouldSkipSensorForUnchangedFilesOnPr() throws Exception { + AnalysisResult result = getResult(tester + .setBranchName("myBranch") + .setBranchTarget("master") + .setBranchType(BranchType.PULL_REQUEST)); + final DefaultInputFile file = (DefaultInputFile) result.inputFile(FILE_PATH); + + List<ScannerReport.Issue> issues = result.issuesFor(file); + assertThat(issues).isEmpty(); + + assertThat(logTester.logs()).contains(ONE_ISSUE_PER_LINE_IS_RESTRICTED_TO_CHANGED_FILES_ONLY); + } + + @Test + public void shouldNotSkipSensorForUnchangedFilesOnBranch() throws Exception { + AnalysisResult result = getResult(tester + .setBranchName("myBranch") + .setBranchTarget("master") + .setBranchType(BranchType.BRANCH)); + final DefaultInputFile file = (DefaultInputFile) result.inputFile(FILE_PATH); + + List<ScannerReport.Issue> issues = result.issuesFor(file); + assertThat(issues).isNotEmpty(); + + assertThat(logTester.logs()).doesNotContain(ONE_ISSUE_PER_LINE_IS_RESTRICTED_TO_CHANGED_FILES_ONLY); + } + + @Test public void verify_metadata() { String branchName = "feature"; String branchTarget = "branch-1.x"; diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/phases/ModuleSensorsExecutorTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/phases/ModuleSensorsExecutorTest.java index 820a528c040..e99bc31d5fa 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/phases/ModuleSensorsExecutorTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/phases/ModuleSensorsExecutorTest.java @@ -177,7 +177,7 @@ public class ModuleSensorsExecutorTest { @DataProvider public static Object[][] sensorsOnlyChangedInPR() { - return new Object[][] {DefaultSensorDescriptor.SENSORS_ONLY_CHANGED_IN_PR.toArray()}; + return new Object[][] {DefaultSensorDescriptor.HARDCODED_INDEPENDENT_FILE_SENSORS.toArray()}; } private ModuleSensorsExecutor createModuleExecutor(String sensorName) throws IOException { |