]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-10544 Load external rules
authorDuarte Meneses <duarte.meneses@sonarsource.com>
Mon, 9 Apr 2018 15:46:48 +0000 (17:46 +0200)
committerSonarTech <sonartech@sonarsource.com>
Thu, 26 Apr 2018 18:20:50 +0000 (20:20 +0200)
14 files changed:
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/NewExternalRule.java [new file with mode: 0644]
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/issue/RuleRepository.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/RuleRepositoryImpl.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/RuleTagsCopier.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/RuleTypeCopier.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/TrackerRawInputFactory.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/LoadQualityProfilesStep.java
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/IntegrateIssuesVisitorTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/NewExternalRuleTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/RuleRepositoryRule.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/TrackerRawInputFactoryTest.java

diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/NewExternalRule.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/NewExternalRule.java
new file mode 100644 (file)
index 0000000..a74632d
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.issue;
+
+import java.util.Collections;
+import java.util.Set;
+import javax.annotation.CheckForNull;
+import javax.annotation.concurrent.Immutable;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rule.RuleStatus;
+import org.sonar.api.rules.RuleType;
+import org.sonar.api.server.debt.DebtRemediationFunction;
+
+@Immutable
+public class NewExternalRule implements Rule {
+  private final RuleKey key;
+  private final String name;
+  private final String descriptionUrl;
+  private final String severity;
+  private final RuleType type;
+
+  private NewExternalRule(Builder builder) {
+    this.key = checkNotNull(builder.key, "key");
+    this.name = checkNotEmpty(builder.name, "name");
+    this.descriptionUrl = builder.descriptionUrl;
+    this.severity = checkNotEmpty(builder.severity, "severity");
+    this.type = checkNotNull(builder.type, "type");
+  }
+
+  private static String checkNotEmpty(String str, String name) {
+    if (StringUtils.isEmpty(str)) {
+      throw new IllegalStateException("'" + name + "' not expected to be empty for an external rule");
+    }
+    return str;
+  }
+
+  private static <T> T checkNotNull(T obj, String name) {
+    if (obj == null) {
+      throw new IllegalStateException("'" + name + "' not expected to be null for an external rule");
+    }
+    return obj;
+  }
+
+  @CheckForNull
+  public String getDescriptionUrl() {
+    return descriptionUrl;
+  }
+
+  public String getSeverity() {
+    return severity;
+  }
+
+  @Override
+  public int getId() {
+    return 0;
+  }
+
+  @Override
+  public RuleKey getKey() {
+    return key;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+
+  @Override
+  public RuleStatus getStatus() {
+    return RuleStatus.defaultStatus();
+  }
+
+  @Override
+  public RuleType getType() {
+    return type;
+  }
+
+  @Override
+  public boolean isExternal() {
+    return true;
+  }
+
+  @Override
+  public Set<String> getTags() {
+    return Collections.emptySet();
+  }
+
+  @Override
+  public DebtRemediationFunction getRemediationFunction() {
+    return null;
+  }
+
+  @Override
+  public String getPluginKey() {
+    return null;
+  }
+
+  public static class Builder {
+    private RuleKey key;
+    private String name;
+    private String descriptionUrl;
+    private String severity;
+    private RuleType type;
+
+    public Builder setKey(RuleKey key) {
+      this.key = key;
+      return this;
+    }
+
+    public Builder setName(String name) {
+      this.name = StringUtils.trimToNull(name);
+      return this;
+    }
+
+    public Builder setDescriptionUrl(String descriptionUrl) {
+      this.descriptionUrl = StringUtils.trimToNull(descriptionUrl);
+      return this;
+    }
+
+    public Builder setSeverity(String severity) {
+      this.severity = StringUtils.trimToNull(severity);
+      return this;
+    }
+
+    public Builder setType(RuleType type) {
+      this.type = type;
+      return this;
+    }
+
+    public String name() {
+      return name;
+    }
+
+    public String descriptionUrl() {
+      return descriptionUrl;
+    }
+
+    public String severity() {
+      return severity;
+    }
+
+    public RuleType type() {
+      return type;
+    }
+
+    public NewExternalRule build() {
+      return new NewExternalRule(this);
+    }
+  }
+}
index 00cf9755c345938bad0359f3fcc1048e137449b7..a3a56a743c0aabaaa00076ac1be6bca1ce57cd68 100644 (file)
@@ -37,6 +37,8 @@ public interface Rule {
   RuleStatus getStatus();
 
   RuleType getType();
+  
+  boolean isExternal();
 
   /**
    * Get all tags, whatever system or user tags.
index 097e15301b13f00b3213afa670b3637b23b54eaa..4762cef06292e4b8ccd823def805656b3fb790cd 100644 (file)
@@ -44,6 +44,7 @@ public class RuleImpl implements Rule {
   private final DebtRemediationFunction remediationFunction;
   private final RuleType type;
   private final String pluginKey;
+  private final boolean external;
 
   public RuleImpl(RuleDto dto) {
     this.id = dto.getId();
@@ -54,6 +55,8 @@ public class RuleImpl implements Rule {
     this.remediationFunction = effectiveRemediationFunction(dto);
     this.type = RuleType.valueOf(dto.getType());
     this.pluginKey = dto.getPluginKey();
+    // TODO
+    this.external = false;
   }
 
   @Override
@@ -138,4 +141,9 @@ public class RuleImpl implements Rule {
     }
     return null;
   }
+
+  @Override
+  public boolean isExternal() {
+    return external;
+  }
 }
index 8bf1e494d196b3d2e4556fad5c90a7cccb8865c7..347638cddc4118ff628be87741b1b6a891c703e2 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.server.computation.task.projectanalysis.issue;
 
 import java.util.Optional;
+import java.util.function.Supplier;
 import org.sonar.api.rule.RuleKey;
 
 /**
@@ -44,4 +45,6 @@ public interface RuleRepository {
   Optional<Rule> findByKey(RuleKey key);
 
   Optional<Rule> findById(int id);
+  
+  void insertNewExternalRuleIfAbsent(RuleKey ruleKey, Supplier<NewExternalRule> ruleSupplier);
 }
index 347cea214d042d6df76d6e71417fb82de2b2dce6..e7f3cd61d80a323b47015206be7ef65689f63834 100644 (file)
@@ -21,8 +21,10 @@ package org.sonar.server.computation.task.projectanalysis.issue;
 
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Multimap;
+import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Optional;
+import java.util.function.Supplier;
 import javax.annotation.CheckForNull;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.core.util.stream.MoreCollectors;
@@ -41,6 +43,8 @@ public class RuleRepositoryImpl implements RuleRepository {
   private Map<RuleKey, Rule> rulesByKey;
   @CheckForNull
   private Map<Integer, Rule> rulesById;
+  @CheckForNull
+  private Map<RuleKey, NewExternalRule> newExternalRulesByKey;
 
   private final DbClient dbClient;
   private final AnalysisMetadataHolder analysisMetadataHolder;
@@ -50,6 +54,12 @@ public class RuleRepositoryImpl implements RuleRepository {
     this.analysisMetadataHolder = analysisMetadataHolder;
   }
 
+  public void insertNewExternalRuleIfAbsent(RuleKey ruleKey, Supplier<NewExternalRule> ruleSupplier) {
+    if (!rulesByKey.containsKey(ruleKey)) {
+      newExternalRulesByKey.computeIfAbsent(ruleKey, s -> ruleSupplier.get());
+    }
+  }
+
   @Override
   public Rule getByKey(RuleKey key) {
     verifyKeyArgument(key);
@@ -112,6 +122,7 @@ public class RuleRepositoryImpl implements RuleRepository {
     }
     this.rulesByKey = rulesByKeyBuilder.build();
     this.rulesById = rulesByIdBuilder.build();
+    this.newExternalRulesByKey = new LinkedHashMap<>();
   }
 
 }
index 78ffaf7654f382cd5e574e3515b17e34e5421589..2f6d220b7a00978093838d64cba5902d22821cdc 100644 (file)
@@ -37,7 +37,9 @@ public class RuleTagsCopier extends IssueVisitor {
     if (issue.isNew()) {
       // analyzer can provide some tags. They must be merged with rule tags
       Rule rule = ruleRepository.getByKey(issue.ruleKey());
-      issue.setTags(union(issue.tags(), rule.getTags()));
+      if (!rule.isExternal()) {
+        issue.setTags(union(issue.tags(), rule.getTags()));
+      }
     }
   }
 }
index d58935099d56b1d5ba602ca183dbc239f02b2ecf..58234a9031aa78a51185da87a6bc3b838c4d88ca 100644 (file)
@@ -34,7 +34,9 @@ public class RuleTypeCopier extends IssueVisitor {
   public void onIssue(Component component, DefaultIssue issue) {
     if (issue.type() == null) {
       Rule rule = ruleRepository.getByKey(issue.ruleKey());
-      issue.setType(rule.getType());
+      if (!rule.isExternal()) {
+        issue.setType(rule.getType());
+      }
     }
   }
 }
index bd2e6f6d396cd4486ea4317d96538873274d1ed4..0bc49a026d6773cf07881b53a3443ffe38144dc4 100644 (file)
@@ -56,14 +56,16 @@ public class TrackerRawInputFactory {
   private final SourceLinesRepository sourceLinesRepository;
   private final CommonRuleEngine commonRuleEngine;
   private final IssueFilter issueFilter;
+  private final RuleRepository ruleRepository;
 
   public TrackerRawInputFactory(TreeRootHolder treeRootHolder, BatchReportReader reportReader,
-    SourceLinesRepository sourceLinesRepository, CommonRuleEngine commonRuleEngine, IssueFilter issueFilter) {
+    SourceLinesRepository sourceLinesRepository, CommonRuleEngine commonRuleEngine, IssueFilter issueFilter, RuleRepository ruleRepository) {
     this.treeRootHolder = treeRootHolder;
     this.reportReader = reportReader;
     this.sourceLinesRepository = sourceLinesRepository;
     this.commonRuleEngine = commonRuleEngine;
     this.issueFilter = issueFilter;
+    this.ruleRepository = ruleRepository;
   }
 
   public Input<DefaultIssue> create(Component component) {
@@ -120,8 +122,7 @@ public class TrackerRawInputFactory {
         // as late as possible
         while (reportIssues.hasNext()) {
           ScannerReport.ExternalIssue reportExternalIssue = reportIssues.next();
-          DefaultIssue issue = toIssue(getLineHashSequence(), reportExternalIssue);
-          result.add(issue);
+          result.add(toExternalIssue(getLineHashSequence(), reportExternalIssue));
         }
       }
 
@@ -171,10 +172,11 @@ public class TrackerRawInputFactory {
       return issue;
     }
 
-    private DefaultIssue toIssue(LineHashSequence lineHashSeq, ScannerReport.ExternalIssue reportIssue) {
+    private DefaultIssue toExternalIssue(LineHashSequence lineHashSeq, ScannerReport.ExternalIssue reportIssue) {
       DefaultIssue issue = new DefaultIssue();
       init(issue);
-      issue.setRuleKey(RuleKey.of(reportIssue.getRuleRepository(), reportIssue.getRuleKey()));
+
+      issue.setRuleKey(RuleKey.of(RuleKey.EXTERNAL_RULE_REPO_PREFIX + reportIssue.getRuleRepository(), reportIssue.getRuleKey()));
       if (reportIssue.hasTextRange()) {
         int startLine = reportIssue.getTextRange().getStartLine();
         issue.setLine(startLine);
@@ -206,10 +208,25 @@ public class TrackerRawInputFactory {
       }
       issue.setLocations(dbLocationsBuilder.build());
       issue.setType(toRuleType(reportIssue.getType()));
-      issue.setDescriptionUrl(StringUtils.stripToNull(reportIssue.getDescriptionUrl()));
+
+      ruleRepository.insertNewExternalRuleIfAbsent(issue.getRuleKey(), () -> toExternalRule(reportIssue));
       return issue;
     }
 
+    private NewExternalRule toExternalRule(ScannerReport.ExternalIssue reportIssue) {
+      NewExternalRule.Builder builder = new NewExternalRule.Builder()
+        .setDescriptionUrl(StringUtils.stripToNull(reportIssue.getDescriptionUrl()))
+        .setType(toRuleType(reportIssue.getType()))
+        .setKey(RuleKey.of(RuleKey.EXTERNAL_RULE_REPO_PREFIX + reportIssue.getRuleRepository(), reportIssue.getRuleKey()))
+        .setPluginKey(reportIssue.getRuleRepository())
+        .setName(reportIssue.getRuleName());
+
+      if (reportIssue.getSeverity() != Severity.UNSET_SEVERITY) {
+        builder.setSeverity(reportIssue.getSeverity().name());
+      }
+      return builder.build();
+    }
+
     private RuleType toRuleType(IssueType type) {
       switch (type) {
         case BUG:
index f0ab906b52699b5d19c98d96c55cb5e238fb9867..4037cbb763cba175397be6637f3a50f898fbd037 100644 (file)
@@ -54,7 +54,7 @@ public class LoadQualityProfilesStep implements ComputationStep {
       while (batchActiveRules.hasNext()) {
         ScannerReport.ActiveRule scannerReportActiveRule = batchActiveRules.next();
         Optional<Rule> rule = ruleRepository.findByKey(RuleKey.of(scannerReportActiveRule.getRuleRepository(), scannerReportActiveRule.getRuleKey()));
-        if (rule.isPresent() && rule.get().getStatus() != RuleStatus.REMOVED) {
+        if (rule.isPresent() && rule.get().getStatus() != RuleStatus.REMOVED && !rule.get().isExternal()) {
           ActiveRule activeRule = convert(scannerReportActiveRule, rule.get());
           activeRules.add(activeRule);
         }
index dc5f9c92796548b5a291899f1dd15238ede9657c..e6066c8c98428776319ab57e9f2834264dc244e0 100644 (file)
@@ -38,6 +38,7 @@ public class DumbRule implements Rule {
   private Set<String> tags = new HashSet<>();
   private DebtRemediationFunction function;
   private String pluginKey;
+  private boolean isExternal;
 
   public DumbRule(RuleKey key) {
     this.key = key;
@@ -84,6 +85,11 @@ public class DumbRule implements Rule {
     return pluginKey;
   }
 
+  @Override
+  public boolean isExternal() {
+    return isExternal;
+  }
+
   public DumbRule setId(Integer id) {
     this.id = id;
     return this;
@@ -119,4 +125,9 @@ public class DumbRule implements Rule {
     return this;
   }
 
+  public DumbRule setIsExtenral(boolean isExternal) {
+    this.isExternal = isExternal;
+    return this;
+  }
+
 }
index 629aad4894b95dfda903ea139ef9f9f8266b1aac..807cc1176b4c939d3431f2da77f0e7896021ee46 100644 (file)
@@ -27,8 +27,6 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
 import org.sonar.api.issue.Issue;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.Severity;
@@ -104,23 +102,17 @@ public class IntegrateIssuesVisitorTest {
   public RuleRepositoryRule ruleRepositoryRule = new RuleRepositoryRule();
   @Rule
   public SourceLinesRepositoryRule fileSourceRepository = new SourceLinesRepositoryRule();
+  @Rule
+  public RuleRepositoryRule ruleRepository = new RuleRepositoryRule();
 
-  @Mock
-  private AnalysisMetadataHolder analysisMetadataHolder;
-  @Mock
-  private IssueFilter issueFilter;
-  @Mock
-  private MovedFilesRepository movedFilesRepository;
-  @Mock
-  private IssueLifecycle issueLifecycle;
-  @Mock
-  private IssueVisitor issueVisitor;
-  @Mock
-  private MergeBranchComponentUuids mergeBranchComponentsUuids;
-  @Mock
-  private ShortBranchIssueMerger issueStatusCopier;
-  @Mock
-  private MergeBranchComponentUuids mergeBranchComponentUuids;
+  private AnalysisMetadataHolder analysisMetadataHolder = mock(AnalysisMetadataHolder.class);
+  private IssueFilter issueFilter = mock(IssueFilter.class);
+  private MovedFilesRepository movedFilesRepository = mock(MovedFilesRepository.class);
+  private IssueLifecycle issueLifecycle = mock(IssueLifecycle.class);
+  private IssueVisitor issueVisitor = mock(IssueVisitor.class);
+  private MergeBranchComponentUuids mergeBranchComponentsUuids = mock(MergeBranchComponentUuids.class);
+  private ShortBranchIssueMerger issueStatusCopier = mock(ShortBranchIssueMerger.class);
+  private MergeBranchComponentUuids mergeBranchComponentUuids = mock(MergeBranchComponentUuids.class);
 
   ArgumentCaptor<DefaultIssue> defaultIssueCaptor;
 
@@ -135,13 +127,13 @@ public class IntegrateIssuesVisitorTest {
 
   @Before
   public void setUp() throws Exception {
-    MockitoAnnotations.initMocks(this);
     IssueVisitors issueVisitors = new IssueVisitors(new IssueVisitor[] {issueVisitor});
 
     defaultIssueCaptor = ArgumentCaptor.forClass(DefaultIssue.class);
     when(movedFilesRepository.getOriginalFile(any(Component.class))).thenReturn(Optional.absent());
 
-    TrackerRawInputFactory rawInputFactory = new TrackerRawInputFactory(treeRootHolder, reportReader, fileSourceRepository, new CommonRuleEngineImpl(), issueFilter);
+    TrackerRawInputFactory rawInputFactory = new TrackerRawInputFactory(treeRootHolder, reportReader, fileSourceRepository, new CommonRuleEngineImpl(),
+      issueFilter, ruleRepository);
     TrackerBaseInputFactory baseInputFactory = new TrackerBaseInputFactory(issuesLoader, dbTester.getDbClient(), movedFilesRepository);
     TrackerMergeBranchInputFactory mergeInputFactory = new TrackerMergeBranchInputFactory(issuesLoader, mergeBranchComponentsUuids, dbTester.getDbClient());
     tracker = new TrackerExecution(baseInputFactory, rawInputFactory, new Tracker<>());
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/NewExternalRuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/NewExternalRuleTest.java
new file mode 100644 (file)
index 0000000..4b5f194
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.issue;
+
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rules.RuleType;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class NewExternalRuleTest {
+  @org.junit.Rule
+  public ExpectedException exception = ExpectedException.none();
+
+  @Test
+  public void should_build_new_external_rule() {
+    NewExternalRule.Builder builder = new NewExternalRule.Builder()
+      .setDescriptionUrl("url")
+      .setKey(RuleKey.of("repo", "rule"))
+      .setName("name")
+      .setSeverity("MAJOR")
+      .setType(RuleType.BUG);
+
+    assertThat(builder.descriptionUrl()).isEqualTo("url");
+    assertThat(builder.name()).isEqualTo("name");
+    assertThat(builder.severity()).isEqualTo("MAJOR");
+    assertThat(builder.type()).isEqualTo(RuleType.BUG);
+    assertThat(builder.descriptionUrl()).isEqualTo("url");
+
+    NewExternalRule rule = builder.build();
+
+    assertThat(rule.getDescriptionUrl()).isEqualTo("url");
+    assertThat(rule.getName()).isEqualTo("name");
+    assertThat(rule.getPluginKey()).isNull();
+    assertThat(rule.getSeverity()).isEqualTo("MAJOR");
+    assertThat(rule.getType()).isEqualTo(RuleType.BUG);
+    assertThat(rule.getDescriptionUrl()).isEqualTo("url");
+  }
+
+  @Test
+  public void fail_if_name_is_not_set() {
+    exception.expect(IllegalStateException.class);
+    exception.expectMessage("'name' not expected to be empty for an external rule");
+
+    new NewExternalRule.Builder()
+      .setDescriptionUrl("url")
+      .setKey(RuleKey.of("repo", "rule"))
+      .setSeverity("MAJOR")
+      .setType(RuleType.BUG)
+      .build();
+  }
+
+  @Test
+  public void fail_if_rule_key_is_not_set() {
+    exception.expect(IllegalStateException.class);
+    exception.expectMessage("'key' not expected to be null for an external rule");
+
+    new NewExternalRule.Builder()
+      .setDescriptionUrl("url")
+      .setName("name")
+      .setSeverity("MAJOR")
+      .setType(RuleType.BUG)
+      .build();
+  }
+}
index 52939ddb8f520c2aadc6cd77a5328d9d17992707..698954885aa3e9e8c94749a6711be5ad55864488 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.server.computation.task.projectanalysis.issue;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Optional;
+import java.util.function.Supplier;
 import org.junit.rules.ExternalResource;
 import org.sonar.api.rule.RuleKey;
 
@@ -32,6 +33,7 @@ public class RuleRepositoryRule extends ExternalResource implements RuleReposito
 
   private final Map<RuleKey, Rule> rulesByKey = new HashMap<>();
   private final Map<Integer, Rule> rulesById = new HashMap<>();
+  private final Map<RuleKey, NewExternalRule> newExternalRulesById = new HashMap<>();
 
   @Override
   protected void after() {
@@ -77,4 +79,9 @@ public class RuleRepositoryRule extends ExternalResource implements RuleReposito
     return this;
   }
 
+  @Override
+  public void insertNewExternalRuleIfAbsent(RuleKey ruleKey, Supplier<NewExternalRule> ruleSupplier) {
+    newExternalRulesById.computeIfAbsent(ruleKey, k -> ruleSupplier.get());
+  }
+
 }
index d6cd5b1c129e8dcbcc4925b522e33c3fb1242b5f..4bfe7bfd937f21bb88cc411c7b420ea80c9abc56 100644 (file)
@@ -62,12 +62,13 @@ public class TrackerRawInputFactoryTest {
 
   @Rule
   public SourceLinesRepositoryRule fileSourceRepository = new SourceLinesRepositoryRule();
+  
+  @Rule
+  public RuleRepositoryRule ruleRepository = new RuleRepositoryRule();
 
   CommonRuleEngine commonRuleEngine = mock(CommonRuleEngine.class);
-
   IssueFilter issueFilter = mock(IssueFilter.class);
-
-  TrackerRawInputFactory underTest = new TrackerRawInputFactory(treeRootHolder, reportReader, fileSourceRepository, commonRuleEngine, issueFilter);
+  TrackerRawInputFactory underTest = new TrackerRawInputFactory(treeRootHolder, reportReader, fileSourceRepository, commonRuleEngine, issueFilter, ruleRepository);
 
   @Test
   public void load_source_hash_sequences() {