Browse Source

SONAR-5389 Analyzer optimizer

tags/4.4-RC1
Julien HENRY 10 years ago
parent
commit
e6a79a3f8d

+ 3
- 4
sonar-batch/src/main/java/org/sonar/batch/scan/SensorWrapper.java View File

@@ -19,8 +19,6 @@
*/
package org.sonar.batch.scan;

import org.sonar.api.batch.measure.Metric;

import org.sonar.api.batch.DependedUpon;
import org.sonar.api.batch.DependsUpon;
import org.sonar.api.batch.Sensor;
@@ -29,6 +27,7 @@ import org.sonar.api.batch.analyzer.Analyzer;
import org.sonar.api.batch.analyzer.AnalyzerContext;
import org.sonar.api.batch.analyzer.internal.DefaultAnalyzerDescriptor;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.measure.Metric;
import org.sonar.api.resources.Project;
import org.sonar.batch.scan2.AnalyzerOptimizer;

@@ -53,12 +52,12 @@ public class SensorWrapper implements Sensor {
}

@DependedUpon
public List<Metric<?>> provides() {
public List<Metric> provides() {
return Arrays.asList(descriptor.provides());
}

@DependsUpon
public List<Metric<?>> depends() {
public List<Metric> depends() {
return Arrays.asList(descriptor.dependsOn());
}


+ 36
- 9
sonar-batch/src/main/java/org/sonar/batch/scan2/AnalyzerOptimizer.java View File

@@ -23,27 +23,54 @@ import org.sonar.api.BatchComponent;
import org.sonar.api.batch.analyzer.internal.DefaultAnalyzerDescriptor;
import org.sonar.api.batch.fs.FilePredicate;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.rule.ActiveRules;

public class AnalyzerOptimizer implements BatchComponent {

private FileSystem fs;
private final FileSystem fs;
private final ActiveRules activeRules;

public AnalyzerOptimizer(FileSystem fs) {
public AnalyzerOptimizer(FileSystem fs, ActiveRules activeRules) {
this.fs = fs;
this.activeRules = activeRules;
}

/**
* Decide if the given Analyzer should be executed.
*/
public boolean shouldExecute(DefaultAnalyzerDescriptor descriptor) {
FilePredicate predicate = fs.predicates().hasLanguages(descriptor.languages());
if (descriptor.types().size() == 1) {
// Size = 0 or Size = 2 means both main and test type
predicate = fs.predicates().and(
predicate,
fs.predicates().hasType(descriptor.types().iterator().next()));
// FS Conditions
boolean fsCondition = fsCondition(descriptor);
boolean activeRulesCondition = activeRulesCondition(descriptor);
return fsCondition && activeRulesCondition;
}

private boolean activeRulesCondition(DefaultAnalyzerDescriptor descriptor) {
if (!descriptor.ruleRepositories().isEmpty()) {
for (String repoKey : descriptor.ruleRepositories()) {
if (!activeRules.findByRepository(repoKey).isEmpty()) {
return true;
}
}
return false;
}
return true;
}

private boolean fsCondition(DefaultAnalyzerDescriptor descriptor) {
if (!descriptor.languages().isEmpty() || !descriptor.types().isEmpty()) {
FilePredicate langPredicate = descriptor.languages().isEmpty() ? fs.predicates().all() : fs.predicates().hasLanguages(descriptor.languages());

FilePredicate typePredicate = descriptor.types().isEmpty() ? fs.predicates().all() : fs.predicates().none();
for (InputFile.Type type : descriptor.types()) {
typePredicate = fs.predicates().or(
typePredicate,
fs.predicates().hasType(type));
}
return fs.hasFiles(fs.predicates().and(langPredicate, typePredicate));
}
return fs.hasFiles(predicate);
return true;
}

}

+ 117
- 0
sonar-batch/src/test/java/org/sonar/batch/scan2/AnalyzerOptimizerTest.java View File

@@ -0,0 +1,117 @@
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube 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.
*
* SonarQube 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.
*/
package org.sonar.batch.scan2;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.batch.analyzer.internal.DefaultAnalyzerDescriptor;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.internal.DefaultFileSystem;
import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.rule.ActiveRules;
import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
import org.sonar.api.rule.RuleKey;

import static org.fest.assertions.Assertions.assertThat;

public class AnalyzerOptimizerTest {

DefaultFileSystem fs = new DefaultFileSystem();

@Rule
public ExpectedException thrown = ExpectedException.none();
private AnalyzerOptimizer optimizer;

@Before
public void prepare() {
optimizer = new AnalyzerOptimizer(fs, new ActiveRulesBuilder().build());
}

@Test
public void should_run_analyzer_with_no_metadata() throws Exception {
DefaultAnalyzerDescriptor descriptor = new DefaultAnalyzerDescriptor();

assertThat(optimizer.shouldExecute(descriptor)).isTrue();
}

@Test
public void should_optimize_on_language() throws Exception {
DefaultAnalyzerDescriptor descriptor = new DefaultAnalyzerDescriptor()
.workOnLanguages("java", "php");
assertThat(optimizer.shouldExecute(descriptor)).isFalse();

fs.add(new DefaultInputFile("src/Foo.java").setLanguage("java"));
assertThat(optimizer.shouldExecute(descriptor)).isTrue();
}

@Test
public void should_optimize_on_type() throws Exception {
DefaultAnalyzerDescriptor descriptor = new DefaultAnalyzerDescriptor()
.workOnFileTypes(InputFile.Type.MAIN);
assertThat(optimizer.shouldExecute(descriptor)).isFalse();

fs.add(new DefaultInputFile("tests/FooTest.java").setType(InputFile.Type.TEST));
assertThat(optimizer.shouldExecute(descriptor)).isFalse();

fs.add(new DefaultInputFile("src/Foo.java").setType(InputFile.Type.MAIN));
assertThat(optimizer.shouldExecute(descriptor)).isTrue();
}

@Test
public void should_optimize_on_both_type_and_language() throws Exception {
DefaultAnalyzerDescriptor descriptor = new DefaultAnalyzerDescriptor()
.workOnLanguages("java", "php")
.workOnFileTypes(InputFile.Type.MAIN);
assertThat(optimizer.shouldExecute(descriptor)).isFalse();

fs.add(new DefaultInputFile("tests/FooTest.java").setLanguage("java").setType(InputFile.Type.TEST));
fs.add(new DefaultInputFile("src/Foo.cbl").setLanguage("cobol").setType(InputFile.Type.MAIN));
assertThat(optimizer.shouldExecute(descriptor)).isFalse();

fs.add(new DefaultInputFile("src/Foo.java").setLanguage("java").setType(InputFile.Type.MAIN));
assertThat(optimizer.shouldExecute(descriptor)).isTrue();
}

@Test
public void should_optimize_on_repository() throws Exception {
DefaultAnalyzerDescriptor descriptor = new DefaultAnalyzerDescriptor()
.createIssuesForRuleRepositories("squid");
assertThat(optimizer.shouldExecute(descriptor)).isFalse();

ActiveRules activeRules = new ActiveRulesBuilder()
.activate(RuleKey.of("repo1", "foo"))
.end()
.build();
optimizer = new AnalyzerOptimizer(fs, activeRules);

assertThat(optimizer.shouldExecute(descriptor)).isFalse();

activeRules = new ActiveRulesBuilder()
.activate(RuleKey.of("repo1", "foo"))
.end()
.activate(RuleKey.of("squid", "rule"))
.end()
.build();
optimizer = new AnalyzerOptimizer(fs, activeRules);
assertThat(optimizer.shouldExecute(descriptor)).isTrue();
}
}

+ 5
- 5
sonar-deprecated/src/main/java/org/sonar/api/rules/RuleRepository.java View File

@@ -28,7 +28,7 @@ import java.util.List;

/**
* @since 2.3
* @deprecated in 4.2. Replaced by org.sonar.api.rule.RuleDefinitions
* @deprecated in 4.2. Replaced by org.sonar.api.server.rule.RuleDefinition
*/
@Deprecated
public abstract class RuleRepository implements ServerExtension {
@@ -71,9 +71,9 @@ public abstract class RuleRepository implements ServerExtension {
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
.append("key", key)
.append("language", language)
.append("name", name)
.toString();
.append("key", key)
.append("language", language)
.append("name", name)
.toString();
}
}

+ 9
- 2
sonar-plugin-api/src/main/java/org/sonar/api/batch/analyzer/AnalyzerDescriptor.java View File

@@ -51,13 +51,20 @@ public interface AnalyzerDescriptor {
* no file for given languages are present in the project.
* If no language is provided then it will be executed for all languages.
*/
AnalyzerDescriptor runOnLanguages(String... languageKeys);
AnalyzerDescriptor workOnLanguages(String... languageKeys);

/**
* List {@link InputFile.Type} this {@link Analyzer} work on. May be used by the platform to skip execution of the {@link Analyzer} when
* no file for given type are present in the project.
* If not type is provided then it will be executed for all types.
*/
AnalyzerDescriptor runOnTypes(InputFile.Type... types);
AnalyzerDescriptor workOnFileTypes(InputFile.Type... types);

/**
* List {@link InputFile.Type} this {@link Analyzer} work on. May be used by the platform to skip execution of the {@link Analyzer} when
* no file for given type are present in the project.
* If not type is provided then it will be executed for all types.
*/
AnalyzerDescriptor createIssuesForRuleRepositories(String... repositoryKeys);

}

+ 16
- 6
sonar-plugin-api/src/main/java/org/sonar/api/batch/analyzer/internal/DefaultAnalyzerDescriptor.java View File

@@ -19,10 +19,9 @@
*/
package org.sonar.api.batch.analyzer.internal;

import org.sonar.api.batch.measure.Metric;

import org.sonar.api.batch.analyzer.AnalyzerDescriptor;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.measure.Metric;

import java.util.Arrays;
import java.util.Collection;
@@ -34,16 +33,17 @@ public class DefaultAnalyzerDescriptor implements AnalyzerDescriptor {
private Metric<?>[] provides = new Metric<?>[0];
private String[] languages = new String[0];
private InputFile.Type[] types = new InputFile.Type[0];
private String[] ruleRepositories = new String[0];

public String name() {
return name;
}

public Metric<?>[] dependsOn() {
public Metric[] dependsOn() {
return dependsOn;
}

public Metric<?>[] provides() {
public Metric[] provides() {
return provides;
}

@@ -55,6 +55,10 @@ public class DefaultAnalyzerDescriptor implements AnalyzerDescriptor {
return Arrays.asList(types);
}

public Collection<String> ruleRepositories() {
return Arrays.asList(ruleRepositories);
}

@Override
public DefaultAnalyzerDescriptor name(String name) {
this.name = name;
@@ -74,15 +78,21 @@ public class DefaultAnalyzerDescriptor implements AnalyzerDescriptor {
}

@Override
public DefaultAnalyzerDescriptor runOnLanguages(String... languageKeys) {
public DefaultAnalyzerDescriptor workOnLanguages(String... languageKeys) {
this.languages = languageKeys;
return this;
}

@Override
public DefaultAnalyzerDescriptor runOnTypes(InputFile.Type... types) {
public DefaultAnalyzerDescriptor workOnFileTypes(InputFile.Type... types) {
this.types = types;
return this;
}

@Override
public DefaultAnalyzerDescriptor createIssuesForRuleRepositories(String... repositoryKeys) {
this.ruleRepositories = repositoryKeys;
return this;
}

}

+ 1
- 1
sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/ActiveRulesBuilder.java View File

@@ -39,7 +39,7 @@ public class ActiveRulesBuilder {
if (map.containsKey(ruleKey)) {
throw new IllegalStateException(String.format("Rule '%s' is already activated", ruleKey));
}
NewActiveRule newActiveRule = new NewActiveRule(ruleKey);
NewActiveRule newActiveRule = new NewActiveRule(this, ruleKey);
map.put(ruleKey, newActiveRule);
return newActiveRule;
}

+ 8
- 1
sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/NewActiveRule.java View File

@@ -24,6 +24,7 @@ import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.Severity;

import javax.annotation.Nullable;

import java.util.HashMap;
import java.util.Map;

@@ -35,8 +36,10 @@ public class NewActiveRule {
String severity = Severity.defaultSeverity();
Map<String, String> params = new HashMap<String, String>();
String internalKey, language;
private final ActiveRulesBuilder builder;

NewActiveRule(RuleKey ruleKey) {
NewActiveRule(ActiveRulesBuilder builder, RuleKey ruleKey) {
this.builder = builder;
this.ruleKey = ruleKey;
}

@@ -68,4 +71,8 @@ public class NewActiveRule {
public Map<String, String> params() {
return params;
}

public ActiveRulesBuilder end() {
return builder;
}
}

+ 2
- 2
sonar-plugin-api/src/test/java/org/sonar/api/batch/analyzer/internal/DefaultAnalyzerDescriptorTest.java View File

@@ -34,8 +34,8 @@ public class DefaultAnalyzerDescriptorTest {
.name("Foo")
.dependsOn(CoreMetrics.NCLOC)
.provides(CoreMetrics.BLOCKER_VIOLATIONS)
.runOnLanguages("java", "php")
.runOnTypes(InputFile.Type.MAIN);
.workOnLanguages("java", "php")
.workOnFileTypes(InputFile.Type.MAIN);

assertThat(descriptor.name()).isEqualTo("Foo");
assertThat(descriptor.dependsOn()).containsOnly(CoreMetrics.NCLOC);

+ 10
- 10
sonar-plugin-api/src/test/java/org/sonar/api/batch/rule/internal/ActiveRulesBuilderTest.java View File

@@ -38,16 +38,16 @@ public class ActiveRulesBuilderTest {

@Test
public void build_rules() throws Exception {
ActiveRulesBuilder builder = new ActiveRulesBuilder();
NewActiveRule newSquid1 = builder.activate(RuleKey.of("squid", "S0001"));
newSquid1.setSeverity(Severity.CRITICAL);
newSquid1.setInternalKey("__S0001__");
newSquid1.setParam("min", "20");
// most simple rule
builder.activate(RuleKey.of("squid", "S0002"));
builder.activate(RuleKey.of("findbugs", "NPE")).setInternalKey(null).setSeverity(null).setParam("foo", null);
ActiveRules activeRules = builder.build();
ActiveRules activeRules = new ActiveRulesBuilder()
.activate(RuleKey.of("squid", "S0001"))
.setSeverity(Severity.CRITICAL)
.setInternalKey("__S0001__")
.setParam("min", "20")
.end()
// most simple rule
.activate(RuleKey.of("squid", "S0002")).end()
.activate(RuleKey.of("findbugs", "NPE")).setInternalKey(null).setSeverity(null).setParam("foo", null).end()
.build();

assertThat(activeRules.findAll()).hasSize(3);
assertThat(activeRules.findByRepository("squid")).hasSize(2);

Loading…
Cancel
Save