]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5389 Analyzer optimizer
authorJulien HENRY <julien.henry@sonarsource.com>
Thu, 19 Jun 2014 12:48:07 +0000 (14:48 +0200)
committerJulien HENRY <julien.henry@sonarsource.com>
Thu, 19 Jun 2014 12:48:47 +0000 (14:48 +0200)
sonar-batch/src/main/java/org/sonar/batch/scan/SensorWrapper.java
sonar-batch/src/main/java/org/sonar/batch/scan2/AnalyzerOptimizer.java
sonar-batch/src/test/java/org/sonar/batch/scan2/AnalyzerOptimizerTest.java [new file with mode: 0644]
sonar-deprecated/src/main/java/org/sonar/api/rules/RuleRepository.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/analyzer/AnalyzerDescriptor.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/analyzer/internal/DefaultAnalyzerDescriptor.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/ActiveRulesBuilder.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/NewActiveRule.java
sonar-plugin-api/src/test/java/org/sonar/api/batch/analyzer/internal/DefaultAnalyzerDescriptorTest.java
sonar-plugin-api/src/test/java/org/sonar/api/batch/rule/internal/ActiveRulesBuilderTest.java

index 1f5408c20023f211610f70aa778a8da5c4bb7ddd..701233abbefbd25fc74e441424596963dfebc738 100644 (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());
   }
 
index 3ad0a29aa765354e058fba6cbd6caff0c356b267..0e8753607430ff3215aed76d80186636cb2bb75e 100644 (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;
   }
 
 }
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 (file)
index 0000000..1384950
--- /dev/null
@@ -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();
+  }
+}
index 1072dc9dc4d4d1b0bd15bc47cbcf5eaa2dee3bfe..a6d849c89701b05afa3918ed7c9d6e3858b2fe98 100644 (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();
   }
 }
index 1a06563949d5506c9a1b4b99b831c7fb8cd5c491..3e38e5da7089421af92775e9d2544d98c8d33a24 100644 (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);
 
 }
index dea8593013f20dfd0f60c816855f50455b9026a4..3f793c98a64a1fb0c67ad712a6086b4868dca1a1 100644 (file)
  */
 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;
+  }
+
 }
index a871a3faf881b6de7f6f19b64c4c6782443f1437..208335e2ff3b448bee8ce7e322491452fd39fc67 100644 (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;
   }
index 73bc8e8afd572506a75eeb44fa9a5b2c9c6047aa..d2dbcf3f420bacd60e60bad840c1eb774c623560 100644 (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;
+  }
 }
index 0c63dd2167c1636be1ee92def826835542bde555..b55bae7bf755f68cb0fb3acd6d245a6cfac9432d 100644 (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);
index 41ff35f22c4216820736654abafa9e2db9c98fbe..d83456058fdac4442ce1ed368d1940780ed2395a 100644 (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);