Browse Source

SONAR-11514 Backdate issue when rule parameter is updated (#971)

tags/7.5
Benoit 5 years ago
parent
commit
40c58462ac
38 changed files with 416 additions and 143 deletions
  1. 3
    4
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueCreationDateCalculator.java
  2. 7
    1
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualityprofile/ActiveRule.java
  3. 1
    1
      server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadQualityProfilesStep.java
  4. 21
    0
      server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueCreationDateCalculatorTest.java
  5. 1
    1
      server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactoryTest.java
  6. 2
    2
      server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/commonrule/CommentDensityRuleTest.java
  7. 4
    4
      server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/commonrule/CommonRuleTest.java
  8. 3
    3
      server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/commonrule/CoverageRuleTest.java
  9. 2
    2
      server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/commonrule/DuplicatedBlockRuleTest.java
  10. 3
    3
      server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/commonrule/SkippedTestRuleTest.java
  11. 3
    3
      server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/commonrule/TestErrorRuleTest.java
  12. 4
    4
      server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualityprofile/ActiveRulesHolderImplTest.java
  13. 1
    1
      server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualityprofile/AlwaysActiveRulesHolderImpl.java
  14. 20
    12
      server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/LoadQualityProfilesStepTest.java
  15. 1
    0
      server/sonar-server/src/main/java/org/sonar/server/rule/ws/ActiveRuleCompleter.java
  16. 2
    1
      server/sonar-server/src/main/java/org/sonar/server/rule/ws/RulesWsParameters.java
  17. 2
    1
      server/sonar-server/src/main/java/org/sonar/server/rule/ws/SearchAction.java
  18. 5
    0
      server/sonar-server/src/main/resources/org/sonar/server/rule/ws/search-example.json
  19. 1
    0
      sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/ActiveRule.java
  20. 2
    5
      sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/ActiveRulesBuilder.java
  21. 6
    0
      sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultActiveRule.java
  22. 74
    51
      sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/NewActiveRule.java
  23. 46
    9
      sonar-plugin-api/src/test/java/org/sonar/api/batch/rule/CheckFactoryTest.java
  24. 18
    7
      sonar-plugin-api/src/test/java/org/sonar/api/batch/rule/internal/ActiveRulesBuilderTest.java
  25. 85
    0
      sonar-plugin-api/src/test/java/org/sonar/api/batch/rule/internal/NewActiveRuleTest.java
  26. 8
    1
      sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/internal/SensorContextTesterTest.java
  27. 1
    0
      sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ActiveRulesPublisher.java
  28. 14
    11
      sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/ActiveRulesProvider.java
  29. 2
    1
      sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/DefaultActiveRulesLoader.java
  30. 9
    0
      sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/LoadedActiveRule.java
  31. 19
    5
      sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ModuleIssuesTest.java
  32. 9
    2
      sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ActiveRulesPublisherTest.java
  33. 24
    0
      sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/ActiveRulesProviderTest.java
  34. 2
    1
      sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/DefaultActiveRulesLoaderTest.java
  35. 5
    1
      sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/RulesProfileProviderTest.java
  36. 4
    6
      sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/SensorOptimizerTest.java
  37. 1
    0
      sonar-scanner-protocol/src/main/protobuf/scanner_report.proto
  38. 1
    0
      sonar-ws/src/main/protobuf/ws-rules.proto

+ 3
- 4
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueCreationDateCalculator.java View File

} else { } else {
// Rule can't be inactive (see contract of IssueVisitor) // Rule can't be inactive (see contract of IssueVisitor)
ActiveRule activeRule = activeRulesHolder.get(issue.getRuleKey()).get(); ActiveRule activeRule = activeRulesHolder.get(issue.getRuleKey()).get();
if (activeRuleIsNew(activeRule, lastAnalysisOptional.get())
if (activeRuleIsNewOrChanged(activeRule, lastAnalysisOptional.get())
|| ruleImplementationChanged(activeRule.getRuleKey(), activeRule.getPluginKey(), lastAnalysisOptional.get())) { || ruleImplementationChanged(activeRule.getRuleKey(), activeRule.getPluginKey(), lastAnalysisOptional.get())) {
backdateIssue(component, issue); backdateIssue(component, issue);
} }
return lastAnalysisDate < scannerPlugin.getUpdatedAt(); return lastAnalysisDate < scannerPlugin.getUpdatedAt();
} }


private static boolean activeRuleIsNew(ActiveRule activeRule, Long lastAnalysisDate) {
long ruleCreationDate = activeRule.getCreatedAt();
return lastAnalysisDate < ruleCreationDate;
private static boolean activeRuleIsNewOrChanged(ActiveRule activeRule, Long lastAnalysisDate) {
return lastAnalysisDate < activeRule.getUpdatedAt();
} }


private Optional<Date> getDateOfLatestChange(Component component, DefaultIssue issue) { private Optional<Date> getDateOfLatestChange(Component component, DefaultIssue issue) {

+ 7
- 1
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualityprofile/ActiveRule.java View File

private final Map<String, String> params; private final Map<String, String> params;
private final long createdAt; private final long createdAt;
private final String pluginKey; private final String pluginKey;
private final long updatedAt;


public ActiveRule(RuleKey ruleKey, String severity, Map<String, String> params, long createdAt, @Nullable String pluginKey) {
public ActiveRule(RuleKey ruleKey, String severity, Map<String, String> params, long createdAt, long updatedAt, @Nullable String pluginKey) {
this.ruleKey = ruleKey; this.ruleKey = ruleKey;
this.severity = severity; this.severity = severity;
this.pluginKey = pluginKey; this.pluginKey = pluginKey;
this.params = ImmutableMap.copyOf(params); this.params = ImmutableMap.copyOf(params);
this.createdAt = createdAt; this.createdAt = createdAt;
this.updatedAt = updatedAt;
} }


public RuleKey getRuleKey() { public RuleKey getRuleKey() {
return createdAt; return createdAt;
} }


public long getUpdatedAt() {
return updatedAt;
}

@CheckForNull @CheckForNull
public String getPluginKey() { public String getPluginKey() {
return pluginKey; return pluginKey;

+ 1
- 1
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadQualityProfilesStep.java View File

private static ActiveRule convert(ScannerReport.ActiveRule input, Rule rule) { private static ActiveRule convert(ScannerReport.ActiveRule input, Rule rule) {
RuleKey key = RuleKey.of(input.getRuleRepository(), input.getRuleKey()); RuleKey key = RuleKey.of(input.getRuleRepository(), input.getRuleKey());
Map<String, String> params = new HashMap<>(input.getParamsByKeyMap()); Map<String, String> params = new HashMap<>(input.getParamsByKeyMap());
return new ActiveRule(key, input.getSeverity().name(), params, input.getCreatedAt(), rule.getPluginKey());
return new ActiveRule(key, input.getSeverity().name(), params, input.getCreatedAt(), input.getUpdatedAt(), rule.getPluginKey());
} }
} }

+ 21
- 0
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueCreationDateCalculatorTest.java View File

assertChangeOfCreationDateTo(expectedDate); assertChangeOfCreationDateTo(expectedDate);
} }


@Test
@UseDataProvider("backdatingDateCases")
public void should_backdate_date_if_scm_is_available_and_rule_has_changed(BiConsumer<DefaultIssue, ScmInfo> configure, long expectedDate) {
previousAnalysisWas(2000L);
currentAnalysisIs(3000L);

makeIssueNew();
configure.accept(issue, createMockScmInfo());
setRuleCreatedAt(1200L);
setRuleUpdatedAt(2800L);

run();

assertChangeOfCreationDateTo(expectedDate);
}

@Test @Test
@UseDataProvider("backdatingDateCases") @UseDataProvider("backdatingDateCases")
public void should_backdate_date_if_scm_is_available_and_first_analysis(BiConsumer<DefaultIssue, ScmInfo> configure, long expectedDate) { public void should_backdate_date_if_scm_is_available_and_first_analysis(BiConsumer<DefaultIssue, ScmInfo> configure, long expectedDate) {


private void setRuleCreatedAt(long createdAt) { private void setRuleCreatedAt(long createdAt) {
when(activeRule.getCreatedAt()).thenReturn(createdAt); when(activeRule.getCreatedAt()).thenReturn(createdAt);
when(activeRule.getUpdatedAt()).thenReturn(createdAt);
}

private void setRuleUpdatedAt(long updateAt) {
when(activeRule.getUpdatedAt()).thenReturn(updateAt);
} }


private void rulePlugin(String pluginKey) { private void rulePlugin(String pluginKey) {

+ 1
- 1
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactoryTest.java View File

} }


private void markRuleAsActive(RuleKey ruleKey) { private void markRuleAsActive(RuleKey ruleKey) {
activeRulesHolder.put(new ActiveRule(ruleKey, Severity.CRITICAL, emptyMap(), 1_000L, null));
activeRulesHolder.put(new ActiveRule(ruleKey, Severity.CRITICAL, emptyMap(), 1_000L, 1_000L, null));
} }
} }

+ 2
- 2
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/commonrule/CommentDensityRuleTest.java View File



@Test @Test
public void no_issues_if_enough_comments() { 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, PLUGIN_KEY));
activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, ImmutableMap.of(CommonRuleKeys.INSUFFICIENT_COMMENT_DENSITY_PROPERTY, "25"), 1_000L, 1_000L, PLUGIN_KEY));
measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.COMMENT_LINES_DENSITY_KEY, Measure.newMeasureBuilder().create(90.0, 1)); measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.COMMENT_LINES_DENSITY_KEY, Measure.newMeasureBuilder().create(90.0, 1));


DefaultIssue issue = underTest.processFile(FILE, PLUGIN_KEY); DefaultIssue issue = underTest.processFile(FILE, PLUGIN_KEY);
} }


private void prepareForIssue(String minDensity, ReportComponent file, double commentLineDensity, int commentLines, int ncloc) { private void prepareForIssue(String minDensity, ReportComponent file, double commentLineDensity, int commentLines, int ncloc) {
activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, ImmutableMap.of(CommonRuleKeys.INSUFFICIENT_COMMENT_DENSITY_PROPERTY, minDensity), 1_000L, PLUGIN_KEY));
activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, ImmutableMap.of(CommonRuleKeys.INSUFFICIENT_COMMENT_DENSITY_PROPERTY, minDensity), 1_000L, 1_000L, PLUGIN_KEY));
measureRepository.addRawMeasure(file.getReportAttributes().getRef(), CoreMetrics.COMMENT_LINES_DENSITY_KEY, Measure.newMeasureBuilder().create(commentLineDensity, 1)); measureRepository.addRawMeasure(file.getReportAttributes().getRef(), CoreMetrics.COMMENT_LINES_DENSITY_KEY, Measure.newMeasureBuilder().create(commentLineDensity, 1));
measureRepository.addRawMeasure(file.getReportAttributes().getRef(), CoreMetrics.COMMENT_LINES_KEY, Measure.newMeasureBuilder().create(commentLines)); measureRepository.addRawMeasure(file.getReportAttributes().getRef(), CoreMetrics.COMMENT_LINES_KEY, Measure.newMeasureBuilder().create(commentLines));
measureRepository.addRawMeasure(file.getReportAttributes().getRef(), CoreMetrics.NCLOC_KEY, Measure.newMeasureBuilder().create(ncloc)); measureRepository.addRawMeasure(file.getReportAttributes().getRef(), CoreMetrics.NCLOC_KEY, Measure.newMeasureBuilder().create(ncloc));

+ 4
- 4
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/commonrule/CommonRuleTest.java View File



@Test @Test
public void test_getMinDensityParam() { public void test_getMinDensityParam() {
ActiveRule activeRule = new ActiveRule(RuleTesting.XOO_X1, Severity.MAJOR, ImmutableMap.of("minDensity", "30.5"), 1_000L, PLUGIN_KEY);
ActiveRule activeRule = new ActiveRule(RuleTesting.XOO_X1, Severity.MAJOR, ImmutableMap.of("minDensity", "30.5"), 1_000L, 1_000L, PLUGIN_KEY);
double minDensity = CommonRule.getMinDensityParam(activeRule, "minDensity"); double minDensity = CommonRule.getMinDensityParam(activeRule, "minDensity");


assertThat(minDensity).isEqualTo(30.5); assertThat(minDensity).isEqualTo(30.5);
thrown.expect(IllegalStateException.class); thrown.expect(IllegalStateException.class);
thrown.expectMessage("Required parameter [minDensity] is missing on rule [xoo:x1]"); thrown.expectMessage("Required parameter [minDensity] is missing on rule [xoo:x1]");


ActiveRule activeRule = new ActiveRule(RuleTesting.XOO_X1, Severity.MAJOR, ImmutableMap.of(), 1_000L, PLUGIN_KEY);
ActiveRule activeRule = new ActiveRule(RuleTesting.XOO_X1, Severity.MAJOR, ImmutableMap.of(), 1_000L, 1_000L, PLUGIN_KEY);
CommonRule.getMinDensityParam(activeRule, "minDensity"); CommonRule.getMinDensityParam(activeRule, "minDensity");
} }


thrown.expect(IllegalStateException.class); thrown.expect(IllegalStateException.class);
thrown.expectMessage("Minimum density of rule [xoo:x1] is incorrect. Got [-30.5] but must be between 0 and 100."); 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, PLUGIN_KEY);
ActiveRule activeRule = new ActiveRule(RuleTesting.XOO_X1, Severity.MAJOR, ImmutableMap.of("minDensity", "-30.5"), 1_000L, 1_000L, PLUGIN_KEY);
CommonRule.getMinDensityParam(activeRule, "minDensity"); CommonRule.getMinDensityParam(activeRule, "minDensity");
} }


thrown.expect(IllegalStateException.class); thrown.expect(IllegalStateException.class);
thrown.expectMessage("Minimum density of rule [xoo:x1] is incorrect. Got [305] but must be between 0 and 100."); 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, PLUGIN_KEY);
ActiveRule activeRule = new ActiveRule(RuleTesting.XOO_X1, Severity.MAJOR, ImmutableMap.of("minDensity", "305"), 1_000L, 1_000L, PLUGIN_KEY);
CommonRule.getMinDensityParam(activeRule, "minDensity"); CommonRule.getMinDensityParam(activeRule, "minDensity");
} }
} }

+ 3
- 3
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/commonrule/CoverageRuleTest.java View File



@Test @Test
public void no_issue_if_enough_coverage() { public void no_issue_if_enough_coverage() {
activeRuleHolder.put(new ActiveRule(getRuleKey(), Severity.CRITICAL, ImmutableMap.of(getMinPropertyKey(), "65"), 1_000L, PLUGIN_KEY));
activeRuleHolder.put(new ActiveRule(getRuleKey(), Severity.CRITICAL, ImmutableMap.of(getMinPropertyKey(), "65"), 1_000L, 1_000L, PLUGIN_KEY));
measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), getCoverageMetricKey(), Measure.newMeasureBuilder().create(90.0, 1)); measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), getCoverageMetricKey(), Measure.newMeasureBuilder().create(90.0, 1));


DefaultIssue issue = underTest.processFile(FILE, "java"); DefaultIssue issue = underTest.processFile(FILE, "java");


@Test @Test
public void issue_if_coverage_is_too_low() { public void issue_if_coverage_is_too_low() {
activeRuleHolder.put(new ActiveRule(getRuleKey(), Severity.CRITICAL, ImmutableMap.of(getMinPropertyKey(), "65"), 1_000L, PLUGIN_KEY));
activeRuleHolder.put(new ActiveRule(getRuleKey(), Severity.CRITICAL, ImmutableMap.of(getMinPropertyKey(), "65"), 1_000L, 1_000L, PLUGIN_KEY));
measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), getCoverageMetricKey(), Measure.newMeasureBuilder().create(20.0, 1)); 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(), getUncoveredMetricKey(), Measure.newMeasureBuilder().create(40));
measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), getToCoverMetricKey(), Measure.newMeasureBuilder().create(50)); measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), getToCoverMetricKey(), Measure.newMeasureBuilder().create(50));


@Test @Test
public void no_issue_if_coverage_is_not_set() { public void no_issue_if_coverage_is_not_set() {
activeRuleHolder.put(new ActiveRule(getRuleKey(), Severity.CRITICAL, ImmutableMap.of(getMinPropertyKey(), "65"), 1_000L, PLUGIN_KEY));
activeRuleHolder.put(new ActiveRule(getRuleKey(), Severity.CRITICAL, ImmutableMap.of(getMinPropertyKey(), "65"), 1_000L, 1_000L, PLUGIN_KEY));


DefaultIssue issue = underTest.processFile(FILE, "java"); DefaultIssue issue = underTest.processFile(FILE, "java");



+ 2
- 2
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/commonrule/DuplicatedBlockRuleTest.java View File



@Test @Test
public void no_issue_if_no_duplicated_blocks() { public void no_issue_if_no_duplicated_blocks() {
activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, Collections.emptyMap(), 1_000L, PLUGIN_KEY));
activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, Collections.emptyMap(), 1_000L, 1_000L, PLUGIN_KEY));
measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.DUPLICATED_BLOCKS_KEY, Measure.newMeasureBuilder().create(0)); measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.DUPLICATED_BLOCKS_KEY, Measure.newMeasureBuilder().create(0));


DefaultIssue issue = underTest.processFile(FILE, "java"); DefaultIssue issue = underTest.processFile(FILE, "java");


@Test @Test
public void issue_if_duplicated_blocks() { public void issue_if_duplicated_blocks() {
activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, Collections.emptyMap(), 1_000L, PLUGIN_KEY));
activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, Collections.emptyMap(), 1_000L, 1_000L, PLUGIN_KEY));
measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.DUPLICATED_BLOCKS_KEY, Measure.newMeasureBuilder().create(3)); measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.DUPLICATED_BLOCKS_KEY, Measure.newMeasureBuilder().create(3));


DefaultIssue issue = underTest.processFile(FILE, "java"); DefaultIssue issue = underTest.processFile(FILE, "java");

+ 3
- 3
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/commonrule/SkippedTestRuleTest.java View File



@Test @Test
public void issue_if_skipped_tests() { public void issue_if_skipped_tests() {
activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, Collections.emptyMap(), 1_000L, PLUGIN_KEY));
activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, Collections.emptyMap(), 1_000L, 1_000L, PLUGIN_KEY));
measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.SKIPPED_TESTS_KEY, Measure.newMeasureBuilder().create(2)); measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.SKIPPED_TESTS_KEY, Measure.newMeasureBuilder().create(2));


DefaultIssue issue = underTest.processFile(FILE, "java"); DefaultIssue issue = underTest.processFile(FILE, "java");


@Test @Test
public void no_issues_if_zero_skipped_tests() { public void no_issues_if_zero_skipped_tests() {
activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, Collections.emptyMap(), 1_000L, PLUGIN_KEY));
activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, Collections.emptyMap(), 1_000L, 1_000L, PLUGIN_KEY));
measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.SKIPPED_TESTS_KEY, Measure.newMeasureBuilder().create(0)); measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.SKIPPED_TESTS_KEY, Measure.newMeasureBuilder().create(0));


DefaultIssue issue = underTest.processFile(FILE, "java"); DefaultIssue issue = underTest.processFile(FILE, "java");


@Test @Test
public void no_issues_if_measure_is_absent() { public void no_issues_if_measure_is_absent() {
activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, Collections.emptyMap(), 1_000L, PLUGIN_KEY));
activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, Collections.emptyMap(), 1_000L, 1_000L, PLUGIN_KEY));


DefaultIssue issue = underTest.processFile(FILE, "java"); DefaultIssue issue = underTest.processFile(FILE, "java");



+ 3
- 3
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/commonrule/TestErrorRuleTest.java View File



@Test @Test
public void issue_if_errors_or_failures() { public void issue_if_errors_or_failures() {
activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, Collections.emptyMap(), 1_000L, PLUGIN_KEY));
activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, Collections.emptyMap(), 1_000L, 1_000L, PLUGIN_KEY));
measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.TEST_ERRORS_KEY, Measure.newMeasureBuilder().create(2)); 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)); measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.TEST_FAILURES_KEY, Measure.newMeasureBuilder().create(1));




@Test @Test
public void no_issues_if_zero_errors_and_failures() { public void no_issues_if_zero_errors_and_failures() {
activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, Collections.emptyMap(), 1_000L, PLUGIN_KEY));
activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, Collections.emptyMap(), 1_000L, 1_000L, PLUGIN_KEY));
measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.TEST_ERRORS_KEY, Measure.newMeasureBuilder().create(0)); 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)); measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.TEST_FAILURES_KEY, Measure.newMeasureBuilder().create(0));




@Test @Test
public void no_issues_if_test_measures_are_absent() { public void no_issues_if_test_measures_are_absent() {
activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, Collections.emptyMap(), 1_000L, PLUGIN_KEY));
activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, Collections.emptyMap(), 1_000L, 1_000L, PLUGIN_KEY));


DefaultIssue issue = underTest.processFile(FILE, "java"); DefaultIssue issue = underTest.processFile(FILE, "java");



+ 4
- 4
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualityprofile/ActiveRulesHolderImplTest.java View File



@Test @Test
public void get_active_rule() { public void get_active_rule() {
underTest.set(asList(new ActiveRule(RULE_KEY, Severity.BLOCKER, Collections.emptyMap(), SOME_DATE, PLUGIN_KEY)));
underTest.set(asList(new ActiveRule(RULE_KEY, Severity.BLOCKER, Collections.emptyMap(), SOME_DATE, SOME_DATE, PLUGIN_KEY)));


Optional<ActiveRule> activeRule = underTest.get(RULE_KEY); Optional<ActiveRule> activeRule = underTest.get(RULE_KEY);
assertThat(activeRule.isPresent()).isTrue(); assertThat(activeRule.isPresent()).isTrue();
thrown.expect(IllegalStateException.class); thrown.expect(IllegalStateException.class);
thrown.expectMessage("Active rules have already been initialized"); thrown.expectMessage("Active rules have already been initialized");


underTest.set(asList(new ActiveRule(RULE_KEY, Severity.BLOCKER, Collections.emptyMap(), 1_000L, PLUGIN_KEY)));
underTest.set(asList(new ActiveRule(RULE_KEY, Severity.BLOCKER, Collections.emptyMap(), SOME_DATE, SOME_DATE, PLUGIN_KEY)));
underTest.set(Collections.emptyList()); underTest.set(Collections.emptyList());


} }
thrown.expectMessage("Active rule must not be declared multiple times: squid:S001"); thrown.expectMessage("Active rule must not be declared multiple times: squid:S001");


underTest.set(asList( underTest.set(asList(
new ActiveRule(RULE_KEY, Severity.BLOCKER, Collections.emptyMap(), 1_000L, PLUGIN_KEY),
new ActiveRule(RULE_KEY, Severity.MAJOR, Collections.emptyMap(), 1_000L, PLUGIN_KEY)));
new ActiveRule(RULE_KEY, Severity.BLOCKER, Collections.emptyMap(), SOME_DATE, SOME_DATE, PLUGIN_KEY),
new ActiveRule(RULE_KEY, Severity.MAJOR, Collections.emptyMap(), SOME_DATE, SOME_DATE, PLUGIN_KEY)));
} }
} }

+ 1
- 1
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualityprofile/AlwaysActiveRulesHolderImpl.java View File

public class AlwaysActiveRulesHolderImpl implements ActiveRulesHolder { public class AlwaysActiveRulesHolderImpl implements ActiveRulesHolder {
@Override @Override
public Optional<ActiveRule> get(RuleKey ruleKey) { public Optional<ActiveRule> get(RuleKey ruleKey) {
return Optional.of(new ActiveRule(ruleKey, Severity.MAJOR, emptyMap(), 1_000L, null));
return Optional.of(new ActiveRule(ruleKey, Severity.MAJOR, emptyMap(), 1_000L, 1_000L, null));
} }


} }

+ 20
- 12
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/LoadQualityProfilesStepTest.java View File

*/ */
package org.sonar.ce.task.projectanalysis.step; package org.sonar.ce.task.projectanalysis.step;


import java.util.Optional;
import org.assertj.core.data.MapEntry; import org.assertj.core.data.MapEntry;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.sonar.ce.task.step.TestComputationStepContext; import org.sonar.ce.task.step.TestComputationStepContext;
import org.sonar.scanner.protocol.Constants; import org.sonar.scanner.protocol.Constants;
import org.sonar.scanner.protocol.output.ScannerReport; import org.sonar.scanner.protocol.output.ScannerReport;
import org.sonarqube.ws.Rules;


import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.sonar.db.rule.RuleTesting.XOO_X1; import static org.sonar.db.rule.RuleTesting.XOO_X1;
import static org.sonar.db.rule.RuleTesting.XOO_X2; import static org.sonar.db.rule.RuleTesting.XOO_X2;


.setPluginKey("xoo"); .setPluginKey("xoo");


ScannerReport.ActiveRule.Builder batch1 = ScannerReport.ActiveRule.newBuilder() ScannerReport.ActiveRule.Builder batch1 = ScannerReport.ActiveRule.newBuilder()
.setRuleRepository(XOO_X1.repository()).setRuleKey(XOO_X1.rule())
.setSeverity(Constants.Severity.BLOCKER);
.setRuleRepository(XOO_X1.repository())
.setRuleKey(XOO_X1.rule())
.setSeverity(Constants.Severity.BLOCKER)
.setCreatedAt(1000L)
.setUpdatedAt(1200L);
batch1.getMutableParamsByKey().put("p1", "v1"); batch1.getMutableParamsByKey().put("p1", "v1");


ScannerReport.ActiveRule.Builder batch2 = ScannerReport.ActiveRule.newBuilder() ScannerReport.ActiveRule.Builder batch2 = ScannerReport.ActiveRule.newBuilder()


assertThat(activeRulesHolder.getAll()).hasSize(2); assertThat(activeRulesHolder.getAll()).hasSize(2);


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");
ActiveRule ar1 = activeRulesHolder.get(XOO_X1).get();
assertThat(ar1.getSeverity()).isEqualTo(Severity.BLOCKER);
assertThat(ar1.getParams()).containsExactly(MapEntry.entry("p1", "v1"));
assertThat(ar1.getPluginKey()).isEqualTo("xoo");
assertThat(ar1.getCreatedAt()).isEqualTo(1000L);
assertThat(ar1.getUpdatedAt()).isEqualTo(1200L);

ActiveRule ar2 = activeRulesHolder.get(XOO_X2).get();
assertThat(ar2.getSeverity()).isEqualTo(Severity.MAJOR);
assertThat(ar2.getParams()).isEmpty();
assertThat(ar2.getPluginKey()).isEqualTo("xoo");
assertThat(ar1.getCreatedAt()).isEqualTo(1000L);
assertThat(ar1.getUpdatedAt()).isEqualTo(1200L);
} }


@Test @Test

+ 1
- 0
server/sonar-server/src/main/java/org/sonar/server/rule/ws/ActiveRuleCompleter.java View File

builder.setInherit(inheritance != null ? inheritance : ActiveRuleInheritance.NONE.name()); builder.setInherit(inheritance != null ? inheritance : ActiveRuleInheritance.NONE.name());
builder.setSeverity(activeRule.getSeverityString()); builder.setSeverity(activeRule.getSeverityString());
builder.setCreatedAt(DateUtils.formatDateTime(activeRule.getCreatedAt())); builder.setCreatedAt(DateUtils.formatDateTime(activeRule.getCreatedAt()));
builder.setUpdatedAt(DateUtils.formatDateTime(activeRule.getUpdatedAt()));
Rules.Active.Param.Builder paramBuilder = Rules.Active.Param.newBuilder(); Rules.Active.Param.Builder paramBuilder = Rules.Active.Param.newBuilder();
for (ActiveRuleParamDto parameter : parameters) { for (ActiveRuleParamDto parameter : parameters) {
builder.addParams(paramBuilder.clear() builder.addParams(paramBuilder.clear()

+ 2
- 1
server/sonar-server/src/main/java/org/sonar/server/rule/ws/RulesWsParameters.java View File

public static final String FIELD_REPO = "repo"; public static final String FIELD_REPO = "repo";
public static final String FIELD_NAME = "name"; public static final String FIELD_NAME = "name";
public static final String FIELD_CREATED_AT = "createdAt"; public static final String FIELD_CREATED_AT = "createdAt";
public static final String FIELD_UPDATED_AT = "updatedAt";
public static final String FIELD_SEVERITY = "severity"; public static final String FIELD_SEVERITY = "severity";
public static final String FIELD_STATUS = "status"; public static final String FIELD_STATUS = "status";
public static final String FIELD_INTERNAL_KEY = "internalKey"; public static final String FIELD_INTERNAL_KEY = "internalKey";
public static final String FIELD_PARAMS = "params"; public static final String FIELD_PARAMS = "params";
public static final String FIELD_ACTIVES = "actives"; public static final String FIELD_ACTIVES = "actives";


public static final Set<String> OPTIONAL_FIELDS = ImmutableSet.of(FIELD_REPO, FIELD_NAME, FIELD_CREATED_AT, FIELD_SEVERITY, FIELD_STATUS, FIELD_INTERNAL_KEY,
public static final Set<String> OPTIONAL_FIELDS = ImmutableSet.of(FIELD_REPO, FIELD_NAME, FIELD_CREATED_AT, FIELD_UPDATED_AT, FIELD_SEVERITY, FIELD_STATUS, FIELD_INTERNAL_KEY,
FIELD_IS_EXTERNAL, FIELD_IS_TEMPLATE, FIELD_TEMPLATE_KEY, FIELD_TAGS, FIELD_SYSTEM_TAGS, FIELD_LANGUAGE, FIELD_LANGUAGE_NAME, FIELD_HTML_DESCRIPTION, FIELD_IS_EXTERNAL, FIELD_IS_TEMPLATE, FIELD_TEMPLATE_KEY, FIELD_TAGS, FIELD_SYSTEM_TAGS, FIELD_LANGUAGE, FIELD_LANGUAGE_NAME, FIELD_HTML_DESCRIPTION,
FIELD_MARKDOWN_DESCRIPTION, FIELD_NOTE_LOGIN, FIELD_MARKDOWN_NOTE, FIELD_HTML_NOTE, FIELD_MARKDOWN_DESCRIPTION, FIELD_NOTE_LOGIN, FIELD_MARKDOWN_NOTE, FIELD_HTML_NOTE,
FIELD_DEFAULT_DEBT_REM_FUNCTION, FIELD_EFFORT_TO_FIX_DESCRIPTION, FIELD_DEBT_OVERLOADED, FIELD_DEBT_REM_FUNCTION, FIELD_DEFAULT_DEBT_REM_FUNCTION, FIELD_EFFORT_TO_FIX_DESCRIPTION, FIELD_DEBT_OVERLOADED, FIELD_DEBT_REM_FUNCTION,

+ 2
- 1
server/sonar-server/src/main/java/org/sonar/server/rule/ws/SearchAction.java View File

.setChangelog(new Change("7.1", "The field 'scope' has been added to the response")) .setChangelog(new Change("7.1", "The field 'scope' has been added to the response"))
.setChangelog(new Change("7.1", "The field 'scope' has been added to the 'f' parameter")) .setChangelog(new Change("7.1", "The field 'scope' has been added to the 'f' parameter"))
.setChangelog(new Change("7.2", "The field 'isExternal' has been added to the response")) .setChangelog(new Change("7.2", "The field 'isExternal' has been added to the response"))
.setChangelog(new Change("7.2", "The field 'includeExternal' has been added to the 'f' parameter"));
.setChangelog(new Change("7.2", "The field 'includeExternal' has been added to the 'f' parameter"))
.setChangelog(new Change("7.5", "The field 'updatedAt' has been added to the 'f' parameter"));


action.createParam(FACETS) action.createParam(FACETS)
.setDescription("Comma-separated list of the facets to be computed. No facet is computed by default.") .setDescription("Comma-separated list of the facets to be computed. No facet is computed by default.")

+ 5
- 0
server/sonar-server/src/main/resources/org/sonar/server/rule/ws/search-example.json View File

"repo": "squid", "repo": "squid",
"name": "Expressions should not be too complex", "name": "Expressions should not be too complex",
"createdAt": "2013-03-27T08:52:40+0100", "createdAt": "2013-03-27T08:52:40+0100",
"updatedAt": "2013-03-27T08:52:40+0100",
"htmlDesc": "<p>\nThe complexity of an expression is defined by the number of <code>&&</code>, <code>||</code> and <code>condition ? ifTrue : ifFalse</code> operators it contains.\nA single expression's complexity should not become too high to keep the code readable.\n</p>\n\n<p>The following code, with a maximum complexity of 3:</p>\n\n<pre>\nif (condition1 && condition2 && condition3 && condition4) { /* ... */ } // Non-Compliant\n</pre>\n\n<p>could be refactored into something like:</p>\n\n<pre>\nif (relevantMethodName1() && relevantMethodName2()) { /* ... */ } // Compliant\n\n/* ... */\n\nprivate boolean relevantMethodName1() {\n return condition1 && condition2;\n}\n\nprivate boolean relevantMethodName2() {\n return condition3 && condition4;\n}\n</pre>", "htmlDesc": "<p>\nThe complexity of an expression is defined by the number of <code>&&</code>, <code>||</code> and <code>condition ? ifTrue : ifFalse</code> operators it contains.\nA single expression's complexity should not become too high to keep the code readable.\n</p>\n\n<p>The following code, with a maximum complexity of 3:</p>\n\n<pre>\nif (condition1 && condition2 && condition3 && condition4) { /* ... */ } // Non-Compliant\n</pre>\n\n<p>could be refactored into something like:</p>\n\n<pre>\nif (relevantMethodName1() && relevantMethodName2()) { /* ... */ } // Compliant\n\n/* ... */\n\nprivate boolean relevantMethodName1() {\n return condition1 && condition2;\n}\n\nprivate boolean relevantMethodName2() {\n return condition3 && condition4;\n}\n</pre>",
"severity": "MAJOR", "severity": "MAJOR",
"status": "READY", "status": "READY",
"repo": "squid", "repo": "squid",
"name": "Avoid too complex class", "name": "Avoid too complex class",
"createdAt": "2013-03-27T08:52:40+0100", "createdAt": "2013-03-27T08:52:40+0100",
"updatedAt": "2013-03-27T08:52:40+0100",
"htmlDesc": "<p>The Cyclomatic Complexity is measured by the number of (&&, ||)\n\toperators and (if, while, do, for, ?:, catch, switch, case, return,\n\tthrow) statements in the body of a class plus one for each constructor,\n\tmethod (but not getter/setter), static initializer, or instance\n\tinitializer in the class. The last return stament in method, if exists,\n\tis not taken into account.</p>\n<p>\n\tEven when the Cyclomatic Complexity of a class is very high, this\n\tcomplexity might be well distributed among all methods. Nevertheless,\n\tmost of the time, a very complex class is a class which breaks the <a\n\t\thref='http://en.wikipedia.org/wiki/Single_responsibility_principle'>Single\n\t\tResponsibility Principle</a> and which should be re-factored to be split\n\tin several classes.\n</p>", "htmlDesc": "<p>The Cyclomatic Complexity is measured by the number of (&&, ||)\n\toperators and (if, while, do, for, ?:, catch, switch, case, return,\n\tthrow) statements in the body of a class plus one for each constructor,\n\tmethod (but not getter/setter), static initializer, or instance\n\tinitializer in the class. The last return stament in method, if exists,\n\tis not taken into account.</p>\n<p>\n\tEven when the Cyclomatic Complexity of a class is very high, this\n\tcomplexity might be well distributed among all methods. Nevertheless,\n\tmost of the time, a very complex class is a class which breaks the <a\n\t\thref='http://en.wikipedia.org/wiki/Single_responsibility_principle'>Single\n\t\tResponsibility Principle</a> and which should be re-factored to be split\n\tin several classes.\n</p>",
"severity": "MAJOR", "severity": "MAJOR",
"status": "READY", "status": "READY",
"repo": "squid", "repo": "squid",
"name": "Methods should not be too complex", "name": "Methods should not be too complex",
"createdAt": "2013-03-27T08:52:40+0100", "createdAt": "2013-03-27T08:52:40+0100",
"updatedAt": "2013-03-27T08:52:40+0100",
"htmlDesc": "<p>The Cyclomatic Complexity is measured by the number of\n\t(&amp;&amp;, ||) operators and (if, while, do, for, ?:, catch, switch,\n\tcase, return, throw) statements in the body of a class plus one for\n\teach constructor, method (but not getter/setter), static initializer,\n\tor instance initializer in the class. The last return stament in\n\tmethod, if exists, is not taken into account.</p>\n<p>\n\tEven when the Cyclomatic Complexity of a class is very high, this\n\tcomplexity might be well distributed among all methods. Nevertheless,\n\tmost of the time, a very complex class is a class which breaks the <a\n\t\thref=\"http://en.wikipedia.org/wiki/Single_responsibility_principle\">Single\n\t\tResponsibility Principle</a> and which should be re-factored to be split\n\tin several classes.\n</p>", "htmlDesc": "<p>The Cyclomatic Complexity is measured by the number of\n\t(&amp;&amp;, ||) operators and (if, while, do, for, ?:, catch, switch,\n\tcase, return, throw) statements in the body of a class plus one for\n\teach constructor, method (but not getter/setter), static initializer,\n\tor instance initializer in the class. The last return stament in\n\tmethod, if exists, is not taken into account.</p>\n<p>\n\tEven when the Cyclomatic Complexity of a class is very high, this\n\tcomplexity might be well distributed among all methods. Nevertheless,\n\tmost of the time, a very complex class is a class which breaks the <a\n\t\thref=\"http://en.wikipedia.org/wiki/Single_responsibility_principle\">Single\n\t\tResponsibility Principle</a> and which should be re-factored to be split\n\tin several classes.\n</p>",
"severity": "MAJOR", "severity": "MAJOR",
"status": "READY", "status": "READY",
"repo": "squid", "repo": "squid",
"name": "XPath rule", "name": "XPath rule",
"createdAt": "2013-03-27T08:52:40+0100", "createdAt": "2013-03-27T08:52:40+0100",
"updatedAt": "2013-03-27T08:52:40+0100",
"htmlDesc": "<p>\nThis rule allows to define some homemade Java rules with help of an XPath expression.\n</p>\n\n<p>\nIssues are created depending on the return value of the XPath expression. If the XPath expression returns:\n</p>\n<ul>\n <li>a single or list of AST nodes, then a line issue with the given message is created for each node</li>\n <li>a boolean, then a file issue with the given message is created only if the boolean is true</li>\n <li>anything else, no issue is created</li>\n</ul>\n\n<p>\nHere is an example of an XPath expression to log an issue on each if statement : //ifStatement\n</p>", "htmlDesc": "<p>\nThis rule allows to define some homemade Java rules with help of an XPath expression.\n</p>\n\n<p>\nIssues are created depending on the return value of the XPath expression. If the XPath expression returns:\n</p>\n<ul>\n <li>a single or list of AST nodes, then a line issue with the given message is created for each node</li>\n <li>a boolean, then a file issue with the given message is created only if the boolean is true</li>\n <li>anything else, no issue is created</li>\n</ul>\n\n<p>\nHere is an example of an XPath expression to log an issue on each if statement : //ifStatement\n</p>",
"severity": "MAJOR", "severity": "MAJOR",
"status": "READY", "status": "READY",
"repo": "squid", "repo": "squid",
"name": "firstOf() alternatives should be rules or token types", "name": "firstOf() alternatives should be rules or token types",
"createdAt": "2013-05-30T10:35:35+0200", "createdAt": "2013-05-30T10:35:35+0200",
"updatedAt": "2013-03-27T08:52:40+0100",
"htmlDesc": "<p>\r\nThe tree produced by the <code>firstOf()</code> matcher is hard to work with from checks when alternatives are not named.\r\n</p>\r\n\r\n<p>\r\nConsider the following rule:\r\n</p>\r\n\r\n<pre>\r\nb.rule(COMPILATION_UNIT).is(\r\n b.firstOf( /* Non-Compliant */\r\n \"FOO\",\r\n \"BAR\"));\r\n</pre>\r\n\r\n<p>\r\nIf, from a check, one wants to forbid the usage of the \"BAR\" alternative,\r\nthe easiest option will be to verify that the value of the first token is \"BAR\",\r\ni.e. <code>\"BAR\".equals(compilationUnitNode.getTokenValue())</code>.\r\n</p>\r\n\r\n<p>\r\nThis is not maintainable, for at least two reasons:\r\n</p>\r\n\r\n<ul>\r\n <li>The grammar might evolve to also accept \"bar\" in lowercase, which will break <code>\"BAR\".equals(...)</code></li>\r\n <li>The grammar might evolve to optionally accept \"hello\" before the <code>firstOf()</code>, which will break <code>compilationUnitNode.getTokenValue()</code></li>\r\n</ul>\r\n\r\n<p>\r\nInstead, it is much better to rewrite the grammar as:\r\n</p>\r\n\r\n<pre>\r\nb.rule(COMPILATION_UNIT).is(\r\n firstOf( /* Compliant */\r\n FOO,\r\n BAR));\r\nb.rule(FOO).is(\"FOO\");\r\nb.rule(BAR).is(\"BAR\");\r\n</pre>\r\n\r\n<p>\r\nThe same check which forbids \"BAR\" would be written as: <code>compilationUnitNode.hasDirectChildren(BAR)</code>.\r\nThis allows both of the previous grammar evolutions to be made without impacting the check at all.\r\n</p>", "htmlDesc": "<p>\r\nThe tree produced by the <code>firstOf()</code> matcher is hard to work with from checks when alternatives are not named.\r\n</p>\r\n\r\n<p>\r\nConsider the following rule:\r\n</p>\r\n\r\n<pre>\r\nb.rule(COMPILATION_UNIT).is(\r\n b.firstOf( /* Non-Compliant */\r\n \"FOO\",\r\n \"BAR\"));\r\n</pre>\r\n\r\n<p>\r\nIf, from a check, one wants to forbid the usage of the \"BAR\" alternative,\r\nthe easiest option will be to verify that the value of the first token is \"BAR\",\r\ni.e. <code>\"BAR\".equals(compilationUnitNode.getTokenValue())</code>.\r\n</p>\r\n\r\n<p>\r\nThis is not maintainable, for at least two reasons:\r\n</p>\r\n\r\n<ul>\r\n <li>The grammar might evolve to also accept \"bar\" in lowercase, which will break <code>\"BAR\".equals(...)</code></li>\r\n <li>The grammar might evolve to optionally accept \"hello\" before the <code>firstOf()</code>, which will break <code>compilationUnitNode.getTokenValue()</code></li>\r\n</ul>\r\n\r\n<p>\r\nInstead, it is much better to rewrite the grammar as:\r\n</p>\r\n\r\n<pre>\r\nb.rule(COMPILATION_UNIT).is(\r\n firstOf( /* Compliant */\r\n FOO,\r\n BAR));\r\nb.rule(FOO).is(\"FOO\");\r\nb.rule(BAR).is(\"BAR\");\r\n</pre>\r\n\r\n<p>\r\nThe same check which forbids \"BAR\" would be written as: <code>compilationUnitNode.hasDirectChildren(BAR)</code>.\r\nThis allows both of the previous grammar evolutions to be made without impacting the check at all.\r\n</p>",
"severity": "MAJOR", "severity": "MAJOR",
"status": "READY", "status": "READY",

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

*/ */
@CheckForNull @CheckForNull
String templateRuleKey(); String templateRuleKey();

} }

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



private final Map<RuleKey, NewActiveRule> map = new LinkedHashMap<>(); private final Map<RuleKey, NewActiveRule> map = new LinkedHashMap<>();


public NewActiveRule create(RuleKey ruleKey) {
return new NewActiveRule(this, ruleKey);
}

void activate(NewActiveRule newActiveRule) {
public ActiveRulesBuilder addRule(NewActiveRule newActiveRule) {
if (map.containsKey(newActiveRule.ruleKey)) { if (map.containsKey(newActiveRule.ruleKey)) {
throw new IllegalStateException(String.format("Rule '%s' is already activated", newActiveRule.ruleKey)); throw new IllegalStateException(String.format("Rule '%s' is already activated", newActiveRule.ruleKey));
} }
map.put(newActiveRule.ruleKey, newActiveRule); map.put(newActiveRule.ruleKey, newActiveRule);
return this;
} }


public ActiveRules build() { public ActiveRules build() {

+ 6
- 0
sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultActiveRule.java View File

private final String templateRuleKey; private final String templateRuleKey;
private final Map<String, String> params; private final Map<String, String> params;
private final long createdAt; private final long createdAt;
private final long updatedAt;


DefaultActiveRule(NewActiveRule newActiveRule) { DefaultActiveRule(NewActiveRule newActiveRule) {
this.severity = newActiveRule.severity; this.severity = newActiveRule.severity;
this.params = Collections.unmodifiableMap(new HashMap<>(newActiveRule.params)); this.params = Collections.unmodifiableMap(new HashMap<>(newActiveRule.params));
this.language = newActiveRule.language; this.language = newActiveRule.language;
this.createdAt = newActiveRule.createdAt; this.createdAt = newActiveRule.createdAt;
this.updatedAt = newActiveRule.updatedAt;
} }


@Override @Override
public long createdAt() { public long createdAt() {
return createdAt; return createdAt;
} }

public long updatedAt() {
return updatedAt;
}
} }

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

import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.Severity; import org.sonar.api.rule.Severity;
/** /**
* @since 4.2 * @since 4.2
*/ */
@Immutable
public class NewActiveRule { public class NewActiveRule {
final RuleKey ruleKey; final RuleKey ruleKey;
String name;
String severity = Severity.defaultSeverity();
Map<String, String> params = new HashMap<>();
long createdAt;
String internalKey;
String language;
String templateRuleKey;
private final ActiveRulesBuilder builder;
final String name;
final String severity;
final Map<String, String> params;
final long createdAt;
final long updatedAt;
final String internalKey;
final String language;
final String templateRuleKey;


NewActiveRule(ActiveRulesBuilder builder, RuleKey ruleKey) {
this.builder = builder;
this.ruleKey = ruleKey;
NewActiveRule(Builder builder) {
this.ruleKey = builder.ruleKey;
this.name = builder.name;
this.severity = builder.severity;
this.params = builder.params;
this.createdAt = builder.createdAt;
this.updatedAt = builder.updatedAt;
this.internalKey = builder.internalKey;
this.language = builder.language;
this.templateRuleKey = builder.templateRuleKey;
} }


public NewActiveRule setName(String name) {
this.name = name;
return this;
}
public static class Builder {
private RuleKey ruleKey;
private String name;
private String severity = Severity.defaultSeverity();
private Map<String, String> params = new HashMap<>();
private long createdAt;
private long updatedAt;
private String internalKey;
private String language;
private String templateRuleKey;


public NewActiveRule setSeverity(@Nullable String severity) {
this.severity = StringUtils.defaultIfBlank(severity, Severity.defaultSeverity());
return this;
}
public Builder setRuleKey(RuleKey ruleKey) {
this.ruleKey = ruleKey;
return this;
}


public NewActiveRule setInternalKey(@Nullable String internalKey) {
this.internalKey = internalKey;
return this;
}
public Builder setName(String name) {
this.name = name;
return this;
}


public NewActiveRule setTemplateRuleKey(@Nullable String templateRuleKey) {
this.templateRuleKey = templateRuleKey;
return this;
}
public Builder setSeverity(@Nullable String severity) {
this.severity = StringUtils.defaultIfBlank(severity, Severity.defaultSeverity());
return this;
}


public NewActiveRule setLanguage(@Nullable String language) {
this.language = language;
return this;
}
public Builder setParam(String key, @Nullable String value) {
// possible improvement : check that the param key exists in rule definition
if (value == null) {
params.remove(key);
} else {
params.put(key, value);
}
return this;
}


public NewActiveRule setParam(String key, @Nullable String value) {
// possible improvement : check that the param key exists in rule definition
if (value == null) {
params.remove(key);
} else {
params.put(key, value);
public Builder setCreatedAt(long createdAt) {
this.createdAt = createdAt;
return this;
} }
return this;
}


public Map<String, String> params() {
return params;
}
public Builder setUpdatedAt(long updatedAt) {
this.updatedAt = updatedAt;
return this;
}


public long getCreatedAt() {
return createdAt;
}
public Builder setInternalKey(@Nullable String internalKey) {
this.internalKey = internalKey;
return this;
}


public void setCreatedAt(long createdAt) {
this.createdAt = createdAt;
}
public Builder setLanguage(@Nullable String language) {
this.language = language;
return this;
}

public Builder setTemplateRuleKey(@Nullable String templateRuleKey) {
this.templateRuleKey = templateRuleKey;
return this;
}


public ActiveRulesBuilder activate() {
builder.activate(this);
return builder;
public NewActiveRule build() {
return new NewActiveRule(this);
}
} }
} }

+ 46
- 9
sonar-plugin-api/src/test/java/org/sonar/api/batch/rule/CheckFactoryTest.java View File

import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException; import org.junit.rules.ExpectedException;
import org.sonar.api.batch.rule.internal.ActiveRulesBuilder; import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
import org.sonar.api.batch.rule.internal.NewActiveRule;
import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.RuleKey;
import org.sonar.api.utils.SonarException; import org.sonar.api.utils.SonarException;


@Test @Test
public void class_name_as_check_key() { public void class_name_as_check_key() {
RuleKey ruleKey = RuleKey.of("squid", "org.sonar.api.batch.rule.CheckWithoutProperties"); RuleKey ruleKey = RuleKey.of("squid", "org.sonar.api.batch.rule.CheckWithoutProperties");
builder.create(ruleKey).activate();
NewActiveRule rule = new NewActiveRule.Builder()
.setRuleKey(ruleKey)
.build();
builder.addRule(rule);
CheckFactory checkFactory = new CheckFactory(builder.build()); CheckFactory checkFactory = new CheckFactory(builder.build());


Checks checks = checkFactory.create("squid").addAnnotatedChecks(CheckWithoutProperties.class); Checks checks = checkFactory.create("squid").addAnnotatedChecks(CheckWithoutProperties.class);
@Test @Test
public void param_as_string_field() { public void param_as_string_field() {
RuleKey ruleKey = RuleKey.of("squid", "org.sonar.api.batch.rule.CheckWithStringProperty"); RuleKey ruleKey = RuleKey.of("squid", "org.sonar.api.batch.rule.CheckWithStringProperty");
builder.create(ruleKey).setParam("pattern", "foo").activate();
NewActiveRule rule = new NewActiveRule.Builder()
.setRuleKey(ruleKey)
.setParam("pattern", "foo")
.build();
builder.addRule(rule);


CheckFactory checkFactory = new CheckFactory(builder.build()); CheckFactory checkFactory = new CheckFactory(builder.build());
Checks checks = checkFactory.create("squid").addAnnotatedChecks(CheckWithStringProperty.class); Checks checks = checkFactory.create("squid").addAnnotatedChecks(CheckWithStringProperty.class);
thrown.expectMessage("The field 'unknown' does not exist or is not annotated with @RuleProperty in the class org.sonar.api.batch.rule.CheckWithStringProperty"); thrown.expectMessage("The field 'unknown' does not exist or is not annotated with @RuleProperty in the class org.sonar.api.batch.rule.CheckWithStringProperty");


RuleKey ruleKey = RuleKey.of("squid", "org.sonar.api.batch.rule.CheckWithStringProperty"); RuleKey ruleKey = RuleKey.of("squid", "org.sonar.api.batch.rule.CheckWithStringProperty");
builder.create(ruleKey).setParam("unknown", "foo").activate();
NewActiveRule rule = new NewActiveRule.Builder()
.setRuleKey(ruleKey)
.setParam("unknown", "foo")
.build();
builder.addRule(rule);


CheckFactory checkFactory = new CheckFactory(builder.build()); CheckFactory checkFactory = new CheckFactory(builder.build());
checkFactory.create("squid").addAnnotatedChecks(CheckWithStringProperty.class); checkFactory.create("squid").addAnnotatedChecks(CheckWithStringProperty.class);
@Test @Test
public void param_as_primitive_fields() { public void param_as_primitive_fields() {
RuleKey ruleKey = RuleKey.of("squid", "org.sonar.api.batch.rule.CheckWithPrimitiveProperties"); RuleKey ruleKey = RuleKey.of("squid", "org.sonar.api.batch.rule.CheckWithPrimitiveProperties");
builder.create(ruleKey).setParam("max", "300").setParam("ignore", "true").activate();
NewActiveRule rule = new NewActiveRule.Builder()
.setRuleKey(ruleKey)
.setParam("max", "300")
.setParam("ignore", "true")
.build();
builder.addRule(rule);


CheckFactory checkFactory = new CheckFactory(builder.build()); CheckFactory checkFactory = new CheckFactory(builder.build());
Checks checks = checkFactory.create("squid").addAnnotatedChecks(CheckWithPrimitiveProperties.class); Checks checks = checkFactory.create("squid").addAnnotatedChecks(CheckWithPrimitiveProperties.class);
@Test @Test
public void param_as_inherited_field() { public void param_as_inherited_field() {
RuleKey ruleKey = RuleKey.of("squid", "org.sonar.api.batch.rule.CheckWithPrimitiveProperties"); RuleKey ruleKey = RuleKey.of("squid", "org.sonar.api.batch.rule.CheckWithPrimitiveProperties");
builder.create(ruleKey).setParam("max", "300").activate();
NewActiveRule rule = new NewActiveRule.Builder()
.setRuleKey(ruleKey)
.setParam("max", "300")
.build();
builder.addRule(rule);


CheckFactory checkFactory = new CheckFactory(builder.build()); CheckFactory checkFactory = new CheckFactory(builder.build());
Checks checks = checkFactory.create("squid").addAnnotatedChecks(CheckWithPrimitiveProperties.class); Checks checks = checkFactory.create("squid").addAnnotatedChecks(CheckWithPrimitiveProperties.class);
@Test @Test
public void use_template_rule_key() { public void use_template_rule_key() {
RuleKey ruleKey = RuleKey.of("squid", "S0001_123"); RuleKey ruleKey = RuleKey.of("squid", "S0001_123");
builder.create(ruleKey).setTemplateRuleKey("S0001").activate();
NewActiveRule rule = new NewActiveRule.Builder()
.setRuleKey(ruleKey)
.setTemplateRuleKey("S0001")
.build();
builder.addRule(rule);


CheckFactory checkFactory = new CheckFactory(builder.build()); CheckFactory checkFactory = new CheckFactory(builder.build());
Checks checks = checkFactory.create("squid").addAnnotatedChecks(CheckWithKey.class); Checks checks = checkFactory.create("squid").addAnnotatedChecks(CheckWithKey.class);
thrown.expect(SonarException.class); thrown.expect(SonarException.class);


RuleKey ruleKey = RuleKey.of("squid", "org.sonar.api.batch.rule.CheckWithUnsupportedPropertyType"); RuleKey ruleKey = RuleKey.of("squid", "org.sonar.api.batch.rule.CheckWithUnsupportedPropertyType");
builder.create(ruleKey).setParam("max", "300").activate();
NewActiveRule rule = new NewActiveRule.Builder()
.setRuleKey(ruleKey)
.setParam("max", "300")
.build();
builder.addRule(rule);


CheckFactory checkFactory = new CheckFactory(builder.build()); CheckFactory checkFactory = new CheckFactory(builder.build());
checkFactory.create("squid").addAnnotatedChecks(CheckWithUnsupportedPropertyType.class); checkFactory.create("squid").addAnnotatedChecks(CheckWithUnsupportedPropertyType.class);
@Test @Test
public void override_field_key() { public void override_field_key() {
RuleKey ruleKey = RuleKey.of("squid", "org.sonar.api.batch.rule.CheckWithOverriddenPropertyKey"); RuleKey ruleKey = RuleKey.of("squid", "org.sonar.api.batch.rule.CheckWithOverriddenPropertyKey");
builder.create(ruleKey).setParam("maximum", "300").activate();
NewActiveRule rule = new NewActiveRule.Builder()
.setRuleKey(ruleKey)
.setParam("maximum", "300")
.build();
builder.addRule(rule);


CheckFactory checkFactory = new CheckFactory(builder.build()); CheckFactory checkFactory = new CheckFactory(builder.build());
Checks checks = checkFactory.create("squid").addAnnotatedChecks(CheckWithOverriddenPropertyKey.class); Checks checks = checkFactory.create("squid").addAnnotatedChecks(CheckWithOverriddenPropertyKey.class);
@Test @Test
public void checks_as_objects() { public void checks_as_objects() {
RuleKey ruleKey = RuleKey.of("squid", "org.sonar.api.batch.rule.CheckWithStringProperty"); RuleKey ruleKey = RuleKey.of("squid", "org.sonar.api.batch.rule.CheckWithStringProperty");
builder.create(ruleKey).setParam("pattern", "foo").activate();
NewActiveRule rule = new NewActiveRule.Builder()
.setRuleKey(ruleKey)
.setParam("pattern", "foo")
.build();
builder.addRule(rule);
CheckFactory checkFactory = new CheckFactory(builder.build()); CheckFactory checkFactory = new CheckFactory(builder.build());


CheckWithStringProperty check = new CheckWithStringProperty(); CheckWithStringProperty check = new CheckWithStringProperty();

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



@Test @Test
public void build_rules() { public void build_rules() {
ActiveRules activeRules = new ActiveRulesBuilder()
.create(RuleKey.of("squid", "S0001"))
NewActiveRule activeRule = new NewActiveRule.Builder()
.setRuleKey(RuleKey.of("squid", "S0001"))
.setName("My Rule") .setName("My Rule")
.setSeverity(Severity.CRITICAL) .setSeverity(Severity.CRITICAL)
.setInternalKey("__S0001__") .setInternalKey("__S0001__")
.setParam("min", "20") .setParam("min", "20")
.activate()
.build();

ActiveRules activeRules = new ActiveRulesBuilder()
.addRule(activeRule)
// most simple rule // most simple rule
.create(RuleKey.of("squid", "S0002")).activate()
.create(RuleKey.of("findbugs", "NPE")).setInternalKey(null).setSeverity(null).setParam("foo", null).activate()
.addRule(new NewActiveRule.Builder().setRuleKey(RuleKey.of("squid", "S0002")).build())
.addRule(new NewActiveRule.Builder()
.setRuleKey(RuleKey.of("findbugs", "NPE"))
.setInternalKey(null)
.setSeverity(null)
.setParam("foo", null)
.build())
.build(); .build();


assertThat(activeRules.findAll()).hasSize(3); assertThat(activeRules.findAll()).hasSize(3);
@Test @Test
public void fail_to_add_twice_the_same_rule() { public void fail_to_add_twice_the_same_rule() {
ActiveRulesBuilder builder = new ActiveRulesBuilder(); ActiveRulesBuilder builder = new ActiveRulesBuilder();
builder.create(RuleKey.of("squid", "S0001")).activate();
NewActiveRule rule = new NewActiveRule.Builder()
.setRuleKey(RuleKey.of("squid", "S0001"))
.build();
builder.addRule(rule);


thrown.expect(IllegalStateException.class); thrown.expect(IllegalStateException.class);
thrown.expectMessage("Rule 'squid:S0001' is already activated"); thrown.expectMessage("Rule 'squid:S0001' is already activated");


builder.create(RuleKey.of("squid", "S0001")).activate();
builder.addRule(rule);
} }
} }

+ 85
- 0
sonar-plugin-api/src/test/java/org/sonar/api/batch/rule/internal/NewActiveRuleTest.java View File

/*
* 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.api.batch.rule.internal;

import com.google.common.collect.ImmutableMap;
import org.junit.Before;
import org.junit.Test;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.Severity;

import static org.assertj.core.api.Assertions.assertThat;

public class NewActiveRuleTest {

private NewActiveRule.Builder builder;

@Before
public void setBuilder() {
builder = new NewActiveRule.Builder();
}

@Test
public void builder_should_set_every_param() {
NewActiveRule rule = builder
.setRuleKey(RuleKey.of("foo", "bar"))
.setName("name")
.setSeverity(Severity.CRITICAL)
.setParam("key", "value")
.setCreatedAt(1_000L)
.setUpdatedAt(1_000L)
.setInternalKey("internal_key")
.setLanguage("language")
.setTemplateRuleKey("templateRuleKey")
.build();

assertThat(rule.ruleKey).isEqualTo(RuleKey.of("foo", "bar"));
assertThat(rule.name).isEqualTo("name");
assertThat(rule.severity).isEqualTo(Severity.CRITICAL);
assertThat(rule.params).isEqualTo(ImmutableMap.of("key", "value"));
assertThat(rule.createdAt).isEqualTo(1_000L);
assertThat(rule.updatedAt).isEqualTo(1_000L);
assertThat(rule.internalKey).isEqualTo("internal_key");
assertThat(rule.language).isEqualTo("language");
assertThat(rule.templateRuleKey).isEqualTo("templateRuleKey");
}

@Test
public void severity_should_have_default_value() {
NewActiveRule rule = builder.build();
assertThat(rule.severity).isEqualTo(Severity.defaultSeverity());
}

@Test
public void params_should_be_empty_map_if_no_params() {
NewActiveRule rule = builder.build();
assertThat(rule.params).isEqualTo(ImmutableMap.of());
}

@Test
public void set_param_remove_param_if_value_is_null() {
NewActiveRule rule = builder
.setParam("foo", "bar")
.setParam("removed", "value")
.setParam("removed", null)
.build();
assertThat(rule.params).isEqualTo(ImmutableMap.of("foo", "bar"));
}
}

+ 8
- 1
sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/internal/SensorContextTesterTest.java View File

import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException; import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder; import org.junit.rules.TemporaryFolder;
import org.mockito.Mockito;
import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.internal.DefaultFileSystem; import org.sonar.api.batch.fs.internal.DefaultFileSystem;
import org.sonar.api.batch.rule.ActiveRules; import org.sonar.api.batch.rule.ActiveRules;
import org.sonar.api.batch.rule.Severity; import org.sonar.api.batch.rule.Severity;
import org.sonar.api.batch.rule.internal.ActiveRulesBuilder; import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
import org.sonar.api.batch.rule.internal.NewActiveRule;
import org.sonar.api.batch.sensor.error.AnalysisError; import org.sonar.api.batch.sensor.error.AnalysisError;
import org.sonar.api.batch.sensor.error.NewAnalysisError; import org.sonar.api.batch.sensor.error.NewAnalysisError;
import org.sonar.api.batch.sensor.highlighting.TypeOfText; import org.sonar.api.batch.sensor.highlighting.TypeOfText;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.tuple; import static org.assertj.core.api.Assertions.tuple;
import static org.assertj.core.data.MapEntry.entry; import static org.assertj.core.data.MapEntry.entry;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;


public class SensorContextTesterTest { public class SensorContextTesterTest {




@Test @Test
public void testActiveRules() { public void testActiveRules() {
ActiveRules activeRules = new ActiveRulesBuilder().create(RuleKey.of("repo", "rule")).activate().build();
NewActiveRule activeRule = new NewActiveRule.Builder()
.setRuleKey(RuleKey.of("foo", "bar"))
.build();
ActiveRules activeRules = new ActiveRulesBuilder().addRule(activeRule).build();
tester.setActiveRules(activeRules); tester.setActiveRules(activeRules);
assertThat(tester.activeRules().findAll()).hasSize(1); assertThat(tester.activeRules().findAll()).hasSize(1);
} }

+ 1
- 0
sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ActiveRulesPublisher.java View File

builder.setRuleKey(input.ruleKey().rule()); builder.setRuleKey(input.ruleKey().rule());
builder.setSeverity(Constants.Severity.valueOf(input.severity())); builder.setSeverity(Constants.Severity.valueOf(input.severity()));
builder.setCreatedAt(input.createdAt()); builder.setCreatedAt(input.createdAt());
builder.setUpdatedAt(input.updatedAt());
builder.getMutableParamsByKey().putAll(input.params()); builder.getMutableParamsByKey().putAll(input.params());
return builder.build(); return builder.build();
}).collect(toList())); }).collect(toList()));

+ 14
- 11
sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/ActiveRulesProvider.java View File

} }


private static ActiveRules transform(Collection<LoadedActiveRule> loadedRules) { private static ActiveRules transform(Collection<LoadedActiveRule> loadedRules) {
ActiveRulesBuilder builder = new ActiveRulesBuilder();
ActiveRulesBuilder activeRulesBuilder = new ActiveRulesBuilder();


for (LoadedActiveRule activeRule : loadedRules) { for (LoadedActiveRule activeRule : loadedRules) {
NewActiveRule newActiveRule = builder.create(activeRule.getRuleKey());
newActiveRule.setName(activeRule.getName());
newActiveRule.setSeverity(activeRule.getSeverity());
newActiveRule.setCreatedAt(activeRule.getCreatedAt());
newActiveRule.setLanguage(activeRule.getLanguage());
newActiveRule.setInternalKey(activeRule.getInternalKey());
newActiveRule.setTemplateRuleKey(activeRule.getTemplateRuleKey());
NewActiveRule.Builder builder = new NewActiveRule.Builder();
builder
.setRuleKey(activeRule.getRuleKey())
.setName(activeRule.getName())
.setSeverity(activeRule.getSeverity())
.setCreatedAt(activeRule.getCreatedAt())
.setUpdatedAt(activeRule.getUpdatedAt())
.setLanguage(activeRule.getLanguage())
.setInternalKey(activeRule.getInternalKey())
.setTemplateRuleKey(activeRule.getTemplateRuleKey());


// load parameters // load parameters
if (activeRule.getParams() != null) { if (activeRule.getParams() != null) {
for (Map.Entry<String, String> params : activeRule.getParams().entrySet()) { for (Map.Entry<String, String> params : activeRule.getParams().entrySet()) {
newActiveRule.setParam(params.getKey(), params.getValue());
builder.setParam(params.getKey(), params.getValue());
} }
} }


newActiveRule.activate();
activeRulesBuilder.addRule(builder.build());
} }
return builder.build();
return activeRulesBuilder.build();
} }


private static List<LoadedActiveRule> load(ActiveRulesLoader loader, String qProfileKey) { private static List<LoadedActiveRule> load(ActiveRulesLoader loader, String qProfileKey) {

+ 2
- 1
sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/DefaultActiveRulesLoader.java View File

import static org.sonar.api.utils.DateUtils.parseDateTime; import static org.sonar.api.utils.DateUtils.parseDateTime;


public class DefaultActiveRulesLoader implements ActiveRulesLoader { public class DefaultActiveRulesLoader implements ActiveRulesLoader {
private static final String RULES_SEARCH_URL = "/api/rules/search.protobuf?f=repo,name,severity,lang,internalKey,templateKey,params,actives,createdAt&activation=true";
private static final String RULES_SEARCH_URL = "/api/rules/search.protobuf?f=repo,name,severity,lang,internalKey,templateKey,params,actives,createdAt,updatedAt&activation=true";
private static final String RULES_SEARCH_NO_HOTSPOT_URL; private static final String RULES_SEARCH_NO_HOTSPOT_URL;


static { static {
loadedRule.setName(r.getName()); loadedRule.setName(r.getName());
loadedRule.setSeverity(active.getSeverity()); loadedRule.setSeverity(active.getSeverity());
loadedRule.setCreatedAt(dateToLong(parseDateTime(active.getCreatedAt()))); loadedRule.setCreatedAt(dateToLong(parseDateTime(active.getCreatedAt())));
loadedRule.setUpdatedAt(dateToLong(parseDateTime(active.getUpdatedAt())));
loadedRule.setLanguage(r.getLang()); loadedRule.setLanguage(r.getLang());
loadedRule.setInternalKey(r.getInternalKey()); loadedRule.setInternalKey(r.getInternalKey());
if (r.hasTemplateKey()) { if (r.hasTemplateKey()) {

+ 9
- 0
sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/LoadedActiveRule.java View File

private String language; private String language;
private Map<String, String> params; private Map<String, String> params;
private long createdAt; private long createdAt;
private long updatedAt;
private String templateRuleKey; private String templateRuleKey;
private String internalKey; private String internalKey;


this.createdAt = createdAt; this.createdAt = createdAt;
} }


public long getUpdatedAt() {
return updatedAt;
}

public void setUpdatedAt(long updatedAt) {
this.updatedAt = updatedAt;
}

@CheckForNull @CheckForNull
public String getTemplateRuleKey() { public String getTemplateRuleKey() {
return templateRuleKey; return templateRuleKey;

+ 19
- 5
sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/ModuleIssuesTest.java View File

import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.fs.internal.TestInputFileBuilder; import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
import org.sonar.api.batch.rule.internal.ActiveRulesBuilder; import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
import org.sonar.api.batch.rule.internal.NewActiveRule;
import org.sonar.api.batch.rule.internal.RulesBuilder; import org.sonar.api.batch.rule.internal.RulesBuilder;
import org.sonar.api.batch.sensor.issue.internal.DefaultExternalIssue; import org.sonar.api.batch.sensor.issue.internal.DefaultExternalIssue;
import org.sonar.api.batch.sensor.issue.internal.DefaultIssue; import org.sonar.api.batch.sensor.issue.internal.DefaultIssue;
@Test @Test
public void ignore_null_rule_of_active_rule() { public void ignore_null_rule_of_active_rule() {
ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME); ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME);
activeRulesBuilder.create(SQUID_RULE_KEY).activate();
activeRulesBuilder.addRule(new NewActiveRule.Builder().setRuleKey(SQUID_RULE_KEY).build());
initModuleIssues(); initModuleIssues();


DefaultIssue issue = new DefaultIssue() DefaultIssue issue = new DefaultIssue()
@Test @Test
public void add_issue_to_cache() { public void add_issue_to_cache() {
ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME); ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME);
activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate();
activeRulesBuilder.addRule(new NewActiveRule.Builder()
.setRuleKey(SQUID_RULE_KEY)
.setSeverity(Severity.INFO)
.build());
initModuleIssues(); initModuleIssues();


DefaultIssue issue = new DefaultIssue() DefaultIssue issue = new DefaultIssue()
@Test @Test
public void use_severity_from_active_rule_if_no_severity_on_issue() { public void use_severity_from_active_rule_if_no_severity_on_issue() {
ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME); ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME);
activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate();
activeRulesBuilder.addRule(new NewActiveRule.Builder()
.setRuleKey(SQUID_RULE_KEY)
.setSeverity(Severity.INFO)
.build());
initModuleIssues(); initModuleIssues();


DefaultIssue issue = new DefaultIssue() DefaultIssue issue = new DefaultIssue()
@Test @Test
public void use_rule_name_if_no_message() { public void use_rule_name_if_no_message() {
ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME); ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME);
activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).setName(SQUID_RULE_NAME).activate();
activeRulesBuilder.addRule(new NewActiveRule.Builder()
.setRuleKey(SQUID_RULE_KEY)
.setSeverity(Severity.INFO)
.setName(SQUID_RULE_NAME)
.build());
initModuleIssues(); initModuleIssues();


DefaultIssue issue = new DefaultIssue() DefaultIssue issue = new DefaultIssue()
@Test @Test
public void filter_issue() { public void filter_issue() {
ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME); ruleBuilder.add(SQUID_RULE_KEY).setName(SQUID_RULE_NAME);
activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate();
activeRulesBuilder.addRule(new NewActiveRule.Builder()
.setRuleKey(SQUID_RULE_KEY)
.setSeverity(Severity.INFO)
.build());
initModuleIssues(); initModuleIssues();


DefaultIssue issue = new DefaultIssue() DefaultIssue issue = new DefaultIssue()

+ 9
- 2
sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ActiveRulesPublisherTest.java View File

import org.junit.Test; import org.junit.Test;
import org.junit.rules.TemporaryFolder; import org.junit.rules.TemporaryFolder;
import org.sonar.api.batch.rule.ActiveRules; import org.sonar.api.batch.rule.ActiveRules;
import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
import org.sonar.api.batch.rule.internal.DefaultActiveRules; import org.sonar.api.batch.rule.internal.DefaultActiveRules;
import org.sonar.api.batch.rule.internal.NewActiveRule; import org.sonar.api.batch.rule.internal.NewActiveRule;
import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.RuleKey;
File outputDir = temp.newFolder(); File outputDir = temp.newFolder();
ScannerReportWriter writer = new ScannerReportWriter(outputDir); ScannerReportWriter writer = new ScannerReportWriter(outputDir);


NewActiveRule ar = new ActiveRulesBuilder().create(RuleKey.of("java", "S001")).setSeverity("BLOCKER").setParam("p1", "v1");
NewActiveRule ar = new NewActiveRule.Builder()
.setRuleKey(RuleKey.of("java", "S001"))
.setSeverity("BLOCKER")
.setParam("p1", "v1")
.setCreatedAt(1_000L)
.setUpdatedAt(2_000L)
.build();
ActiveRules activeRules = new DefaultActiveRules(singletonList(ar)); ActiveRules activeRules = new DefaultActiveRules(singletonList(ar));


ActiveRulesPublisher underTest = new ActiveRulesPublisher(activeRules); ActiveRulesPublisher underTest = new ActiveRulesPublisher(activeRules);
assertThat(reportAr.getRuleRepository()).isEqualTo("java"); assertThat(reportAr.getRuleRepository()).isEqualTo("java");
assertThat(reportAr.getRuleKey()).isEqualTo("S001"); assertThat(reportAr.getRuleKey()).isEqualTo("S001");
assertThat(reportAr.getSeverity()).isEqualTo(Constants.Severity.BLOCKER); assertThat(reportAr.getSeverity()).isEqualTo(Constants.Severity.BLOCKER);
assertThat(reportAr.getCreatedAt()).isEqualTo(1_000L);
assertThat(reportAr.getUpdatedAt()).isEqualTo(2_000L);
assertThat(reportAr.getParamsByKeyMap()).hasSize(1); assertThat(reportAr.getParamsByKeyMap()).hasSize(1);
assertThat(reportAr.getParamsByKeyMap().entrySet().iterator().next().getKey()).isEqualTo("p1"); assertThat(reportAr.getParamsByKeyMap().entrySet().iterator().next().getKey()).isEqualTo("p1");
assertThat(reportAr.getParamsByKeyMap().entrySet().iterator().next().getValue()).isEqualTo("v1"); assertThat(reportAr.getParamsByKeyMap().entrySet().iterator().next().getValue()).isEqualTo("v1");

+ 24
- 0
sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/ActiveRulesProviderTest.java View File

package org.sonar.scanner.rule; package org.sonar.scanner.rule;


import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.util.Date; import java.util.Date;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import org.assertj.core.groups.Tuple;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mock; import org.mockito.Mock;
verifyNoMoreInteractions(loader); verifyNoMoreInteractions(loader);
} }


@Test
public void testParamsAreTransformed() {
LoadedActiveRule r1 = mockRule("rule1");
LoadedActiveRule r2 = mockRule("rule2");
r2.setParams(ImmutableMap.of("foo1", "bar1", "foo2", "bar2"));

List<LoadedActiveRule> qpRules = ImmutableList.of(r1, r2);
when(loader.load(eq("qp"))).thenReturn(qpRules);

ModuleQProfiles profiles = mockProfiles("qp");
ActiveRules activeRules = provider.provide(loader, profiles);

assertThat(activeRules.findAll()).hasSize(2);
assertThat(activeRules.findAll()).extracting("ruleKey", "params").containsOnly(
Tuple.tuple(RuleKey.of("rule1", "rule1"), ImmutableMap.of()),
Tuple.tuple(RuleKey.of("rule2", "rule2"), ImmutableMap.of("foo1", "bar1", "foo2", "bar2"))
);

verify(loader).load(eq("qp"));
verifyNoMoreInteractions(loader);
}

private static ModuleQProfiles mockProfiles(String... keys) { private static ModuleQProfiles mockProfiles(String... keys) {
List<QualityProfile> profiles = new LinkedList<>(); List<QualityProfile> profiles = new LinkedList<>();



+ 2
- 1
sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/DefaultActiveRulesLoaderTest.java View File

} }


private String urlOfPage(int page, boolean noHotspots) { private String urlOfPage(int page, boolean noHotspots) {
return "/api/rules/search.protobuf?f=repo,name,severity,lang,internalKey,templateKey,params,actives,createdAt&activation=true"
return "/api/rules/search.protobuf?f=repo,name,severity,lang,internalKey,templateKey,params,actives,createdAt,updatedAt&activation=true"
+ (noHotspots ? "&types=CODE_SMELL,BUG,VULNERABILITY" : "") + "&qprofile=c%2B-test_c%2B-values-17445&p=" + page + (noHotspots ? "&types=CODE_SMELL,BUG,VULNERABILITY" : "") + "&qprofile=c%2B-test_c%2B-values-17445&p=" + page
+ "&ps=500"; + "&ps=500";
} }


Active.Builder activeBuilder = Active.newBuilder(); Active.Builder activeBuilder = Active.newBuilder();
activeBuilder.setCreatedAt("2014-05-27T15:50:45+0100"); activeBuilder.setCreatedAt("2014-05-27T15:50:45+0100");
activeBuilder.setUpdatedAt("2014-05-27T15:50:45+0100");
if (EXAMPLE_KEY.equals(key)) { if (EXAMPLE_KEY.equals(key)) {
activeBuilder.addParams(Rules.Active.Param.newBuilder().setKey(FORMAT_KEY).setValue(FORMAT_VALUE)); activeBuilder.addParams(Rules.Active.Param.newBuilder().setKey(FORMAT_KEY).setValue(FORMAT_VALUE));
activeBuilder.setSeverity(SEVERITY_VALUE); activeBuilder.setSeverity(SEVERITY_VALUE);

+ 5
- 1
sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/RulesProfileProviderTest.java View File

import java.util.Arrays; import java.util.Arrays;
import org.junit.Test; import org.junit.Test;
import org.sonar.api.batch.rule.internal.ActiveRulesBuilder; import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
import org.sonar.api.batch.rule.internal.NewActiveRule;
import org.sonar.api.config.internal.MapSettings; import org.sonar.api.config.internal.MapSettings;
import org.sonar.api.profiles.RulesProfile; import org.sonar.api.profiles.RulesProfile;
import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.RuleKey;
QProfile qProfile = new QProfile("java-sw", "Sonar way", "java", null); QProfile qProfile = new QProfile("java-sw", "Sonar way", "java", null);
when(qProfiles.findAll()).thenReturn(Arrays.asList(qProfile)); when(qProfiles.findAll()).thenReturn(Arrays.asList(qProfile));
ActiveRulesBuilder activeRulesBuilder = new ActiveRulesBuilder(); ActiveRulesBuilder activeRulesBuilder = new ActiveRulesBuilder();
activeRulesBuilder.create(RuleKey.of("java", "S001")).setTemplateRuleKey("T001").setLanguage("java").activate();
activeRulesBuilder.addRule(new NewActiveRule.Builder()
.setRuleKey(RuleKey.of("java", "S001"))
.setTemplateRuleKey("T001").setLanguage("java")
.build());


RulesProfile profile = provider.provide(qProfiles, activeRulesBuilder.build(), settings.asConfig()); RulesProfile profile = provider.provide(qProfiles, activeRulesBuilder.build(), settings.asConfig());



+ 4
- 6
sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/SensorOptimizerTest.java View File

import org.sonar.api.batch.fs.internal.TestInputFileBuilder; import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
import org.sonar.api.batch.rule.ActiveRules; import org.sonar.api.batch.rule.ActiveRules;
import org.sonar.api.batch.rule.internal.ActiveRulesBuilder; import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
import org.sonar.api.batch.rule.internal.NewActiveRule;
import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor; import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor;
import org.sonar.api.config.internal.MapSettings; import org.sonar.api.config.internal.MapSettings;
import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.RuleKey;
assertThat(optimizer.shouldExecute(descriptor)).isFalse(); assertThat(optimizer.shouldExecute(descriptor)).isFalse();


ActiveRules activeRules = new ActiveRulesBuilder() ActiveRules activeRules = new ActiveRulesBuilder()
.create(RuleKey.of("repo1", "foo"))
.activate()
.addRule(new NewActiveRule.Builder().setRuleKey(RuleKey.of("repo1", "foo")).build())
.build(); .build();
optimizer = new SensorOptimizer(fs, activeRules, settings.asConfig()); optimizer = new SensorOptimizer(fs, activeRules, settings.asConfig());


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


activeRules = new ActiveRulesBuilder() activeRules = new ActiveRulesBuilder()
.create(RuleKey.of("repo1", "foo"))
.activate()
.create(RuleKey.of("squid", "rule"))
.activate()
.addRule(new NewActiveRule.Builder().setRuleKey(RuleKey.of("repo1", "foo")).build())
.addRule(new NewActiveRule.Builder().setRuleKey(RuleKey.of("squid", "rule")).build())
.build(); .build();
optimizer = new SensorOptimizer(fs, activeRules, settings.asConfig()); optimizer = new SensorOptimizer(fs, activeRules, settings.asConfig());
assertThat(optimizer.shouldExecute(descriptor)).isTrue(); assertThat(optimizer.shouldExecute(descriptor)).isTrue();

+ 1
- 0
sonar-scanner-protocol/src/main/protobuf/scanner_report.proto View File

Severity severity = 3; Severity severity = 3;
map<string,string> params_by_key = 4; map<string,string> params_by_key = 4;
int64 createdAt = 5; int64 createdAt = 5;
int64 updatedAt = 6;
} }


message ComponentLink { message ComponentLink {

+ 1
- 0
sonar-ws/src/main/protobuf/ws-rules.proto View File

optional string unusedParent = 4; optional string unusedParent = 4;
repeated Param params = 5; repeated Param params = 5;
optional string createdAt = 6; optional string createdAt = 6;
optional string updatedAt = 7;


message Param { message Param {
optional string key = 1; optional string key = 1;

Loading…
Cancel
Save