Browse Source

SONAR-15761 new API to identify Sensors processing files independently (#5091)

tags/9.3.0.51899
Julien HENRY 2 years ago
parent
commit
1ffcc9a85c

+ 2
- 1
plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssuePerLineSensor.java View File

@@ -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

+ 17
- 11
sonar-plugin-api-impl/src/main/java/org/sonar/api/batch/sensor/internal/DefaultSensorDescriptor.java View File

@@ -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;
}
}

+ 22
- 7
sonar-plugin-api-impl/src/test/java/org/sonar/api/batch/sensor/internal/DefaultSensorDescriptorTest.java View File

@@ -32,6 +32,19 @@ import static org.assertj.core.api.Assertions.assertThat;
@RunWith(DataProviderRunner.class)
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();
@@ -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()};
}

}

+ 8
- 0
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorDescriptor.java View File

@@ -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();
}

+ 1
- 1
sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/AbstractSensorWrapper.java View File

@@ -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());
}

+ 36
- 0
sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/branch/BranchMediumTest.java View File

@@ -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,11 +54,15 @@ 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())
@@ -99,6 +107,34 @@ public class BranchMediumTest {
assertThat(result2.getReportReader().readFileSource(fileId)).isNull();
}

@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";

+ 1
- 1
sonar-scanner-engine/src/test/java/org/sonar/scanner/phases/ModuleSensorsExecutorTest.java View File

@@ -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 {

Loading…
Cancel
Save