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