@@ -33,6 +33,7 @@ import org.sonar.ce.task.projectanalysis.component.Component; | |||
import org.sonar.ce.task.projectanalysis.filemove.AddedFileRepository; | |||
import org.sonar.ce.task.projectanalysis.qualityprofile.ActiveRule; | |||
import org.sonar.ce.task.projectanalysis.qualityprofile.ActiveRulesHolder; | |||
import org.sonar.ce.task.projectanalysis.qualityprofile.QProfileStatusRepository; | |||
import org.sonar.ce.task.projectanalysis.scm.Changeset; | |||
import org.sonar.ce.task.projectanalysis.scm.ScmInfo; | |||
import org.sonar.ce.task.projectanalysis.scm.ScmInfoRepository; | |||
@@ -40,6 +41,7 @@ import org.sonar.core.issue.DefaultIssue; | |||
import org.sonar.core.issue.IssueChangeContext; | |||
import org.sonar.server.issue.IssueFieldsSetter; | |||
import static org.sonar.ce.task.projectanalysis.qualityprofile.QProfileStatusRepository.Status.UNCHANGED; | |||
import static org.sonar.core.issue.IssueChangeContext.createScan; | |||
/** | |||
@@ -55,10 +57,11 @@ public class IssueCreationDateCalculator extends IssueVisitor { | |||
private final ActiveRulesHolder activeRulesHolder; | |||
private final RuleRepository ruleRepository; | |||
private final AddedFileRepository addedFileRepository; | |||
private QProfileStatusRepository qProfileStatusRepository; | |||
public IssueCreationDateCalculator(AnalysisMetadataHolder analysisMetadataHolder, ScmInfoRepository scmInfoRepository, | |||
IssueFieldsSetter issueUpdater, ActiveRulesHolder activeRulesHolder, RuleRepository ruleRepository, | |||
AddedFileRepository addedFileRepository) { | |||
AddedFileRepository addedFileRepository, QProfileStatusRepository qProfileStatusRepository) { | |||
this.scmInfoRepository = scmInfoRepository; | |||
this.issueUpdater = issueUpdater; | |||
this.analysisMetadataHolder = analysisMetadataHolder; | |||
@@ -66,6 +69,7 @@ public class IssueCreationDateCalculator extends IssueVisitor { | |||
this.changeContext = createScan(new Date(analysisMetadataHolder.getAnalysisDate())); | |||
this.activeRulesHolder = activeRulesHolder; | |||
this.addedFileRepository = addedFileRepository; | |||
this.qProfileStatusRepository = qProfileStatusRepository; | |||
} | |||
@Override | |||
@@ -89,12 +93,22 @@ public class IssueCreationDateCalculator extends IssueVisitor { | |||
// Rule can't be inactive (see contract of IssueVisitor) | |||
ActiveRule activeRule = activeRulesHolder.get(issue.getRuleKey()).get(); | |||
if (activeRuleIsNewOrChanged(activeRule, lastAnalysisOptional.get()) | |||
|| ruleImplementationChanged(activeRule.getRuleKey(), activeRule.getPluginKey(), lastAnalysisOptional.get())) { | |||
|| ruleImplementationChanged(activeRule.getRuleKey(), activeRule.getPluginKey(), lastAnalysisOptional.get()) | |||
|| qualityProfileChanged(activeRule.getQProfileKey())) { | |||
backdateIssue(component, issue); | |||
} | |||
} | |||
} | |||
private boolean qualityProfileChanged(@Nullable String qpKey) { | |||
// Support issue from report created before scanner protocol update -> no backdating | |||
if (qpKey == null) { | |||
return false; | |||
} | |||
return qProfileStatusRepository.get(qpKey).filter(s -> !s.equals(UNCHANGED)).isPresent(); | |||
} | |||
private boolean isNewFile(Component component) { | |||
return component.getType() == Component.Type.FILE && addedFileRepository.isAdded(component); | |||
} |
@@ -33,13 +33,15 @@ public class ActiveRule { | |||
private final Map<String, String> params; | |||
private final String pluginKey; | |||
private final long updatedAt; | |||
private final String qProfileKey; | |||
public ActiveRule(RuleKey ruleKey, String severity, Map<String, String> params, long updatedAt, @Nullable String pluginKey) { | |||
public ActiveRule(RuleKey ruleKey, String severity, Map<String, String> params, long updatedAt, @Nullable String pluginKey, @Nullable String qProfileKey) { | |||
this.ruleKey = ruleKey; | |||
this.severity = severity; | |||
this.pluginKey = pluginKey; | |||
this.params = ImmutableMap.copyOf(params); | |||
this.updatedAt = updatedAt; | |||
this.qProfileKey = qProfileKey; | |||
} | |||
public RuleKey getRuleKey() { | |||
@@ -62,4 +64,9 @@ public class ActiveRule { | |||
public String getPluginKey() { | |||
return pluginKey; | |||
} | |||
@CheckForNull | |||
public String getQProfileKey() { | |||
return qProfileKey; | |||
} | |||
} |
@@ -35,6 +35,8 @@ import org.sonar.ce.task.step.ComputationStep; | |||
import org.sonar.core.util.CloseableIterator; | |||
import org.sonar.scanner.protocol.output.ScannerReport; | |||
import static com.google.common.base.Strings.emptyToNull; | |||
public class LoadQualityProfilesStep implements ComputationStep { | |||
private final BatchReportReader batchReportReader; | |||
@@ -55,8 +57,7 @@ public class LoadQualityProfilesStep implements ComputationStep { | |||
ScannerReport.ActiveRule scannerReportActiveRule = batchActiveRules.next(); | |||
Optional<Rule> rule = ruleRepository.findByKey(RuleKey.of(scannerReportActiveRule.getRuleRepository(), scannerReportActiveRule.getRuleKey())); | |||
if (rule.isPresent() && rule.get().getStatus() != RuleStatus.REMOVED && !rule.get().isExternal()) { | |||
ActiveRule activeRule = convert(scannerReportActiveRule, rule.get()); | |||
activeRules.add(activeRule); | |||
activeRules.add(convert(scannerReportActiveRule, rule.get())); | |||
} | |||
} | |||
} | |||
@@ -73,6 +74,6 @@ public class LoadQualityProfilesStep implements ComputationStep { | |||
RuleKey key = RuleKey.of(input.getRuleRepository(), input.getRuleKey()); | |||
Map<String, String> params = new HashMap<>(input.getParamsByKeyMap()); | |||
long updatedAt = input.getUpdatedAt(); | |||
return new ActiveRule(key, input.getSeverity().name(), params, updatedAt == 0 ? input.getCreatedAt() : updatedAt, rule.getPluginKey()); | |||
return new ActiveRule(key, input.getSeverity().name(), params, updatedAt == 0 ? input.getCreatedAt() : updatedAt, rule.getPluginKey(), emptyToNull(input.getQProfileKey())); | |||
} | |||
} |
@@ -40,12 +40,16 @@ import org.sonar.ce.task.projectanalysis.language.LanguageRepository; | |||
import org.sonar.ce.task.projectanalysis.measure.Measure; | |||
import org.sonar.ce.task.projectanalysis.measure.MeasureRepository; | |||
import org.sonar.ce.task.projectanalysis.metric.MetricRepository; | |||
import org.sonar.ce.task.projectanalysis.qualityprofile.QProfileStatusRepository; | |||
import org.sonar.ce.task.step.ComputationStep; | |||
import org.sonar.core.util.UtcDateUtils; | |||
import org.sonar.server.qualityprofile.QPMeasureData; | |||
import org.sonar.server.qualityprofile.QualityProfile; | |||
import static org.sonar.ce.task.projectanalysis.component.ComponentVisitor.Order.POST_ORDER; | |||
import static org.sonar.ce.task.projectanalysis.qualityprofile.QProfileStatusRepository.Status.ADDED; | |||
import static org.sonar.ce.task.projectanalysis.qualityprofile.QProfileStatusRepository.Status.REMOVED; | |||
import static org.sonar.ce.task.projectanalysis.qualityprofile.QProfileStatusRepository.Status.UPDATED; | |||
/** | |||
* Computation of quality profile events | |||
@@ -58,15 +62,17 @@ public class QualityProfileEventsStep implements ComputationStep { | |||
private final MeasureRepository measureRepository; | |||
private final EventRepository eventRepository; | |||
private final LanguageRepository languageRepository; | |||
private QProfileStatusRepository qProfileStatusRepository; | |||
public QualityProfileEventsStep(TreeRootHolder treeRootHolder, | |||
MetricRepository metricRepository, MeasureRepository measureRepository, LanguageRepository languageRepository, | |||
EventRepository eventRepository) { | |||
EventRepository eventRepository, QProfileStatusRepository qProfileStatusRepository) { | |||
this.treeRootHolder = treeRootHolder; | |||
this.metricRepository = metricRepository; | |||
this.measureRepository = measureRepository; | |||
this.eventRepository = eventRepository; | |||
this.languageRepository = languageRepository; | |||
this.qProfileStatusRepository = qProfileStatusRepository; | |||
} | |||
@Override | |||
@@ -87,7 +93,7 @@ public class QualityProfileEventsStep implements ComputationStep { | |||
return; | |||
} | |||
// Load base profiles | |||
// Load profiles used in current analysis for which at least one file of the corresponding language exists | |||
Optional<Measure> rawMeasure = measureRepository.getRawMeasure(projectComponent, metricRepository.getByKey(CoreMetrics.QUALITY_PROFILES_KEY)); | |||
if (!rawMeasure.isPresent()) { | |||
// No qualify profile computed on the project | |||
@@ -97,7 +103,7 @@ public class QualityProfileEventsStep implements ComputationStep { | |||
Map<String, QualityProfile> baseProfiles = parseJsonData(baseMeasure.get()); | |||
detectNewOrUpdatedProfiles(projectComponent, baseProfiles, rawProfiles); | |||
detectNoMoreUsedProfiles(projectComponent, baseProfiles, rawProfiles); | |||
detectNoMoreUsedProfiles(projectComponent, baseProfiles); | |||
} | |||
private static Map<String, QualityProfile> parseJsonData(Measure measure) { | |||
@@ -108,9 +114,9 @@ public class QualityProfileEventsStep implements ComputationStep { | |||
return QPMeasureData.fromJson(data).getProfilesByKey(); | |||
} | |||
private void detectNoMoreUsedProfiles(Component context, Map<String, QualityProfile> baseProfiles, Map<String, QualityProfile> rawProfiles) { | |||
private void detectNoMoreUsedProfiles(Component context, Map<String, QualityProfile> baseProfiles) { | |||
for (QualityProfile baseProfile : baseProfiles.values()) { | |||
if (!rawProfiles.containsKey(baseProfile.getQpKey())) { | |||
if (qProfileStatusRepository.get(baseProfile.getQpKey()).filter(REMOVED::equals).isPresent()) { | |||
markAsRemoved(context, baseProfile); | |||
} | |||
} | |||
@@ -118,12 +124,13 @@ public class QualityProfileEventsStep implements ComputationStep { | |||
private void detectNewOrUpdatedProfiles(Component component, Map<String, QualityProfile> baseProfiles, Map<String, QualityProfile> rawProfiles) { | |||
for (QualityProfile profile : rawProfiles.values()) { | |||
QualityProfile baseProfile = baseProfiles.get(profile.getQpKey()); | |||
if (baseProfile == null) { | |||
markAsAdded(component, profile); | |||
} else if (profile.getRulesUpdatedAt().after(baseProfile.getRulesUpdatedAt())) { | |||
markAsChanged(component, baseProfile, profile); | |||
} | |||
qProfileStatusRepository.get(profile.getQpKey()).ifPresent(status -> { | |||
if (status.equals(ADDED)) { | |||
markAsAdded(component, profile); | |||
} else if (status.equals(UPDATED)) { | |||
markAsChanged(component, baseProfiles.get(profile.getQpKey()), profile); | |||
} | |||
}); | |||
} | |||
} | |||
@@ -28,6 +28,8 @@ import java.util.HashMap; | |||
import java.util.Map; | |||
import java.util.Optional; | |||
import java.util.function.BiConsumer; | |||
import java.util.stream.Stream; | |||
import org.apache.commons.lang.ArrayUtils; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
@@ -40,6 +42,7 @@ import org.sonar.ce.task.projectanalysis.component.Component; | |||
import org.sonar.ce.task.projectanalysis.filemove.AddedFileRepository; | |||
import org.sonar.ce.task.projectanalysis.qualityprofile.ActiveRule; | |||
import org.sonar.ce.task.projectanalysis.qualityprofile.ActiveRulesHolder; | |||
import org.sonar.ce.task.projectanalysis.qualityprofile.QProfileStatusRepository; | |||
import org.sonar.ce.task.projectanalysis.scm.Changeset; | |||
import org.sonar.ce.task.projectanalysis.scm.ScmInfo; | |||
import org.sonar.ce.task.projectanalysis.scm.ScmInfoRepository; | |||
@@ -60,6 +63,7 @@ import static org.mockito.Mockito.never; | |||
import static org.mockito.Mockito.verify; | |||
import static org.mockito.Mockito.verifyZeroInteractions; | |||
import static org.mockito.Mockito.when; | |||
import static org.sonar.ce.task.projectanalysis.qualityprofile.QProfileStatusRepository.Status.UNCHANGED; | |||
@RunWith(DataProviderRunner.class) | |||
public class IssueCreationDateCalculatorTest { | |||
@@ -84,6 +88,7 @@ public class IssueCreationDateCalculatorTest { | |||
private Map<String, ScannerPlugin> scannerPlugins = new HashMap<>(); | |||
private RuleRepository ruleRepository = mock(RuleRepository.class); | |||
private AddedFileRepository addedFileRepository = mock(AddedFileRepository.class); | |||
private QProfileStatusRepository qProfileStatusRepository = mock(QProfileStatusRepository.class); | |||
private ScmInfo scmInfo; | |||
private Rule rule = mock(Rule.class); | |||
@@ -92,15 +97,14 @@ public class IssueCreationDateCalculatorTest { | |||
analysisMetadataHolder.setScannerPluginsByKey(scannerPlugins); | |||
analysisMetadataHolder.setAnalysisDate(new Date()); | |||
when(component.getUuid()).thenReturn(COMPONENT_UUID); | |||
underTest = new IssueCreationDateCalculator(analysisMetadataHolder, scmInfoRepository, issueUpdater, activeRulesHolder, ruleRepository, addedFileRepository); | |||
underTest = new IssueCreationDateCalculator(analysisMetadataHolder, scmInfoRepository, issueUpdater, activeRulesHolder, ruleRepository, addedFileRepository, qProfileStatusRepository); | |||
when(ruleRepository.findByKey(ruleKey)).thenReturn(Optional.of(rule)); | |||
when(activeRulesHolder.get(any(RuleKey.class))) | |||
.thenReturn(Optional.empty()); | |||
when(activeRulesHolder.get(ruleKey)) | |||
.thenReturn(Optional.of(activeRule)); | |||
when(issue.getRuleKey()) | |||
.thenReturn(ruleKey); | |||
when(activeRulesHolder.get(any(RuleKey.class))).thenReturn(Optional.empty()); | |||
when(activeRulesHolder.get(ruleKey)).thenReturn(Optional.of(activeRule)); | |||
when(activeRule.getQProfileKey()).thenReturn("qpKey"); | |||
when(issue.getRuleKey()).thenReturn(ruleKey); | |||
when(qProfileStatusRepository.get(any())).thenReturn(Optional.of(UNCHANGED)); | |||
} | |||
@Test | |||
@@ -251,6 +255,22 @@ public class IssueCreationDateCalculatorTest { | |||
assertChangeOfCreationDateTo(expectedDate); | |||
} | |||
@Test | |||
@UseDataProvider("backdatingDateAndChangedQPStatusCases") | |||
public void should_backdate_if_qp_of_the_rule_which_raised_the_issue_has_changed(BiConsumer<DefaultIssue, ScmInfo> configure, long expectedDate, QProfileStatusRepository.Status status) { | |||
previousAnalysisWas(2000L); | |||
currentAnalysisIs(3000L); | |||
makeIssueNew(); | |||
configure.accept(issue, createMockScmInfo()); | |||
changeQualityProfile(status); | |||
run(); | |||
assertChangeOfCreationDateTo(expectedDate); | |||
} | |||
@Test | |||
@UseDataProvider("backdatingDateCases") | |||
public void should_backdate_if_scm_is_available_and_plugin_is_new(BiConsumer<DefaultIssue, ScmInfo> configure, long expectedDate) { | |||
@@ -302,6 +322,16 @@ public class IssueCreationDateCalculatorTest { | |||
verifyZeroInteractions(activeRulesHolder); | |||
} | |||
@DataProvider | |||
public static Object[][] backdatingDateAndChangedQPStatusCases() { | |||
return Stream.of(backdatingDateCases()) | |||
.flatMap(datesCases -> | |||
Stream.of(QProfileStatusRepository.Status.values()) | |||
.filter(s -> !UNCHANGED.equals(s)) | |||
.map(s -> ArrayUtils.add(datesCases, s))) | |||
.toArray(Object[][]::new); | |||
} | |||
@DataProvider | |||
public static Object[][] backdatingDateCases() { | |||
return new Object[][] { | |||
@@ -401,6 +431,10 @@ public class IssueCreationDateCalculatorTest { | |||
.thenReturn(false); | |||
} | |||
private void changeQualityProfile(QProfileStatusRepository.Status status) { | |||
when(qProfileStatusRepository.get(any())).thenReturn(Optional.of(status)); | |||
} | |||
private void setIssueBelongToNonExistingRule() { | |||
when(issue.getRuleKey()) | |||
.thenReturn(RuleKey.of("repo", "disabled")); |
@@ -367,6 +367,6 @@ public class TrackerRawInputFactoryTest { | |||
} | |||
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, null, "qp1")); | |||
} | |||
} |
@@ -44,6 +44,7 @@ import static org.sonar.ce.task.projectanalysis.component.ReportComponent.DUMB_P | |||
public class CommentDensityRuleTest { | |||
private static final String PLUGIN_KEY = "java"; | |||
private static final String QP_KEY = "qp1"; | |||
static RuleKey RULE_KEY = RuleKey.of(CommonRuleKeys.commonRepositoryForLang(PLUGIN_KEY), CommonRuleKeys.INSUFFICIENT_COMMENT_DENSITY); | |||
@@ -77,7 +78,7 @@ public class CommentDensityRuleTest { | |||
@Test | |||
public void no_issues_if_enough_comments() { | |||
activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, ImmutableMap.of(CommonRuleKeys.INSUFFICIENT_COMMENT_DENSITY_PROPERTY, "25"), 1_000L, PLUGIN_KEY)); | |||
activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL, ImmutableMap.of(CommonRuleKeys.INSUFFICIENT_COMMENT_DENSITY_PROPERTY, "25"), 1_000L, PLUGIN_KEY, QP_KEY)); | |||
measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.COMMENT_LINES_DENSITY_KEY, Measure.newMeasureBuilder().create(90.0, 1)); | |||
DefaultIssue issue = underTest.processFile(FILE, PLUGIN_KEY); | |||
@@ -135,7 +136,7 @@ public class CommentDensityRuleTest { | |||
} | |||
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, PLUGIN_KEY, QP_KEY)); | |||
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.NCLOC_KEY, Measure.newMeasureBuilder().create(ncloc)); |
@@ -32,13 +32,14 @@ import static org.assertj.core.api.Assertions.assertThat; | |||
public class CommonRuleTest { | |||
private static final String PLUGIN_KEY = "java"; | |||
private static final String QP_KEY = "qp1"; | |||
@Rule | |||
public ExpectedException thrown = ExpectedException.none(); | |||
@Test | |||
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, PLUGIN_KEY, QP_KEY); | |||
double minDensity = CommonRule.getMinDensityParam(activeRule, "minDensity"); | |||
assertThat(minDensity).isEqualTo(30.5); | |||
@@ -49,7 +50,7 @@ public class CommonRuleTest { | |||
thrown.expect(IllegalStateException.class); | |||
thrown.expectMessage("Required parameter [minDensity] is missing on rule [xoo:x1]"); | |||
ActiveRule activeRule = new ActiveRule(RuleTesting.XOO_X1, Severity.MAJOR, ImmutableMap.of(), 1_000L, PLUGIN_KEY); | |||
ActiveRule activeRule = new ActiveRule(RuleTesting.XOO_X1, Severity.MAJOR, ImmutableMap.of(), 1_000L, PLUGIN_KEY, QP_KEY); | |||
CommonRule.getMinDensityParam(activeRule, "minDensity"); | |||
} | |||
@@ -58,7 +59,7 @@ public class CommonRuleTest { | |||
thrown.expect(IllegalStateException.class); | |||
thrown.expectMessage("Minimum density of rule [xoo:x1] is incorrect. Got [-30.5] but must be between 0 and 100."); | |||
ActiveRule activeRule = new ActiveRule(RuleTesting.XOO_X1, Severity.MAJOR, ImmutableMap.of("minDensity", "-30.5"), 1_000L, PLUGIN_KEY); | |||
ActiveRule activeRule = new ActiveRule(RuleTesting.XOO_X1, Severity.MAJOR, ImmutableMap.of("minDensity", "-30.5"), 1_000L, PLUGIN_KEY, QP_KEY); | |||
CommonRule.getMinDensityParam(activeRule, "minDensity"); | |||
} | |||
@@ -67,7 +68,7 @@ public class CommonRuleTest { | |||
thrown.expect(IllegalStateException.class); | |||
thrown.expectMessage("Minimum density of rule [xoo:x1] is incorrect. Got [305] but must be between 0 and 100."); | |||
ActiveRule activeRule = new ActiveRule(RuleTesting.XOO_X1, Severity.MAJOR, ImmutableMap.of("minDensity", "305"), 1_000L, PLUGIN_KEY); | |||
ActiveRule activeRule = new ActiveRule(RuleTesting.XOO_X1, Severity.MAJOR, ImmutableMap.of("minDensity", "305"), 1_000L, PLUGIN_KEY, QP_KEY); | |||
CommonRule.getMinDensityParam(activeRule, "minDensity"); | |||
} | |||
} |
@@ -42,6 +42,7 @@ import static org.assertj.core.api.Assertions.assertThat; | |||
public abstract class CoverageRuleTest { | |||
private static final String PLUGIN_KEY = "java"; | |||
private static final String QP_KEY = "qp1"; | |||
static ReportComponent FILE = ReportComponent.builder(Component.Type.FILE, 1) | |||
.setFileAttributes(new FileAttributes(false, "java", 1)) | |||
@@ -86,7 +87,7 @@ public abstract class CoverageRuleTest { | |||
@Test | |||
public void no_issue_if_enough_coverage() { | |||
activeRuleHolder.put(new ActiveRule(getRuleKey(), Severity.CRITICAL, ImmutableMap.of(getMinPropertyKey(), "65"), 1_000L, PLUGIN_KEY)); | |||
activeRuleHolder.put(new ActiveRule(getRuleKey(), Severity.CRITICAL, ImmutableMap.of(getMinPropertyKey(), "65"), 1_000L, PLUGIN_KEY, QP_KEY)); | |||
measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), getCoverageMetricKey(), Measure.newMeasureBuilder().create(90.0, 1)); | |||
DefaultIssue issue = underTest.processFile(FILE, "java"); | |||
@@ -96,7 +97,7 @@ public abstract class CoverageRuleTest { | |||
@Test | |||
public void issue_if_coverage_is_too_low() { | |||
activeRuleHolder.put(new ActiveRule(getRuleKey(), Severity.CRITICAL, ImmutableMap.of(getMinPropertyKey(), "65"), 1_000L, PLUGIN_KEY)); | |||
activeRuleHolder.put(new ActiveRule(getRuleKey(), Severity.CRITICAL, ImmutableMap.of(getMinPropertyKey(), "65"), 1_000L, PLUGIN_KEY, QP_KEY)); | |||
measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), getCoverageMetricKey(), Measure.newMeasureBuilder().create(20.0, 1)); | |||
measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), getUncoveredMetricKey(), Measure.newMeasureBuilder().create(40)); | |||
measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), getToCoverMetricKey(), Measure.newMeasureBuilder().create(50)); | |||
@@ -114,7 +115,7 @@ public abstract class CoverageRuleTest { | |||
@Test | |||
public void no_issue_if_coverage_is_not_set() { | |||
activeRuleHolder.put(new ActiveRule(getRuleKey(), Severity.CRITICAL, ImmutableMap.of(getMinPropertyKey(), "65"), 1_000L, PLUGIN_KEY)); | |||
activeRuleHolder.put(new ActiveRule(getRuleKey(), Severity.CRITICAL, ImmutableMap.of(getMinPropertyKey(), "65"), 1_000L, PLUGIN_KEY, QP_KEY)); | |||
DefaultIssue issue = underTest.processFile(FILE, "java"); | |||
@@ -42,6 +42,7 @@ import static org.assertj.core.api.Assertions.assertThat; | |||
public class DuplicatedBlockRuleTest { | |||
private static final String PLUGIN_KEY = "java"; | |||
private static final String QP_KEY = "qp1"; | |||
static RuleKey RULE_KEY = RuleKey.of(CommonRuleKeys.commonRepositoryForLang("java"), CommonRuleKeys.DUPLICATED_BLOCKS); | |||
@@ -66,7 +67,7 @@ public class DuplicatedBlockRuleTest { | |||
@Test | |||
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, PLUGIN_KEY, QP_KEY)); | |||
measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.DUPLICATED_BLOCKS_KEY, Measure.newMeasureBuilder().create(0)); | |||
DefaultIssue issue = underTest.processFile(FILE, "java"); | |||
@@ -76,7 +77,7 @@ public class DuplicatedBlockRuleTest { | |||
@Test | |||
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, PLUGIN_KEY, QP_KEY)); | |||
measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.DUPLICATED_BLOCKS_KEY, Measure.newMeasureBuilder().create(3)); | |||
DefaultIssue issue = underTest.processFile(FILE, "java"); |
@@ -42,6 +42,7 @@ import static org.assertj.core.api.Assertions.assertThat; | |||
public class SkippedTestRuleTest { | |||
private static final String PLUGIN_KEY = "java"; | |||
private static final String QP_KEY = "qp1"; | |||
static RuleKey RULE_KEY = RuleKey.of(CommonRuleKeys.commonRepositoryForLang("java"), CommonRuleKeys.SKIPPED_UNIT_TESTS); | |||
@@ -67,7 +68,7 @@ public class SkippedTestRuleTest { | |||
@Test | |||
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, PLUGIN_KEY, QP_KEY)); | |||
measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.SKIPPED_TESTS_KEY, Measure.newMeasureBuilder().create(2)); | |||
DefaultIssue issue = underTest.processFile(FILE, "java"); | |||
@@ -80,7 +81,7 @@ public class SkippedTestRuleTest { | |||
@Test | |||
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, PLUGIN_KEY, QP_KEY)); | |||
measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.SKIPPED_TESTS_KEY, Measure.newMeasureBuilder().create(0)); | |||
DefaultIssue issue = underTest.processFile(FILE, "java"); | |||
@@ -90,7 +91,7 @@ public class SkippedTestRuleTest { | |||
@Test | |||
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, PLUGIN_KEY, QP_KEY)); | |||
DefaultIssue issue = underTest.processFile(FILE, "java"); | |||
@@ -43,6 +43,7 @@ import static org.sonar.ce.task.projectanalysis.component.ReportComponent.DUMB_P | |||
public class TestErrorRuleTest { | |||
private static final String PLUGIN_KEY = "java"; | |||
private static final String QP_KEY = "qp1"; | |||
static RuleKey RULE_KEY = RuleKey.of(CommonRuleKeys.commonRepositoryForLang("java"), CommonRuleKeys.FAILED_UNIT_TESTS); | |||
@@ -69,7 +70,7 @@ public class TestErrorRuleTest { | |||
@Test | |||
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, PLUGIN_KEY, QP_KEY)); | |||
measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.TEST_ERRORS_KEY, Measure.newMeasureBuilder().create(2)); | |||
measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.TEST_FAILURES_KEY, Measure.newMeasureBuilder().create(1)); | |||
@@ -83,7 +84,7 @@ public class TestErrorRuleTest { | |||
@Test | |||
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, PLUGIN_KEY, QP_KEY)); | |||
measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.TEST_ERRORS_KEY, Measure.newMeasureBuilder().create(0)); | |||
measureRepository.addRawMeasure(FILE.getReportAttributes().getRef(), CoreMetrics.TEST_FAILURES_KEY, Measure.newMeasureBuilder().create(0)); | |||
@@ -94,7 +95,7 @@ public class TestErrorRuleTest { | |||
@Test | |||
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, PLUGIN_KEY, QP_KEY)); | |||
DefaultIssue issue = underTest.processFile(FILE, "java"); | |||
@@ -37,6 +37,7 @@ public class ActiveRulesHolderImplTest { | |||
private static final long SOME_DATE = 1_000L; | |||
static final RuleKey RULE_KEY = RuleKey.of("squid", "S001"); | |||
private static final String QP_KEY = "qp1"; | |||
@Rule | |||
public ExpectedException thrown = ExpectedException.none(); | |||
@@ -52,7 +53,7 @@ public class ActiveRulesHolderImplTest { | |||
@Test | |||
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, PLUGIN_KEY, QP_KEY))); | |||
Optional<ActiveRule> activeRule = underTest.get(RULE_KEY); | |||
assertThat(activeRule.isPresent()).isTrue(); | |||
@@ -65,7 +66,7 @@ public class ActiveRulesHolderImplTest { | |||
thrown.expect(IllegalStateException.class); | |||
thrown.expectMessage("Active rules have already been initialized"); | |||
underTest.set(asList(new ActiveRule(RULE_KEY, Severity.BLOCKER, Collections.emptyMap(), SOME_DATE, PLUGIN_KEY))); | |||
underTest.set(asList(new ActiveRule(RULE_KEY, Severity.BLOCKER, Collections.emptyMap(), SOME_DATE, PLUGIN_KEY, QP_KEY))); | |||
underTest.set(Collections.emptyList()); | |||
} | |||
@@ -84,7 +85,7 @@ public class ActiveRulesHolderImplTest { | |||
thrown.expectMessage("Active rule must not be declared multiple times: squid:S001"); | |||
underTest.set(asList( | |||
new ActiveRule(RULE_KEY, Severity.BLOCKER, Collections.emptyMap(), SOME_DATE, PLUGIN_KEY), | |||
new ActiveRule(RULE_KEY, Severity.MAJOR, Collections.emptyMap(), SOME_DATE, PLUGIN_KEY))); | |||
new ActiveRule(RULE_KEY, Severity.BLOCKER, Collections.emptyMap(), SOME_DATE, PLUGIN_KEY, QP_KEY), | |||
new ActiveRule(RULE_KEY, Severity.MAJOR, Collections.emptyMap(), SOME_DATE, PLUGIN_KEY, QP_KEY))); | |||
} | |||
} |
@@ -28,7 +28,7 @@ import static java.util.Collections.emptyMap; | |||
public class AlwaysActiveRulesHolderImpl implements ActiveRulesHolder { | |||
@Override | |||
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, null, "qp1")); | |||
} | |||
} |
@@ -44,6 +44,9 @@ import org.sonar.ce.task.projectanalysis.measure.Measure; | |||
import org.sonar.ce.task.projectanalysis.measure.MeasureRepository; | |||
import org.sonar.ce.task.projectanalysis.metric.Metric; | |||
import org.sonar.ce.task.projectanalysis.metric.MetricRepository; | |||
import org.sonar.ce.task.projectanalysis.qualityprofile.MutableQProfileStatusRepository; | |||
import org.sonar.ce.task.projectanalysis.qualityprofile.QProfileStatusRepository; | |||
import org.sonar.ce.task.projectanalysis.qualityprofile.QProfileStatusRepositoryImpl; | |||
import org.sonar.ce.task.step.TestComputationStepContext; | |||
import org.sonar.core.util.UtcDateUtils; | |||
import org.sonar.server.qualityprofile.QPMeasureData; | |||
@@ -60,6 +63,10 @@ import static org.mockito.Mockito.verify; | |||
import static org.mockito.Mockito.verifyNoMoreInteractions; | |||
import static org.mockito.Mockito.when; | |||
import static org.sonar.api.utils.DateUtils.parseDateTime; | |||
import static org.sonar.ce.task.projectanalysis.qualityprofile.QProfileStatusRepository.Status.ADDED; | |||
import static org.sonar.ce.task.projectanalysis.qualityprofile.QProfileStatusRepository.Status.REMOVED; | |||
import static org.sonar.ce.task.projectanalysis.qualityprofile.QProfileStatusRepository.Status.UNCHANGED; | |||
import static org.sonar.ce.task.projectanalysis.qualityprofile.QProfileStatusRepository.Status.UPDATED; | |||
public class QualityProfileEventsStepTest { | |||
@Rule | |||
@@ -76,10 +83,11 @@ public class QualityProfileEventsStepTest { | |||
private LanguageRepository languageRepository = mock(LanguageRepository.class); | |||
private EventRepository eventRepository = mock(EventRepository.class); | |||
private ArgumentCaptor<Event> eventArgumentCaptor = ArgumentCaptor.forClass(Event.class); | |||
private MutableQProfileStatusRepository qProfileStatusRepository = new QProfileStatusRepositoryImpl(); | |||
private Metric qualityProfileMetric = mock(Metric.class); | |||
private QualityProfileEventsStep underTest = new QualityProfileEventsStep(treeRootHolder, metricRepository, measureRepository, languageRepository, eventRepository); | |||
private QualityProfileEventsStep underTest = new QualityProfileEventsStep(treeRootHolder, metricRepository, measureRepository, languageRepository, eventRepository, qProfileStatusRepository); | |||
@Before | |||
public void setUp() { | |||
@@ -116,8 +124,9 @@ public class QualityProfileEventsStepTest { | |||
} | |||
@Test | |||
public void added_event_if_one_new_qp() { | |||
public void added_event_if_qp_is_added() { | |||
QualityProfile qp = qp(QP_NAME_1, LANGUAGE_KEY_1, new Date()); | |||
qProfileStatusRepository.register(qp.getQpKey(), ADDED); | |||
Language language = mockLanguageInRepository(LANGUAGE_KEY_1); | |||
mockMeasures(treeRootHolder.getRoot(), null, arrayOf(qp)); | |||
@@ -132,6 +141,7 @@ public class QualityProfileEventsStepTest { | |||
@Test | |||
public void added_event_uses_language_key_in_message_if_language_not_found() { | |||
QualityProfile qp = qp(QP_NAME_1, LANGUAGE_KEY_1, new Date()); | |||
qProfileStatusRepository.register(qp.getQpKey(), ADDED); | |||
mockLanguageNotInRepository(LANGUAGE_KEY_1); | |||
mockMeasures(treeRootHolder.getRoot(), null, arrayOf(qp)); | |||
@@ -144,8 +154,9 @@ public class QualityProfileEventsStepTest { | |||
} | |||
@Test | |||
public void no_more_used_event_if_qp_no_more_listed() { | |||
public void no_more_used_event_if_qp_is_removed() { | |||
QualityProfile qp = qp(QP_NAME_1, LANGUAGE_KEY_1, new Date()); | |||
qProfileStatusRepository.register(qp.getQpKey(), REMOVED); | |||
mockMeasures(treeRootHolder.getRoot(), arrayOf(qp), null); | |||
Language language = mockLanguageInRepository(LANGUAGE_KEY_1); | |||
@@ -160,7 +171,7 @@ public class QualityProfileEventsStepTest { | |||
@Test | |||
public void no_more_used_event_uses_language_key_in_message_if_language_not_found() { | |||
QualityProfile qp = qp(QP_NAME_1, LANGUAGE_KEY_1, new Date()); | |||
qProfileStatusRepository.register(qp.getQpKey(), REMOVED); | |||
mockMeasures(treeRootHolder.getRoot(), arrayOf(qp), null); | |||
mockLanguageNotInRepository(LANGUAGE_KEY_1); | |||
@@ -172,9 +183,9 @@ public class QualityProfileEventsStepTest { | |||
} | |||
@Test | |||
public void no_event_if_same_qp_with_same_date() { | |||
public void no_event_if_qp_is_unchanged() { | |||
QualityProfile qp = qp(QP_NAME_1, LANGUAGE_KEY_1, new Date()); | |||
qProfileStatusRepository.register(qp.getQpKey(), UNCHANGED); | |||
mockMeasures(treeRootHolder.getRoot(), arrayOf(qp), arrayOf(qp)); | |||
underTest.execute(new TestComputationStepContext()); | |||
@@ -183,10 +194,10 @@ public class QualityProfileEventsStepTest { | |||
} | |||
@Test | |||
public void changed_event_if_same_qp_but_no_same_date() { | |||
public void changed_event_if_qp_has_been_updated() { | |||
QualityProfile qp1 = qp(QP_NAME_1, LANGUAGE_KEY_1, parseDateTime("2011-04-25T01:05:13+0100")); | |||
QualityProfile qp2 = qp(QP_NAME_1, LANGUAGE_KEY_1, parseDateTime("2011-04-25T01:05:17+0100")); | |||
qProfileStatusRepository.register(qp2.getQpKey(), UPDATED); | |||
mockMeasures(treeRootHolder.getRoot(), arrayOf(qp1), arrayOf(qp2)); | |||
Language language = mockLanguageInRepository(LANGUAGE_KEY_1); | |||
@@ -209,17 +220,21 @@ public class QualityProfileEventsStepTest { | |||
}).when(eventRepository).add(eq(treeRootHolder.getRoot()), any(Event.class)); | |||
Date date = new Date(); | |||
QualityProfile qp1 = qp(QP_NAME_2, LANGUAGE_KEY_1, date); | |||
QualityProfile qp2 = qp(QP_NAME_2, LANGUAGE_KEY_2, date); | |||
QualityProfile qp3 = qp(QP_NAME_1, LANGUAGE_KEY_1, parseDateTime("2011-04-25T01:05:13+0100")); | |||
QualityProfile qp3_updated = qp(QP_NAME_1, LANGUAGE_KEY_1, parseDateTime("2011-04-25T01:05:17+0100")); | |||
QualityProfile qp4 = qp(QP_NAME_2, LANGUAGE_KEY_3, date); | |||
mockMeasures( | |||
treeRootHolder.getRoot(), | |||
arrayOf( | |||
qp(QP_NAME_2, LANGUAGE_KEY_1, date), | |||
qp(QP_NAME_2, LANGUAGE_KEY_2, date), | |||
qp(QP_NAME_1, LANGUAGE_KEY_1, parseDateTime("2011-04-25T01:05:13+0100"))), | |||
arrayOf( | |||
qp(QP_NAME_1, LANGUAGE_KEY_1, parseDateTime("2011-04-25T01:05:17+0100")), | |||
qp(QP_NAME_2, LANGUAGE_KEY_2, date), | |||
qp(QP_NAME_2, LANGUAGE_KEY_3, date))); | |||
arrayOf(qp1, qp2, qp3), | |||
arrayOf(qp3_updated, qp2, qp4)); | |||
mockNoLanguageInRepository(); | |||
qProfileStatusRepository.register(qp1.getQpKey(), REMOVED); | |||
qProfileStatusRepository.register(qp2.getQpKey(), UNCHANGED); | |||
qProfileStatusRepository.register(qp3.getQpKey(), UPDATED); | |||
qProfileStatusRepository.register(qp4.getQpKey(), ADDED); | |||
underTest.execute(new TestComputationStepContext()); | |||
@@ -227,7 +242,6 @@ public class QualityProfileEventsStepTest { | |||
"Stop using '" + QP_NAME_2 + "' (" + LANGUAGE_KEY_1 + ")", | |||
"Use '" + QP_NAME_2 + "' (" + LANGUAGE_KEY_3 + ")", | |||
"Changes in '" + QP_NAME_1 + "' (" + LANGUAGE_KEY_1 + ")"); | |||
} | |||
private Language mockLanguageInRepository(String languageKey) { |