diff options
author | Julien HENRY <julien.henry@sonarsource.com> | 2014-06-19 14:48:07 +0200 |
---|---|---|
committer | Julien HENRY <julien.henry@sonarsource.com> | 2014-06-19 14:48:47 +0200 |
commit | e6a79a3f8da7bd3351aade2e9fb8a4f57c9fabe6 (patch) | |
tree | 6b590a32cc284372ef50bee6c1354b394ba3c60f | |
parent | 059c65d520fc80ac85616e98d789b4b67cac9019 (diff) | |
download | sonarqube-e6a79a3f8da7bd3351aade2e9fb8a4f57c9fabe6.tar.gz sonarqube-e6a79a3f8da7bd3351aade2e9fb8a4f57c9fabe6.zip |
SONAR-5389 Analyzer optimizer
10 files changed, 207 insertions, 40 deletions
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/SensorWrapper.java b/sonar-batch/src/main/java/org/sonar/batch/scan/SensorWrapper.java index 1f5408c2002..701233abbef 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/SensorWrapper.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/SensorWrapper.java @@ -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()); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/AnalyzerOptimizer.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/AnalyzerOptimizer.java index 3ad0a29aa76..0e875360743 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan2/AnalyzerOptimizer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/AnalyzerOptimizer.java @@ -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; } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan2/AnalyzerOptimizerTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan2/AnalyzerOptimizerTest.java new file mode 100644 index 00000000000..1384950b99d --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/scan2/AnalyzerOptimizerTest.java @@ -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(); + } +} diff --git a/sonar-deprecated/src/main/java/org/sonar/api/rules/RuleRepository.java b/sonar-deprecated/src/main/java/org/sonar/api/rules/RuleRepository.java index 1072dc9dc4d..a6d849c8970 100644 --- a/sonar-deprecated/src/main/java/org/sonar/api/rules/RuleRepository.java +++ b/sonar-deprecated/src/main/java/org/sonar/api/rules/RuleRepository.java @@ -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(); } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/analyzer/AnalyzerDescriptor.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/analyzer/AnalyzerDescriptor.java index 1a06563949d..3e38e5da708 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/analyzer/AnalyzerDescriptor.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/analyzer/AnalyzerDescriptor.java @@ -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); } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/analyzer/internal/DefaultAnalyzerDescriptor.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/analyzer/internal/DefaultAnalyzerDescriptor.java index dea8593013f..3f793c98a64 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/analyzer/internal/DefaultAnalyzerDescriptor.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/analyzer/internal/DefaultAnalyzerDescriptor.java @@ -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; + } + } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/ActiveRulesBuilder.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/ActiveRulesBuilder.java index a871a3faf88..208335e2ff3 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/ActiveRulesBuilder.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/ActiveRulesBuilder.java @@ -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; } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/NewActiveRule.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/NewActiveRule.java index 73bc8e8afd5..d2dbcf3f420 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/NewActiveRule.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/NewActiveRule.java @@ -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; + } } diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/analyzer/internal/DefaultAnalyzerDescriptorTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/analyzer/internal/DefaultAnalyzerDescriptorTest.java index 0c63dd2167c..b55bae7bf75 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/analyzer/internal/DefaultAnalyzerDescriptorTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/analyzer/internal/DefaultAnalyzerDescriptorTest.java @@ -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); diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/rule/internal/ActiveRulesBuilderTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/rule/internal/ActiveRulesBuilderTest.java index 41ff35f22c4..d83456058fd 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/rule/internal/ActiveRulesBuilderTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/rule/internal/ActiveRulesBuilderTest.java @@ -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); |