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

descriptor descriptor
.name("One Issue Per Line") .name("One Issue Per Line")
.onlyOnLanguages(Xoo.KEY, Xoo2.KEY) .onlyOnLanguages(Xoo.KEY, Xoo2.KEY)
.createIssuesForRuleRepositories(XooRulesDefinition.XOO_REPOSITORY, XooRulesDefinition.XOO2_REPOSITORY);
.createIssuesForRuleRepositories(XooRulesDefinition.XOO_REPOSITORY, XooRulesDefinition.XOO2_REPOSITORY)
.processesFilesIndependently();
} }


@Override @Override

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

import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import javax.annotation.Nullable; import javax.annotation.Nullable;

import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.sensor.SensorDescriptor; import org.sonar.api.batch.sensor.SensorDescriptor;
import org.sonar.api.config.Configuration; import org.sonar.api.config.Configuration;


public class DefaultSensorDescriptor implements SensorDescriptor { 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())); ).collect(Collectors.toSet()));


private String name; private String name;
private String[] ruleRepositories = new String[0]; private String[] ruleRepositories = new String[0];
private boolean global = false; private boolean global = false;
private Predicate<Configuration> configurationPredicate; private Predicate<Configuration> configurationPredicate;
private boolean onlyChangedFilesInPullRequests = false;
private boolean processesFilesIndependently = false;


public String name() { public String name() {
return name; return name;
return global; return global;
} }


public boolean onlyChangedFilesInPullRequest() {
return onlyChangedFilesInPullRequests;
public boolean isProcessesFilesIndependently() {
return processesFilesIndependently;
} }


@Override @Override
public DefaultSensorDescriptor name(String name) { 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; this.name = name;
return this; return this;
return this; 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

@RunWith(DataProviderRunner.class) @RunWith(DataProviderRunner.class)
public class DefaultSensorDescriptorTest { 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 @Test
public void describe() { public void describe() {
DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor(); DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor();
.onlyOnLanguage("java") .onlyOnLanguage("java")
.onlyOnFileType(InputFile.Type.MAIN) .onlyOnFileType(InputFile.Type.MAIN)
.onlyWhenConfiguration(c -> c.hasKey("sonar.foo.reportPath2") && c.hasKey("sonar.foo.reportPath")) .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.name()).isEqualTo("Foo");
assertThat(descriptor.languages()).containsOnly("java"); assertThat(descriptor.languages()).containsOnly("java");
settings.setProperty("sonar.foo.reportPath2", "foo"); settings.setProperty("sonar.foo.reportPath2", "foo");
assertThat(descriptor.configurationPredicate().test(settings.asConfig())).isTrue(); assertThat(descriptor.configurationPredicate().test(settings.asConfig())).isTrue();
assertThat(descriptor.ruleRepositories()).containsOnly("squid-java"); assertThat(descriptor.ruleRepositories()).containsOnly("squid-java");
assertThat(descriptor.isProcessesFilesIndependently()).isTrue();
} }


@Test @Test
@UseDataProvider("sensorsOnlyChangedInPR")
@UseDataProvider("independentFilesSensors")
public void describe_with_restricted_sensor(String sensorName) { public void describe_with_restricted_sensor(String sensorName) {
DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor(); DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor();
descriptor descriptor
.name(sensorName); .name(sensorName);


assertThat(descriptor.onlyChangedFilesInPullRequest()).isTrue();
assertThat(descriptor.isProcessesFilesIndependently()).isTrue();
} }


@Test @Test
@UseDataProvider("sensorsOnlyChangedInPR")
@UseDataProvider("independentFilesSensors")
public void describe_with_non_restricted_sensor(String sensorName) { public void describe_with_non_restricted_sensor(String sensorName) {
DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor(); DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor();
descriptor descriptor
.name(sensorName + "other"); .name(sensorName + "other");


assertThat(descriptor.onlyChangedFilesInPullRequest()).isFalse();
assertThat(descriptor.isProcessesFilesIndependently()).isFalse();
} }


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

* @since 6.5 * @since 6.5
*/ */
SensorDescriptor onlyWhenConfiguration(Predicate<Configuration> predicate); 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

} }


public void analyse() { public void analyse() {
boolean sensorIsRestricted = descriptor.onlyChangedFilesInPullRequest() && isPullRequest;
boolean sensorIsRestricted = descriptor.isProcessesFilesIndependently() && isPullRequest;
if (sensorIsRestricted) { if (sensorIsRestricted) {
LOGGER.info("Sensor {} is restricted to changed files only", descriptor.name()); 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

package org.sonar.scanner.mediumtest.branch; package org.sonar.scanner.mediumtest.branch;


import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;

import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.List;

import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.fs.internal.FileMetadata; import org.sonar.api.batch.fs.internal.FileMetadata;
import org.sonar.api.notifications.AnalysisWarnings; import org.sonar.api.notifications.AnalysisWarnings;
import org.sonar.api.utils.log.LogTester;
import org.sonar.scanner.mediumtest.AnalysisResult; import org.sonar.scanner.mediumtest.AnalysisResult;
import org.sonar.scanner.mediumtest.ScannerMediumTester; import org.sonar.scanner.mediumtest.ScannerMediumTester;
import org.sonar.scanner.protocol.output.ScannerReport; import org.sonar.scanner.protocol.output.ScannerReport;
private static final String PROJECT_KEY = "sample"; private static final String PROJECT_KEY = "sample";
private static final String FILE_PATH = "HelloJava.xoo"; private static final String FILE_PATH = "HelloJava.xoo";
private static final String FILE_CONTENT = "xoooo"; 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; private File baseDir;


@Rule @Rule
public TemporaryFolder temp = new TemporaryFolder(); public TemporaryFolder temp = new TemporaryFolder();


@Rule
public LogTester logTester = new LogTester();

@Rule @Rule
public ScannerMediumTester tester = new ScannerMediumTester() public ScannerMediumTester tester = new ScannerMediumTester()
.registerPlugin("xoo", new XooPlugin()) .registerPlugin("xoo", new XooPlugin())
assertThat(result2.getReportReader().readFileSource(fileId)).isNull(); 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 @Test
public void verify_metadata() { public void verify_metadata() {
String branchName = "feature"; String branchName = "feature";

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



@DataProvider @DataProvider
public static Object[][] sensorsOnlyChangedInPR() { 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 { private ModuleSensorsExecutor createModuleExecutor(String sensorName) throws IOException {

Loading…
Cancel
Save