]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9663 Backdate new issues when rule's plugin was updated
authorJulien HENRY <julien.henry@sonarsource.com>
Tue, 8 Aug 2017 15:42:46 +0000 (17:42 +0200)
committerJulien HENRY <julien.henry@sonarsource.com>
Thu, 7 Sep 2017 06:33:31 +0000 (08:33 +0200)
24 files changed:
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolder.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolderImpl.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/MutableAnalysisMetadataHolder.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/ScannerPlugin.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueCreationDateCalculator.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/Rule.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/RuleImpl.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/qualityprofile/ActiveRule.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/LoadQualityProfilesStep.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStep.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolderRule.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/MutableAnalysisMetadataHolderRule.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/ScannerPluginTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/DumbRule.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IssueCreationDateCalculatorTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/commonrule/CommentDensityRuleTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/commonrule/CommonRuleTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/commonrule/CoverageRuleTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/commonrule/DuplicatedBlockRuleTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/commonrule/SkippedTestRuleTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/commonrule/TestErrorRuleTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/qualityprofile/ActiveRulesHolderImplTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/LoadQualityProfilesStepTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStepTest.java

index 16840e230f1d1fdb3661fd1e050776597c1b6488..bb922d5e256bd5e6ddb02f884d44a65d18a6ccc9 100644 (file)
@@ -53,7 +53,7 @@ public interface AnalysisMetadataHolder {
    * @throws IllegalStateException if baseProjectSnapshot has not been set
    */
   boolean isFirstAnalysis();
-  
+
   /**
    * Whether this is an incremental analysis or a full analysis.
    */
@@ -86,4 +86,9 @@ public interface AnalysisMetadataHolder {
 
   Map<String, QualityProfile> getQProfilesByLanguage();
 
+  /**
+   * Plugins used during the analysis on scanner side
+   */
+  Map<String, ScannerPlugin> getScannerPluginsByKey();
+
 }
index a5b3128a0aafd7e9cded8389cbc20a87237e2dde..e03ddd2afcf137ee93b49d4dd76c8cfae0bddaaf 100644 (file)
@@ -40,6 +40,7 @@ public class AnalysisMetadataHolderImpl implements MutableAnalysisMetadataHolder
   private final InitializedProperty<String> branch = new InitializedProperty<>();
   private final InitializedProperty<Integer> rootComponentRef = new InitializedProperty<>();
   private final InitializedProperty<Map<String, QualityProfile>> qProfilesPerLanguage = new InitializedProperty<>();
+  private final InitializedProperty<Map<String, ScannerPlugin>> pluginsByKey = new InitializedProperty<>();
 
   @Override
   public MutableAnalysisMetadataHolder setOrganization(Organization organization) {
@@ -171,4 +172,17 @@ public class AnalysisMetadataHolderImpl implements MutableAnalysisMetadataHolder
     return qProfilesPerLanguage.getProperty();
   }
 
+  @Override
+  public MutableAnalysisMetadataHolder setScannerPluginsByKey(Map<String, ScannerPlugin> pluginsByKey) {
+    checkState(!this.pluginsByKey.isInitialized(), "Plugins by key has already been set");
+    this.pluginsByKey.setProperty(ImmutableMap.copyOf(pluginsByKey));
+    return this;
+  }
+
+  @Override
+  public Map<String, ScannerPlugin> getScannerPluginsByKey() {
+    checkState(pluginsByKey.isInitialized(), "Plugins by key has not been set");
+    return pluginsByKey.getProperty();
+  }
+
 }
index b72db65691660696f52b9f6667e07a7b456003e5..88214c7c5966d2cb0538c2710d68cd72e1d95bf9 100644 (file)
@@ -44,7 +44,7 @@ public interface MutableAnalysisMetadataHolder extends AnalysisMetadataHolder {
    * @throws IllegalStateException if it has already been set
    */
   MutableAnalysisMetadataHolder setIncrementalAnalysis(boolean isIncrementalAnalysis);
-  
+
   /**
    * @throws IllegalStateException if baseAnalysis has already been set
    */
@@ -70,4 +70,8 @@ public interface MutableAnalysisMetadataHolder extends AnalysisMetadataHolder {
    */
   MutableAnalysisMetadataHolder setQProfilesByLanguage(Map<String, QualityProfile> qprofilesByLanguage);
 
+  /**
+   * @throws IllegalStateException if Plugins by key has already been set
+   */
+  MutableAnalysisMetadataHolder setScannerPluginsByKey(Map<String, ScannerPlugin> pluginsByKey);
 }
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/ScannerPlugin.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/ScannerPlugin.java
new file mode 100644 (file)
index 0000000..d4eee35
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.server.computation.task.projectanalysis.analysis;
+
+import javax.annotation.concurrent.Immutable;
+
+import static java.util.Objects.requireNonNull;
+
+@Immutable
+public class ScannerPlugin {
+  private final String key;
+  private final long updatedAt;
+
+  public ScannerPlugin(String key, long updatedAt) {
+    this.key = requireNonNull(key, "key can't be null");
+    this.updatedAt = updatedAt;
+  }
+
+  public String getKey() {
+    return key;
+  }
+
+  public long getUpdatedAt() {
+    return updatedAt;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    ScannerPlugin that = (ScannerPlugin) o;
+    return key.equals(that.key);
+  }
+
+  @Override
+  public int hashCode() {
+    return key.hashCode();
+  }
+
+  @Override
+  public String toString() {
+    return "ScannerPlugin{" +
+      "key='" + key + '\'' +
+      ", updatedAt='" + updatedAt + '\'' +
+      '}';
+  }
+
+}
index 26137cbe6a89e3937efc9e9f4f460a37645b5002..8352a4e54a8d687fdbbf55c971e30fcd258007f0 100644 (file)
@@ -31,6 +31,7 @@ import org.sonar.core.issue.IssueChangeContext;
 import org.sonar.server.computation.task.projectanalysis.analysis.Analysis;
 import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolder;
 import org.sonar.server.computation.task.projectanalysis.component.Component;
+import org.sonar.server.computation.task.projectanalysis.qualityprofile.ActiveRule;
 import org.sonar.server.computation.task.projectanalysis.qualityprofile.ActiveRulesHolder;
 import org.sonar.server.computation.task.projectanalysis.scm.Changeset;
 import org.sonar.server.computation.task.projectanalysis.scm.ScmInfo;
@@ -64,40 +65,44 @@ public class IssueCreationDateCalculator extends IssueVisitor {
 
   @Override
   public void onIssue(Component component, DefaultIssue issue) {
-    if (issue.isNew() && ruleIsNew(issue)) {
+    if (!issue.isNew()) {
+      return;
+    }
+    Optional<Long> lastAnalysisOptional = lastAnalysis();
+    boolean firstAnalysis = !lastAnalysisOptional.isPresent();
+    ActiveRule activeRule = toJavaUtilOptional(activeRulesHolder.get(issue.getRuleKey()))
+      .orElseThrow(illegalStateException("The rule %s raised an issue, but is not one of the active rules.", issue.getRuleKey()));
+    if (firstAnalysis
+      || ruleIsNew(activeRule, lastAnalysisOptional.get())
+      || pluginIsNew(activeRule, lastAnalysisOptional.get())) {
       getScmChangeDate(component, issue)
         .ifPresent(changeDate -> updateDate(issue, changeDate));
     }
   }
 
+  private boolean pluginIsNew(ActiveRule activeRule, Long lastAnalysisDate) {
+    String pluginKey = activeRule.getPluginKey();
+    long pluginUpdateDate = Optional.ofNullable(analysisMetadataHolder.getScannerPluginsByKey().get(pluginKey))
+      .orElseThrow(illegalStateException("The rule %s is declared to come from plugin %s, but this plugin was not used by scanner.", activeRule.getRuleKey(), pluginKey))
+      .getUpdatedAt();
+    return lastAnalysisDate < pluginUpdateDate;
+  }
+
+  private static boolean ruleIsNew(ActiveRule activeRule, Long lastAnalysisDate) {
+    long ruleCreationDate = activeRule.getCreatedAt();
+    return lastAnalysisDate < ruleCreationDate;
+  }
+
   private Optional<Date> getScmChangeDate(Component component, DefaultIssue issue) {
     return getScmInfo(component)
       .flatMap(scmInfo -> getChangeset(scmInfo, issue))
       .map(IssueCreationDateCalculator::getChangeDate);
   }
 
-  private boolean ruleIsNew(DefaultIssue issue) {
-    long ruleCreation = ruleCreation(issue);
-    Optional<Long> lastAnalysisOptional = lastAnalysis();
-
-    if (lastAnalysisOptional.isPresent()) {
-      return lastAnalysisOptional.get() < ruleCreation;
-    }
-
-    // special case: this is the first analysis of the project: use scm dates for all issues
-    return true;
-  }
-
   private Optional<Long> lastAnalysis() {
     return Optional.ofNullable(analysisMetadataHolder.getBaseAnalysis()).map(Analysis::getCreatedAt);
   }
 
-  private long ruleCreation(DefaultIssue issue) {
-    return toJavaUtilOptional(activeRulesHolder.get(issue.getRuleKey()))
-      .orElseThrow(illegalStateException("The rule %s raised an issue, but is not one of the active rules.", issue.getRuleKey().rule()))
-      .getCreatedAt();
-  }
-
   private Optional<ScmInfo> getScmInfo(Component component) {
     return toJavaUtilOptional(scmInfoRepository.getScmInfo(component));
   }
index 87370e87986ac512e411c1cdb8d0eee530dd3b0f..3c6cb7b4207d74b63cdff7f54e7451ece9bea72b 100644 (file)
@@ -45,4 +45,6 @@ public interface Rule {
 
   @CheckForNull
   DebtRemediationFunction getRemediationFunction();
+
+  String getPluginKey();
 }
index adda0c98725b220d557274ec9b42138adc17cbbf..56aab9d104d472aa7922a8dd31d1e1f89418e517 100644 (file)
@@ -43,6 +43,7 @@ public class RuleImpl implements Rule {
   private final Set<String> tags;
   private final DebtRemediationFunction remediationFunction;
   private final RuleType type;
+  private final String pluginKey;
 
   public RuleImpl(RuleDto dto) {
     this.id = dto.getId();
@@ -52,6 +53,7 @@ public class RuleImpl implements Rule {
     this.tags = union(dto.getSystemTags(), dto.getTags());
     this.remediationFunction = effectiveRemediationFunction(dto);
     this.type = RuleType.valueOf(dto.getType());
+    this.pluginKey = dto.getPluginKey();
   }
 
   @Override
@@ -89,6 +91,11 @@ public class RuleImpl implements Rule {
     return type;
   }
 
+  @Override
+  public String getPluginKey() {
+    return pluginKey;
+  }
+
   @Override
   public boolean equals(@Nullable Object o) {
     if (this == o) {
@@ -114,6 +121,7 @@ public class RuleImpl implements Rule {
       .add("name", name)
       .add("status", status)
       .add("tags", tags)
+      .add("pluginKey", pluginKey)
       .toString();
   }
 
index 261577af5b810fe2026d73bdd045081abf1ab5cf..68465731a9c9f752876ce83038b85ba490851d5f 100644 (file)
@@ -30,10 +30,12 @@ public class ActiveRule {
   private final String severity;
   private final Map<String, String> params;
   private final long createdAt;
+  private final String pluginKey;
 
-  public ActiveRule(RuleKey ruleKey, String severity, Map<String, String> params, long createdAt) {
+  public ActiveRule(RuleKey ruleKey, String severity, Map<String, String> params, long createdAt, String pluginKey) {
     this.ruleKey = ruleKey;
     this.severity = severity;
+    this.pluginKey = pluginKey;
     this.params = ImmutableMap.copyOf(params);
     this.createdAt = createdAt;
   }
@@ -53,4 +55,8 @@ public class ActiveRule {
   public long getCreatedAt() {
     return createdAt;
   }
+
+  public String getPluginKey() {
+    return pluginKey;
+  }
 }
index 6ab07659f73fd1232bbeb18898770dc7d80b5a79..07bf6e4b4a429be0d5959c507505d0f8dc0d6565 100644 (file)
 package org.sonar.server.computation.task.projectanalysis.step;
 
 import com.google.common.base.Optional;
-import com.google.common.base.Predicate;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import javax.annotation.Nonnull;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.RuleStatus;
 import org.sonar.core.util.CloseableIterator;
@@ -37,8 +35,6 @@ import org.sonar.server.computation.task.projectanalysis.qualityprofile.ActiveRu
 import org.sonar.server.computation.task.projectanalysis.qualityprofile.ActiveRulesHolderImpl;
 import org.sonar.server.computation.task.step.ComputationStep;
 
-import static com.google.common.collect.FluentIterable.from;
-
 public class LoadQualityProfilesStep implements ComputationStep {
 
   private final BatchReportReader batchReportReader;
@@ -56,21 +52,16 @@ public class LoadQualityProfilesStep implements ComputationStep {
     List<ActiveRule> activeRules = new ArrayList<>();
     try (CloseableIterator<ScannerReport.ActiveRule> batchActiveRules = batchReportReader.readActiveRules()) {
       while (batchActiveRules.hasNext()) {
-        ScannerReport.ActiveRule batchActiveRule = batchActiveRules.next();
-        activeRules.add(convert(batchActiveRule));
+        ScannerReport.ActiveRule scannerReportActiveRule = batchActiveRules.next();
+        Optional<Rule> rule = ruleRepository.findByKey(RuleKey.of(scannerReportActiveRule.getRuleRepository(), scannerReportActiveRule.getRuleKey()));
+        if (rule.isPresent() && rule.get().getStatus() != RuleStatus.REMOVED) {
+          ActiveRule activeRule = convert(scannerReportActiveRule, rule.get());
+          activeRules.add(activeRule);
+        }
       }
     }
 
-    List<ActiveRule> validActiveRules = from(activeRules).filter(new IsValid()).toList();
-    activeRulesHolder.set(validActiveRules);
-  }
-
-  private class IsValid implements Predicate<ActiveRule> {
-    @Override
-    public boolean apply(@Nonnull ActiveRule input) {
-      Optional<Rule> rule = ruleRepository.findByKey(input.getRuleKey());
-      return rule.isPresent() && rule.get().getStatus() != RuleStatus.REMOVED;
-    }
+    activeRulesHolder.set(activeRules);
   }
 
   @Override
@@ -78,9 +69,9 @@ public class LoadQualityProfilesStep implements ComputationStep {
     return "Load quality profiles";
   }
 
-  private static ActiveRule convert(ScannerReport.ActiveRule input) {
+  private static ActiveRule convert(ScannerReport.ActiveRule input, Rule rule) {
     RuleKey key = RuleKey.of(input.getRuleRepository(), input.getRuleKey());
     Map<String, String> params = new HashMap<>(input.getParamsByKey());
-    return new ActiveRule(key, input.getSeverity().name(), params, input.getCreatedAt());
+    return new ActiveRule(key, input.getSeverity().name(), params, input.getCreatedAt(), rule.getPluginKey());
   }
 }
index f4cff3f6738864d119397a1240508aa43324a804..18658bda389f5cda598519ec343d7dea4f7b05ae 100644 (file)
@@ -19,7 +19,6 @@
  */
 package org.sonar.server.computation.task.projectanalysis.step;
 
-import com.google.common.base.Function;
 import com.google.common.base.Joiner;
 import java.util.Date;
 import java.util.List;
@@ -33,9 +32,11 @@ import org.sonar.db.DbSession;
 import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.qualityprofile.QProfileDto;
 import org.sonar.scanner.protocol.output.ScannerReport;
+import org.sonar.scanner.protocol.output.ScannerReport.Metadata.Plugin;
 import org.sonar.scanner.protocol.output.ScannerReport.Metadata.QProfile;
 import org.sonar.server.computation.task.projectanalysis.analysis.MutableAnalysisMetadataHolder;
 import org.sonar.server.computation.task.projectanalysis.analysis.Organization;
+import org.sonar.server.computation.task.projectanalysis.analysis.ScannerPlugin;
 import org.sonar.server.computation.task.projectanalysis.batch.BatchReportReader;
 import org.sonar.server.computation.task.step.ComputationStep;
 import org.sonar.server.organization.BillingValidations;
@@ -45,8 +46,8 @@ import org.sonar.server.organization.DefaultOrganizationProvider;
 import org.sonar.server.qualityprofile.QualityProfile;
 
 import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.collect.Maps.transformValues;
 import static java.lang.String.format;
+import static java.util.stream.Collectors.toMap;
 import static org.apache.commons.lang.StringUtils.isNotEmpty;
 import static org.sonar.core.util.stream.MoreCollectors.toList;
 
@@ -55,15 +56,6 @@ import static org.sonar.core.util.stream.MoreCollectors.toList;
  */
 public class LoadReportAnalysisMetadataHolderStep implements ComputationStep {
 
-  private static final ToComputeQProfile TO_COMPUTE_QPROFILE = new ToComputeQProfile();
-
-  private static final class ToComputeQProfile implements Function<QProfile, QualityProfile> {
-    @Override
-    public QualityProfile apply(QProfile input) {
-      return new QualityProfile(input.getKey(), input.getName(), input.getLanguage(), new Date(input.getRulesUpdatedAt()));
-    }
-  }
-
   private final CeTask ceTask;
   private final BatchReportReader reportReader;
   private final MutableAnalysisMetadataHolder mutableAnalysisMetadataHolder;
@@ -96,7 +88,14 @@ public class LoadReportAnalysisMetadataHolderStep implements ComputationStep {
     mutableAnalysisMetadataHolder.setBranch(isNotEmpty(reportMetadata.getBranch()) ? reportMetadata.getBranch() : null);
     mutableAnalysisMetadataHolder.setCrossProjectDuplicationEnabled(reportMetadata.getCrossProjectDuplicationActivated());
     mutableAnalysisMetadataHolder.setIncrementalAnalysis(reportMetadata.getIncremental());
-    mutableAnalysisMetadataHolder.setQProfilesByLanguage(transformValues(reportMetadata.getQprofilesPerLanguage(), TO_COMPUTE_QPROFILE));
+    mutableAnalysisMetadataHolder.setQProfilesByLanguage(reportMetadata.getQprofilesPerLanguage().values().stream()
+      .collect(toMap(
+        QProfile::getLanguage,
+        qp -> new QualityProfile(qp.getKey(), qp.getName(), qp.getLanguage(), new Date(qp.getRulesUpdatedAt())))));
+    mutableAnalysisMetadataHolder.setScannerPluginsByKey(reportMetadata.getPluginsByKey().values().stream()
+      .collect(toMap(
+        Plugin::getKey,
+        p -> new ScannerPlugin(p.getKey(), p.getUpdatedAt()))));
     mutableAnalysisMetadataHolder.setOrganization(organization);
   }
 
index 41a06d3f9ee1c5b4a7e42d6cb33c75043bcd3a52..96edd5650ac54a75ab20435b5247d2578da357c9 100644 (file)
@@ -52,6 +52,8 @@ public class AnalysisMetadataHolderRule extends ExternalResource implements Muta
 
   private final InitializedProperty<Map<String, QualityProfile>> qProfilesPerLanguage = new InitializedProperty<>();
 
+  private final InitializedProperty<Map<String, ScannerPlugin>> pluginsByKey = new InitializedProperty<>();
+
   @Override
   public AnalysisMetadataHolderRule setOrganization(Organization organization) {
     requireNonNull(organization, "organization can't be null");
@@ -174,6 +176,18 @@ public class AnalysisMetadataHolderRule extends ExternalResource implements Muta
     return qProfilesPerLanguage.getProperty();
   }
 
+  @Override
+  public AnalysisMetadataHolderRule setScannerPluginsByKey(Map<String, ScannerPlugin> plugins) {
+    this.pluginsByKey.setProperty(plugins);
+    return this;
+  }
+
+  @Override
+  public Map<String, ScannerPlugin> getScannerPluginsByKey() {
+    checkState(pluginsByKey.isInitialized(), "Plugins per key has not been set");
+    return pluginsByKey.getProperty();
+  }
+
   @Override
   public boolean isIncrementalAnalysis() {
     checkState(incremental.isInitialized(), "Incremental mode flag has not been set");
index 39bdf31b1f8b2376fad3395f15f839d869836db3..a6c6ca2154801ebd5a92f43f9ac6133b70232007 100644 (file)
@@ -130,6 +130,17 @@ public class MutableAnalysisMetadataHolderRule extends ExternalResource implemen
     return delegate.getQProfilesByLanguage();
   }
 
+  @Override
+  public MutableAnalysisMetadataHolder setScannerPluginsByKey(Map<String, ScannerPlugin> plugins) {
+    delegate.setScannerPluginsByKey(plugins);
+    return this;
+  }
+
+  @Override
+  public Map<String, ScannerPlugin> getScannerPluginsByKey() {
+    return delegate.getScannerPluginsByKey();
+  }
+
   @Override
   public boolean isIncrementalAnalysis() {
     return delegate.isIncrementalAnalysis();
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/ScannerPluginTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/ScannerPluginTest.java
new file mode 100644 (file)
index 0000000..1322d25
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.server.computation.task.projectanalysis.analysis;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ScannerPluginTest {
+
+  @Test
+  public void verify_getters() {
+    ScannerPlugin plugin = new ScannerPlugin("key", 12345L);
+
+    assertThat(plugin.getKey()).isEqualTo("key");
+    assertThat(plugin.getUpdatedAt()).isEqualTo(12345L);
+  }
+
+  @Test
+  public void verify_toString() {
+    ScannerPlugin plugin = new ScannerPlugin("key", 12345L);
+
+    assertThat(plugin.toString()).isEqualTo("ScannerPlugin{key='key', updatedAt='12345'}");
+  }
+
+  @Test
+  public void equals_is_based_on_key_only() {
+    ScannerPlugin plugin = new ScannerPlugin("key", 12345L);
+
+    assertThat(plugin).isEqualTo(plugin);
+    assertThat(plugin).isEqualTo(new ScannerPlugin("key", 45678L));
+    assertThat(plugin).isNotEqualTo(new ScannerPlugin("key2", 12345L));
+    assertThat(plugin).isNotEqualTo(null);
+    assertThat(plugin).isNotEqualTo("toto");
+  }
+
+  @Test
+  public void hashcode_is_based_on_key_only() {
+    ScannerPlugin plugin = new ScannerPlugin("key", 12345L);
+
+    assertThat(plugin.hashCode()).isEqualTo("key".hashCode());
+  }
+}
index df2652b0954a0caca73fcda0efd6c5089aae0549..e797cda3baf5d8a86196a5f694c7ce78f9505894 100644 (file)
@@ -37,6 +37,7 @@ public class DumbRule implements Rule {
   private RuleType type = RuleType.CODE_SMELL;
   private Set<String> tags = new HashSet<>();
   private DebtRemediationFunction function;
+  private String pluginKey;
 
   public DumbRule(RuleKey key) {
     this.key = key;
@@ -78,6 +79,11 @@ public class DumbRule implements Rule {
     return function;
   }
 
+  @Override
+  public String getPluginKey() {
+    return pluginKey;
+  }
+
   public DumbRule setId(Integer id) {
     this.id = id;
     return this;
@@ -98,11 +104,19 @@ public class DumbRule implements Rule {
     return this;
   }
 
-  public void setTags(Set<String> tags) {
+  public DumbRule setTags(Set<String> tags) {
     this.tags = tags;
+    return this;
   }
 
-  public void setType(RuleType type) {
+  public DumbRule setType(RuleType type) {
     this.type = type;
+    return this;
   }
+
+  public DumbRule setPluginKey(String pluginKey) {
+    this.pluginKey = pluginKey;
+    return this;
+  }
+
 }
index 1e2ffcfd40df628b43c29ce6344dc4fd928b5c90..aef9e5af5b0d24ec6103cd396b4ffd536023a97d 100644 (file)
@@ -21,12 +21,15 @@ package org.sonar.server.computation.task.projectanalysis.issue;
 
 import com.google.common.base.Optional;
 import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
 import org.junit.Before;
 import org.junit.Test;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.core.issue.DefaultIssue;
 import org.sonar.server.computation.task.projectanalysis.analysis.Analysis;
 import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolder;
+import org.sonar.server.computation.task.projectanalysis.analysis.ScannerPlugin;
 import org.sonar.server.computation.task.projectanalysis.component.Component;
 import org.sonar.server.computation.task.projectanalysis.qualityprofile.ActiveRule;
 import org.sonar.server.computation.task.projectanalysis.qualityprofile.ActiveRulesHolder;
@@ -56,10 +59,13 @@ public class IssueCreationDateCalculatorTest {
   private ActiveRule activeRule;
   private IssueCreationDateCalculator calculator;
   private Analysis baseAnalysis;
+  private Map<String, ScannerPlugin> scannerPlugins;
 
   @Before
   public void before() {
     analysisMetadataHolder = mock(AnalysisMetadataHolder.class);
+    scannerPlugins = new HashMap<>();
+    when(analysisMetadataHolder.getScannerPluginsByKey()).thenReturn(scannerPlugins);
     scmInfoRepository = mock(ScmInfoRepository.class);
     issueUpdater = mock(IssueFieldsSetter.class);
     activeRulesHolder = mock(ActiveRulesHolder.class);
@@ -70,6 +76,8 @@ public class IssueCreationDateCalculatorTest {
     baseAnalysis = mock(Analysis.class);
     calculator = new IssueCreationDateCalculator(analysisMetadataHolder, scmInfoRepository, issueUpdater, activeRulesHolder);
 
+    when(activeRulesHolder.get(any(RuleKey.class)))
+      .thenReturn(Optional.absent());
     when(activeRulesHolder.get(ruleKey))
       .thenReturn(Optional.of(activeRule));
     when(issue.getRuleKey())
@@ -83,7 +91,7 @@ public class IssueCreationDateCalculatorTest {
 
     newIssue();
     noScm();
-    rule(2800L);
+    ruleCreatedAt(2800L);
 
     run();
 
@@ -97,7 +105,9 @@ public class IssueCreationDateCalculatorTest {
 
     newIssue();
     withScm(1200L);
-    rule(1500L);
+    ruleCreatedAt(1500L);
+    rulePlugin("java");
+    pluginUpdatedAt("java", 1700L);
 
     run();
 
@@ -111,7 +121,21 @@ public class IssueCreationDateCalculatorTest {
 
     existingIssue();
     withScm(1200L);
-    rule(2800L);
+    ruleCreatedAt(2800L);
+
+    run();
+
+    assertNoChangeOfCreationDate();
+  }
+
+  @Test
+  public void should_not_fail_for_issue_about_to_be_closed() {
+    previousAnalysisWas(2000L);
+    currentAnalysisIs(3000L);
+
+    existingIssue();
+    when(issue.getRuleKey())
+      .thenReturn(RuleKey.of("repo", "disabled"));
 
     run();
 
@@ -125,7 +149,35 @@ public class IssueCreationDateCalculatorTest {
 
     newIssue();
     withScm(1200L);
-    rule(2800L);
+    ruleCreatedAt(2800L);
+
+    run();
+
+    assertChangeOfCreationDateTo(1200L);
+  }
+
+  @Test
+  public void should_change_date_if_scm_is_available_and_first_analysis() {
+    currentAnalysisIs(3000L);
+
+    newIssue();
+    withScm(1200L);
+
+    run();
+
+    assertChangeOfCreationDateTo(1200L);
+  }
+
+  @Test
+  public void should_change_date_if_scm_is_available_and_plugin_is_new() {
+    previousAnalysisWas(2000L);
+    currentAnalysisIs(3000L);
+
+    newIssue();
+    withScm(1200L);
+    ruleCreatedAt(1500L);
+    rulePlugin("java");
+    pluginUpdatedAt("java", 2500L);
 
     run();
 
@@ -139,6 +191,10 @@ public class IssueCreationDateCalculatorTest {
       .thenReturn(analysisDate);
   }
 
+  private void pluginUpdatedAt(String pluginKey, long updatedAt) {
+    scannerPlugins.put(pluginKey, new ScannerPlugin(pluginKey, updatedAt));
+  }
+
   private void currentAnalysisIs(long analysisDate) {
     when(analysisMetadataHolder.getAnalysisDate()).thenReturn(analysisDate);
   }
@@ -166,10 +222,14 @@ public class IssueCreationDateCalculatorTest {
     when(scmInfo.getLatestChangeset()).thenReturn(changeset);
   }
 
-  private void rule(long createdAt) {
+  private void ruleCreatedAt(long createdAt) {
     when(activeRule.getCreatedAt()).thenReturn(createdAt);
   }
 
+  private void rulePlugin(String pluginKey) {
+    when(activeRule.getPluginKey()).thenReturn(pluginKey);
+  }
+
   private void run() {
     calculator.beforeComponent(component);
     calculator.onIssue(component, issue);
index a5296b2dca043958ef29186b40624b5b55530fbb..32385bee3c8129a192f77be6369292d433427dec 100644 (file)
@@ -43,10 +43,12 @@ import static org.sonar.server.computation.task.projectanalysis.component.Report
 
 public class CommentDensityRuleTest {
 
-  static RuleKey RULE_KEY = RuleKey.of(CommonRuleKeys.commonRepositoryForLang("java"), CommonRuleKeys.INSUFFICIENT_COMMENT_DENSITY);
+  private static final String PLUGIN_KEY = "java";
+
+  static RuleKey RULE_KEY = RuleKey.of(CommonRuleKeys.commonRepositoryForLang(PLUGIN_KEY), CommonRuleKeys.INSUFFICIENT_COMMENT_DENSITY);
 
   static ReportComponent FILE = ReportComponent.builder(Component.Type.FILE, 1)
-    .setFileAttributes(new FileAttributes(false, "java", 1))
+    .setFileAttributes(new FileAttributes(false, PLUGIN_KEY, 1))
     .build();
 
   @Rule
@@ -71,22 +73,22 @@ public class CommentDensityRuleTest {
 
   @Test
   public void no_issues_if_enough_comments() {
-    activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, ImmutableMap.of(CommonRuleKeys.INSUFFICIENT_COMMENT_DENSITY_PROPERTY, "25"), 1_000L));
+    activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, ImmutableMap.of(CommonRuleKeys.INSUFFICIENT_COMMENT_DENSITY_PROPERTY, "25"), 1_000L, PLUGIN_KEY));
     measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.COMMENT_LINES_DENSITY_KEY, Measure.newMeasureBuilder().create(90.0, 1));
 
-    DefaultIssue issue = underTest.processFile(FILE, "java");
+    DefaultIssue issue = underTest.processFile(FILE, PLUGIN_KEY);
 
     assertThat(issue).isNull();
   }
 
   @Test
   public void issue_if_not_enough_comments() {
-    activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, ImmutableMap.of(CommonRuleKeys.INSUFFICIENT_COMMENT_DENSITY_PROPERTY, "25"), 1_000L));
+    activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, ImmutableMap.of(CommonRuleKeys.INSUFFICIENT_COMMENT_DENSITY_PROPERTY, "25"), 1_000L, PLUGIN_KEY));
     measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.COMMENT_LINES_DENSITY_KEY, Measure.newMeasureBuilder().create(10.0, 1));
     measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.COMMENT_LINES_KEY, Measure.newMeasureBuilder().create(40));
     measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.NCLOC_KEY, Measure.newMeasureBuilder().create(360));
 
-    DefaultIssue issue = underTest.processFile(FILE, "java");
+    DefaultIssue issue = underTest.processFile(FILE, PLUGIN_KEY);
 
     assertThat(issue.ruleKey()).isEqualTo(RULE_KEY);
     assertThat(issue.severity()).isEqualTo(Severity.CRITICAL);
@@ -98,12 +100,12 @@ public class CommentDensityRuleTest {
 
   @Test
   public void issue_if_not_enough_comments__test_ceil() {
-    activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, ImmutableMap.of(CommonRuleKeys.INSUFFICIENT_COMMENT_DENSITY_PROPERTY, "25"), 1_000L));
+    activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, ImmutableMap.of(CommonRuleKeys.INSUFFICIENT_COMMENT_DENSITY_PROPERTY, "25"), 1_000L, PLUGIN_KEY));
     measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.COMMENT_LINES_DENSITY_KEY, Measure.newMeasureBuilder().create(0.0, 1));
     measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.COMMENT_LINES_KEY, Measure.newMeasureBuilder().create(0));
     measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.NCLOC_KEY, Measure.newMeasureBuilder().create(1));
 
-    DefaultIssue issue = underTest.processFile(FILE, "java");
+    DefaultIssue issue = underTest.processFile(FILE, PLUGIN_KEY);
 
     assertThat(issue.ruleKey()).isEqualTo(RULE_KEY);
     assertThat(issue.severity()).isEqualTo(Severity.CRITICAL);
@@ -120,11 +122,11 @@ public class CommentDensityRuleTest {
     thrown.expect(IllegalStateException.class);
     thrown.expectMessage("Minimum density of rule [common-java:InsufficientCommentDensity] is incorrect. Got [100] but must be strictly less than 100.");
 
-    activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, ImmutableMap.of(CommonRuleKeys.INSUFFICIENT_COMMENT_DENSITY_PROPERTY, "100"), 1_000L));
+    activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, ImmutableMap.of(CommonRuleKeys.INSUFFICIENT_COMMENT_DENSITY_PROPERTY, "100"), 1_000L, PLUGIN_KEY));
     measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.COMMENT_LINES_DENSITY_KEY, Measure.newMeasureBuilder().create(0.0, 1));
     measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.COMMENT_LINES_KEY, Measure.newMeasureBuilder().create(0));
     measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.NCLOC_KEY, Measure.newMeasureBuilder().create(1));
 
-    underTest.processFile(FILE, "java");
+    underTest.processFile(FILE, PLUGIN_KEY);
   }
 }
index 99bc76e5e7db9304d7698cfd33e2f9003a20f4d4..71f16a14584f5c27539107e43b3a712bebdc573c 100644 (file)
@@ -31,12 +31,14 @@ import static org.assertj.core.api.Assertions.assertThat;
 
 public class CommonRuleTest {
 
+  private static final String PLUGIN_KEY = "java";
+
   @Rule
   public ExpectedException thrown = ExpectedException.none();
 
   @Test
   public void test_getMinDensityParam() throws Exception {
-    ActiveRule activeRule = new ActiveRule(RuleTesting.XOO_X1, Severity.MAJOR, ImmutableMap.of("minDensity", "30.5"), 1_000L);
+    ActiveRule activeRule = new ActiveRule(RuleTesting.XOO_X1, Severity.MAJOR, ImmutableMap.of("minDensity", "30.5"), 1_000L, PLUGIN_KEY);
     double minDensity = CommonRule.getMinDensityParam(activeRule, "minDensity");
 
     assertThat(minDensity).isEqualTo(30.5);
@@ -47,7 +49,7 @@ public class CommonRuleTest {
     thrown.expect(IllegalStateException.class);
     thrown.expectMessage("Required parameter [minDensity] is missing on rule [xoo:x1]");
 
-    ActiveRule activeRule = new ActiveRule(RuleTesting.XOO_X1, Severity.MAJOR, ImmutableMap.<String, String>of(), 1_000L);
+    ActiveRule activeRule = new ActiveRule(RuleTesting.XOO_X1, Severity.MAJOR, ImmutableMap.<String, String>of(), 1_000L, PLUGIN_KEY);
     CommonRule.getMinDensityParam(activeRule, "minDensity");
   }
 
@@ -56,7 +58,7 @@ public class CommonRuleTest {
     thrown.expect(IllegalStateException.class);
     thrown.expectMessage("Minimum density of rule [xoo:x1] is incorrect. Got [-30.5] but must be between 0 and 100.");
 
-    ActiveRule activeRule = new ActiveRule(RuleTesting.XOO_X1, Severity.MAJOR, ImmutableMap.of("minDensity", "-30.5"), 1_000L);
+    ActiveRule activeRule = new ActiveRule(RuleTesting.XOO_X1, Severity.MAJOR, ImmutableMap.of("minDensity", "-30.5"), 1_000L, PLUGIN_KEY);
     CommonRule.getMinDensityParam(activeRule, "minDensity");
   }
 
@@ -65,7 +67,7 @@ public class CommonRuleTest {
     thrown.expect(IllegalStateException.class);
     thrown.expectMessage("Minimum density of rule [xoo:x1] is incorrect. Got [305] but must be between 0 and 100.");
 
-    ActiveRule activeRule = new ActiveRule(RuleTesting.XOO_X1, Severity.MAJOR, ImmutableMap.of("minDensity", "305"), 1_000L);
+    ActiveRule activeRule = new ActiveRule(RuleTesting.XOO_X1, Severity.MAJOR, ImmutableMap.of("minDensity", "305"), 1_000L, PLUGIN_KEY);
     CommonRule.getMinDensityParam(activeRule, "minDensity");
   }
 }
index 6cef16c1a6d88ea8c41054408d34324deebab6fc..ab4351ba8ceebe62858bd3cba25ff7552520134d 100644 (file)
@@ -42,6 +42,8 @@ import static org.sonar.server.computation.task.projectanalysis.component.Report
 
 public abstract class CoverageRuleTest {
 
+  private static final String PLUGIN_KEY = "java";
+
   static ReportComponent FILE = ReportComponent.builder(Component.Type.FILE, 1)
     .setFileAttributes(new FileAttributes(false, "java", 1))
     .build();
@@ -85,7 +87,7 @@ public abstract class CoverageRuleTest {
 
   @Test
   public void no_issue_if_enough_coverage() {
-    activeRuleHolder.put(new ActiveRule(getRuleKey(), Severity.CRITICAL, ImmutableMap.of(getMinPropertyKey(), "65"), 1_000L));
+    activeRuleHolder.put(new ActiveRule(getRuleKey(), Severity.CRITICAL, ImmutableMap.of(getMinPropertyKey(), "65"), 1_000L, PLUGIN_KEY));
     measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), getCoverageMetricKey(), Measure.newMeasureBuilder().create(90.0, 1));
 
     DefaultIssue issue = underTest.processFile(FILE, "java");
@@ -95,7 +97,7 @@ public abstract class CoverageRuleTest {
 
   @Test
   public void issue_if_coverage_is_too_low() {
-    activeRuleHolder.put(new ActiveRule(getRuleKey(), Severity.CRITICAL, ImmutableMap.of(getMinPropertyKey(), "65"), 1_000L));
+    activeRuleHolder.put(new ActiveRule(getRuleKey(), Severity.CRITICAL, ImmutableMap.of(getMinPropertyKey(), "65"), 1_000L, PLUGIN_KEY));
     measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), getCoverageMetricKey(), Measure.newMeasureBuilder().create(20.0, 1));
     measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), getUncoveredMetricKey(), Measure.newMeasureBuilder().create(40));
     measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), getToCoverMetricKey(), Measure.newMeasureBuilder().create(50));
@@ -113,7 +115,7 @@ public abstract class CoverageRuleTest {
 
   @Test
   public void no_issue_if_coverage_is_not_set() {
-    activeRuleHolder.put(new ActiveRule(getRuleKey(), Severity.CRITICAL, ImmutableMap.of(getMinPropertyKey(), "65"), 1_000L));
+    activeRuleHolder.put(new ActiveRule(getRuleKey(), Severity.CRITICAL, ImmutableMap.of(getMinPropertyKey(), "65"), 1_000L, PLUGIN_KEY));
 
     DefaultIssue issue = underTest.processFile(FILE, "java");
 
index 78dffc688bf7b8c042ce34a2a7d8854ef451ee0e..44156f51ff68e3dc16cbf1ae24b51e9fd3ee0abf 100644 (file)
@@ -42,6 +42,8 @@ import static org.sonar.server.computation.task.projectanalysis.component.Report
 
 public class DuplicatedBlockRuleTest {
 
+  private static final String PLUGIN_KEY = "java";
+
   static RuleKey RULE_KEY = RuleKey.of(CommonRuleKeys.commonRepositoryForLang("java"), CommonRuleKeys.DUPLICATED_BLOCKS);
 
   static ReportComponent FILE = ReportComponent.builder(Component.Type.FILE, 1)
@@ -65,7 +67,7 @@ public class DuplicatedBlockRuleTest {
 
   @Test
   public void no_issue_if_no_duplicated_blocks() throws Exception {
-    activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, Collections.<String, String>emptyMap(), 1_000L));
+    activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, Collections.<String, String>emptyMap(), 1_000L, PLUGIN_KEY));
     measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.DUPLICATED_BLOCKS_KEY, Measure.newMeasureBuilder().create(0));
 
     DefaultIssue issue = underTest.processFile(FILE, "java");
@@ -75,7 +77,7 @@ public class DuplicatedBlockRuleTest {
 
   @Test
   public void issue_if_duplicated_blocks() throws Exception {
-    activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, Collections.<String, String>emptyMap(), 1_000L));
+    activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, Collections.<String, String>emptyMap(), 1_000L, PLUGIN_KEY));
     measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.DUPLICATED_BLOCKS_KEY, Measure.newMeasureBuilder().create(3));
 
     DefaultIssue issue = underTest.processFile(FILE, "java");
index 14507e2241b29680093e532825dee1e4a29e1d1a..3644451971298c299548d85eb256518293cc8eac 100644 (file)
@@ -42,6 +42,8 @@ import static org.sonar.server.computation.task.projectanalysis.component.Report
 
 public class SkippedTestRuleTest {
 
+  private static final String PLUGIN_KEY = "java";
+
   static RuleKey RULE_KEY = RuleKey.of(CommonRuleKeys.commonRepositoryForLang("java"), CommonRuleKeys.SKIPPED_UNIT_TESTS);
 
   static ReportComponent FILE = ReportComponent.builder(Component.Type.FILE, 1)
@@ -66,7 +68,7 @@ public class SkippedTestRuleTest {
 
   @Test
   public void issue_if_skipped_tests() throws Exception {
-    activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, Collections.<String, String>emptyMap(), 1_000L));
+    activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, Collections.<String, String>emptyMap(), 1_000L, PLUGIN_KEY));
     measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.SKIPPED_TESTS_KEY, Measure.newMeasureBuilder().create(2));
 
     DefaultIssue issue = underTest.processFile(FILE, "java");
@@ -79,7 +81,7 @@ public class SkippedTestRuleTest {
 
   @Test
   public void no_issues_if_zero_skipped_tests() throws Exception {
-    activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, Collections.<String, String>emptyMap(), 1_000L));
+    activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, Collections.<String, String>emptyMap(), 1_000L, PLUGIN_KEY));
     measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.SKIPPED_TESTS_KEY, Measure.newMeasureBuilder().create(0));
 
     DefaultIssue issue = underTest.processFile(FILE, "java");
@@ -89,7 +91,7 @@ public class SkippedTestRuleTest {
 
   @Test
   public void no_issues_if_measure_is_absent() throws Exception {
-    activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, Collections.<String, String>emptyMap(), 1_000L));
+    activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, Collections.<String, String>emptyMap(), 1_000L, PLUGIN_KEY));
 
     DefaultIssue issue = underTest.processFile(FILE, "java");
 
index 81cc3e87ad9910dc59549c80d54f1b7f7ac05948..abe61b1e605fbb81b7822f4b828442b761764f89 100644 (file)
@@ -42,6 +42,8 @@ import static org.sonar.server.computation.task.projectanalysis.component.Report
 
 public class TestErrorRuleTest {
 
+  private static final String PLUGIN_KEY = "java";
+
   static RuleKey RULE_KEY = RuleKey.of(CommonRuleKeys.commonRepositoryForLang("java"), CommonRuleKeys.FAILED_UNIT_TESTS);
 
   static ReportComponent FILE = ReportComponent.builder(Component.Type.FILE, 1)
@@ -67,7 +69,7 @@ public class TestErrorRuleTest {
 
   @Test
   public void issue_if_errors_or_failures() throws Exception {
-    activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, Collections.<String, String>emptyMap(), 1_000L));
+    activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, Collections.<String, String>emptyMap(), 1_000L, PLUGIN_KEY));
     measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.TEST_ERRORS_KEY, Measure.newMeasureBuilder().create(2));
     measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.TEST_FAILURES_KEY, Measure.newMeasureBuilder().create(1));
 
@@ -81,7 +83,7 @@ public class TestErrorRuleTest {
 
   @Test
   public void no_issues_if_zero_errors_and_failures() throws Exception {
-    activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, Collections.<String, String>emptyMap(), 1_000L));
+    activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, Collections.<String, String>emptyMap(), 1_000L, PLUGIN_KEY));
     measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.TEST_ERRORS_KEY, Measure.newMeasureBuilder().create(0));
     measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.TEST_FAILURES_KEY, Measure.newMeasureBuilder().create(0));
 
@@ -92,7 +94,7 @@ public class TestErrorRuleTest {
 
   @Test
   public void no_issues_if_test_measures_are_absent() throws Exception {
-    activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, Collections.<String, String>emptyMap(), 1_000L));
+    activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, Collections.<String, String>emptyMap(), 1_000L, PLUGIN_KEY));
 
     DefaultIssue issue = underTest.processFile(FILE, "java");
 
index 404668ad8418fb20d2d012bd5879984cec4279ca..f108aec008573779d9cb975bca17dfe439310148 100644 (file)
@@ -32,9 +32,11 @@ import static org.assertj.core.api.Assertions.assertThat;
 
 public class ActiveRulesHolderImplTest {
 
+  private static final String PLUGIN_KEY = "java";
+
   private static final long SOME_DATE = 1_000L;
 
-  static final RuleKey RULE_KEY = RuleKey.of("java", "S001");
+  static final RuleKey RULE_KEY = RuleKey.of("squid", "S001");
 
   @Rule
   public ExpectedException thrown = ExpectedException.none();
@@ -50,7 +52,7 @@ public class ActiveRulesHolderImplTest {
 
   @Test
   public void get_active_rule() throws Exception {
-    underTest.set(asList(new ActiveRule(RULE_KEY, Severity.BLOCKER, Collections.<String, String>emptyMap(), SOME_DATE)));
+    underTest.set(asList(new ActiveRule(RULE_KEY, Severity.BLOCKER, Collections.<String, String>emptyMap(), SOME_DATE, PLUGIN_KEY)));
 
     Optional<ActiveRule> activeRule = underTest.get(RULE_KEY);
     assertThat(activeRule.isPresent()).isTrue();
@@ -63,7 +65,7 @@ public class ActiveRulesHolderImplTest {
     thrown.expect(IllegalStateException.class);
     thrown.expectMessage("Active rules have already been initialized");
 
-    underTest.set(asList(new ActiveRule(RULE_KEY, Severity.BLOCKER, Collections.<String, String>emptyMap(), 1_000L)));
+    underTest.set(asList(new ActiveRule(RULE_KEY, Severity.BLOCKER, Collections.<String, String>emptyMap(), 1_000L, PLUGIN_KEY)));
     underTest.set(Collections.<ActiveRule>emptyList());
 
   }
@@ -79,10 +81,10 @@ public class ActiveRulesHolderImplTest {
   @Test
   public void can_not_set_duplicated_rules() throws Exception {
     thrown.expect(IllegalArgumentException.class);
-    thrown.expectMessage("Active rule must not be declared multiple times: java:S001");
+    thrown.expectMessage("Active rule must not be declared multiple times: squid:S001");
 
     underTest.set(asList(
-      new ActiveRule(RULE_KEY, Severity.BLOCKER, Collections.<String, String>emptyMap(), 1_000L),
-      new ActiveRule(RULE_KEY, Severity.MAJOR, Collections.<String, String>emptyMap(), 1_000L)));
+      new ActiveRule(RULE_KEY, Severity.BLOCKER, Collections.<String, String>emptyMap(), 1_000L, PLUGIN_KEY),
+      new ActiveRule(RULE_KEY, Severity.MAJOR, Collections.<String, String>emptyMap(), 1_000L, PLUGIN_KEY)));
   }
 }
index 5b3da4eb2ef20812f08fb783d800e50ea5d5af4b..132828a6a160ebaefffcbcc48fd0fdb17d46508b 100644 (file)
@@ -51,8 +51,10 @@ public class LoadQualityProfilesStepTest {
 
   @Test
   public void feed_active_rules() throws Exception {
-    ruleRepository.add(XOO_X1);
-    ruleRepository.add(XOO_X2);
+    ruleRepository.add(XOO_X1)
+      .setPluginKey("xoo");
+    ruleRepository.add(XOO_X2)
+      .setPluginKey("xoo");
 
     ScannerReport.ActiveRule.Builder batch1 = ScannerReport.ActiveRule.newBuilder()
       .setRuleRepository(XOO_X1.repository()).setRuleKey(XOO_X1.rule())
@@ -70,10 +72,12 @@ public class LoadQualityProfilesStepTest {
     Optional<ActiveRule> ar1 = activeRulesHolder.get(XOO_X1);
     assertThat(ar1.get().getSeverity()).isEqualTo(Severity.BLOCKER);
     assertThat(ar1.get().getParams()).containsExactly(MapEntry.entry("p1", "v1"));
+    assertThat(ar1.get().getPluginKey()).isEqualTo("xoo");
 
     Optional<ActiveRule> ar2 = activeRulesHolder.get(XOO_X2);
     assertThat(ar2.get().getSeverity()).isEqualTo(Severity.MAJOR);
     assertThat(ar2.get().getParams()).isEmpty();
+    assertThat(ar2.get().getPluginKey()).isEqualTo("xoo");
   }
 
   @Test
index 3169ada19ed85f65e6c8a8e51489d68613306888..589c6d5f2a68110e5edec1406daa0be7d004e04c 100644 (file)
@@ -42,6 +42,7 @@ import org.sonar.server.organization.DefaultOrganizationProvider;
 import org.sonar.server.organization.TestDefaultOrganizationProvider;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
@@ -147,7 +148,7 @@ public class LoadReportAnalysisMetadataHolderStepTest {
 
     assertThat(analysisMetadataHolder.isCrossProjectDuplicationEnabled()).isEqualTo(false);
   }
-  
+
   @Test
   public void set_incremental_analysis_to_true() {
     reportReader.setMetadata(
@@ -159,12 +160,12 @@ public class LoadReportAnalysisMetadataHolderStepTest {
 
     assertThat(analysisMetadataHolder.isIncrementalAnalysis()).isTrue();
   }
-  
+
   @Test
   public void set_incremental_analysis_to_false() {
     reportReader.setMetadata(
       newBatchReportBuilder()
-      .setIncremental(false)
+        .setIncremental(false)
         .build());
 
     underTest.execute();
@@ -172,7 +173,6 @@ public class LoadReportAnalysisMetadataHolderStepTest {
     assertThat(analysisMetadataHolder.isIncrementalAnalysis()).isFalse();
   }
 
-
   @Test
   public void set_cross_project_duplication_to_false_when_nothing_in_the_report() {
     reportReader.setMetadata(
@@ -373,6 +373,21 @@ public class LoadReportAnalysisMetadataHolderStepTest {
     assertThat(argumentCaptor.getValue().getUuid()).isEqualTo(organization.getUuid());
   }
 
+  @Test
+  public void execute_read_plugins_from_report() {
+    ScannerReport.Metadata.Builder metadataBuilder = newBatchReportBuilder();
+    metadataBuilder.getMutablePluginsByKey().put("java", ScannerReport.Metadata.Plugin.newBuilder().setKey("java").setUpdatedAt(12345L).build());
+    metadataBuilder.getMutablePluginsByKey().put("php", ScannerReport.Metadata.Plugin.newBuilder().setKey("php").setUpdatedAt(678910L).build());
+    reportReader.setMetadata(metadataBuilder.build());
+
+    underTest.execute();
+
+    assertThat(analysisMetadataHolder.getScannerPluginsByKey()).containsOnlyKeys("java", "php");
+    assertThat(analysisMetadataHolder.getScannerPluginsByKey().values()).extracting("key", "updatedAt").containsOnly(
+      tuple("java", 12345L),
+      tuple("php", 678910L));
+  }
+
   private LoadReportAnalysisMetadataHolderStep createStep(CeTask ceTask) {
     return new LoadReportAnalysisMetadataHolderStep(ceTask, reportReader, analysisMetadataHolder, defaultOrganizationProvider, dbClient, billingValidations);
   }