summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Brandhof <simon.brandhof@sonarsource.com>2015-07-16 16:32:53 +0200
committerSimon Brandhof <simon.brandhof@sonarsource.com>2015-07-20 22:30:41 +0200
commit01bb0a109044ab7338932d885cb8666cae88907c (patch)
tree9b7f10758c7644dc0a401aa47b3cda137c568342
parent29eedac50ad5e862c395cef427cf635b35d4c476 (diff)
downloadsonarqube-01bb0a109044ab7338932d885cb8666cae88907c.tar.gz
sonarqube-01bb0a109044ab7338932d885cb8666cae88907c.zip
SONAR-6703 new common rules
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/component/MutableTreeRootHolder.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/component/TreeRootHolderImpl.java3
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainerImpl.java19
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/formula/coverage/CoverageFormula.java5
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/TrackerRawInputFactory.java48
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/AbstractCoverageRule.java70
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/BranchCoverageRule.java44
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/CommentDensityRule.java74
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/CommonRule.java79
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/CommonRuleEngine.java34
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/CommonRuleEngineImpl.java58
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/DuplicatedBlockRule.java56
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/LineCoverageRule.java44
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/SkippedTestRule.java57
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/TestErrorRule.java62
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/package-info.java24
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/qualityprofile/ActiveRule.java42
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/qualityprofile/ActiveRulesHolder.java29
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/qualityprofile/ActiveRulesHolderImpl.java55
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java10
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/step/FeedActiveRulesStep.java63
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/rule/CommonRuleDefinitionsImpl.java30
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/rule/CommonRuleKeys.java40
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/batch/TreeRootHolderRule.java6
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/component/MutableTreeRootHolderRule.java4
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/issue/TrackerRawInputFactoryTest.java154
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/issue/commonrule/BranchCoverageRuleTest.java57
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/issue/commonrule/CommentDensityRuleTest.java139
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/issue/commonrule/CommonRuleEngineImplTest.java75
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/issue/commonrule/CoverageRuleTest.java129
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/issue/commonrule/DuplicatedBlockRuleTest.java87
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/issue/commonrule/LineCoverageRuleTest.java57
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/issue/commonrule/SkippedTestRuleTest.java97
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/issue/commonrule/TestErrorRuleTest.java100
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/qualityprofile/ActiveRulesHolderImplTest.java87
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/qualityprofile/ActiveRulesHolderRule.java46
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/rule/CommonRuleKeysTest.java39
37 files changed, 1987 insertions, 38 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/component/MutableTreeRootHolder.java b/server/sonar-server/src/main/java/org/sonar/server/computation/component/MutableTreeRootHolder.java
index f4873dfc567..cfb79c72184 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/component/MutableTreeRootHolder.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/component/MutableTreeRootHolder.java
@@ -29,5 +29,5 @@ public interface MutableTreeRootHolder extends TreeRootHolder {
*
* @throws NullPointerException if {@code newRoot} is {@code null}
*/
- void setRoot(Component newRoot);
+ MutableTreeRootHolder setRoot(Component newRoot);
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/component/TreeRootHolderImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/component/TreeRootHolderImpl.java
index 3aa32ff0c12..6ebc2aade59 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/component/TreeRootHolderImpl.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/component/TreeRootHolderImpl.java
@@ -34,9 +34,10 @@ public class TreeRootHolderImpl implements MutableTreeRootHolder {
private Map<Integer, Component> componentsByRef = new HashMap<>();
@Override
- public void setRoot(Component newRoot) {
+ public MutableTreeRootHolder setRoot(Component newRoot) {
this.root = Objects.requireNonNull(newRoot);
feedComponentsByRef();
+ return this;
}
@Override
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainerImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainerImpl.java
index ebd3a564caf..101829f90b5 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainerImpl.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainerImpl.java
@@ -63,6 +63,13 @@ import org.sonar.server.computation.issue.TrackerBaseInputFactory;
import org.sonar.server.computation.issue.TrackerExecution;
import org.sonar.server.computation.issue.TrackerRawInputFactory;
import org.sonar.server.computation.issue.UpdateConflictResolver;
+import org.sonar.server.computation.issue.commonrule.BranchCoverageRule;
+import org.sonar.server.computation.issue.commonrule.CommentDensityRule;
+import org.sonar.server.computation.issue.commonrule.CommonRuleEngineImpl;
+import org.sonar.server.computation.issue.commonrule.DuplicatedBlockRule;
+import org.sonar.server.computation.issue.commonrule.LineCoverageRule;
+import org.sonar.server.computation.issue.commonrule.SkippedTestRule;
+import org.sonar.server.computation.issue.commonrule.TestErrorRule;
import org.sonar.server.computation.language.LanguageRepositoryImpl;
import org.sonar.server.computation.measure.MeasureRepositoryImpl;
import org.sonar.server.computation.measure.newcoverage.NewCoverageMetricKeysModule;
@@ -71,6 +78,7 @@ import org.sonar.server.computation.period.PeriodsHolderImpl;
import org.sonar.server.computation.qualitygate.EvaluationResultTextConverterImpl;
import org.sonar.server.computation.qualitygate.QualityGateHolderImpl;
import org.sonar.server.computation.qualitygate.QualityGateServiceImpl;
+import org.sonar.server.computation.qualityprofile.ActiveRulesHolderImpl;
import org.sonar.server.computation.sqale.SqaleRatingSettings;
import org.sonar.server.computation.step.ComputationStep;
import org.sonar.server.computation.step.ComputationSteps;
@@ -152,6 +160,7 @@ public class ComputeEngineContainerImpl extends ComponentContainer implements Co
QualityGateHolderImpl.class,
DebtModelHolderImpl.class,
SqaleRatingSettings.class,
+ ActiveRulesHolderImpl.class,
BatchReportReaderImpl.class,
@@ -162,7 +171,6 @@ public class ComputeEngineContainerImpl extends ComponentContainer implements Co
ProjectSettingsRepository.class,
DbIdsRepository.class,
CoreFormulaRepositoryImpl.class,
-
QualityGateServiceImpl.class,
EvaluationResultTextConverterImpl.class,
@@ -179,6 +187,15 @@ public class ComputeEngineContainerImpl extends ComponentContainer implements Co
IssueVisitors.class,
IssueLifecycle.class,
+ // common rules
+ CommonRuleEngineImpl.class,
+ BranchCoverageRule.class,
+ LineCoverageRule.class,
+ CommentDensityRule.class,
+ DuplicatedBlockRule.class,
+ TestErrorRule.class,
+ SkippedTestRule.class,
+
// order is important: DebtAggregator then NewDebtAggregator (new debt requires debt)
DebtCalculator.class,
DebtAggregator.class,
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/formula/coverage/CoverageFormula.java b/server/sonar-server/src/main/java/org/sonar/server/computation/formula/coverage/CoverageFormula.java
index c25b0d185f1..1521df8f9d0 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/formula/coverage/CoverageFormula.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/formula/coverage/CoverageFormula.java
@@ -20,6 +20,7 @@
package org.sonar.server.computation.formula.coverage;
import com.google.common.base.Optional;
+import org.sonar.server.computation.formula.Counter;
import org.sonar.server.computation.formula.CreateMeasureContext;
import org.sonar.server.computation.formula.Formula;
import org.sonar.server.computation.measure.Measure;
@@ -28,8 +29,8 @@ import static org.sonar.server.computation.formula.coverage.CoverageUtils.calcul
import static org.sonar.server.computation.measure.Measure.newMeasureBuilder;
/**
- * An abstract Formula which implements the aggregation of a Counter of type ElementsAndCoveredElementsCounter with
- * another counter.
+ * An abstract {@link Formula} which implements the aggregation of a {@link Counter} of
+ * type {@link ElementsAndCoveredElementsCounter} with another counter.
*/
public abstract class CoverageFormula<T extends ElementsAndCoveredElementsCounter> implements Formula<T> {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/TrackerRawInputFactory.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/TrackerRawInputFactory.java
index 6f7cc4640cf..357d1df8694 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/TrackerRawInputFactory.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/TrackerRawInputFactory.java
@@ -27,6 +27,7 @@ import java.util.List;
import org.sonar.api.issue.Issue;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.utils.KeyValueFormat;
+import org.sonar.api.utils.log.Loggers;
import org.sonar.batch.protocol.output.BatchReport;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.core.issue.tracking.Input;
@@ -35,15 +36,20 @@ import org.sonar.core.issue.tracking.LineHashSequence;
import org.sonar.server.computation.batch.BatchReportReader;
import org.sonar.server.computation.component.Component;
import org.sonar.server.computation.component.TreeRootHolder;
+import org.sonar.server.computation.issue.commonrule.CommonRuleEngine;
+import org.sonar.server.rule.CommonRuleKeys;
public class TrackerRawInputFactory {
private final TreeRootHolder treeRootHolder;
private final BatchReportReader reportReader;
+ private final CommonRuleEngine commonRuleEngine;
- public TrackerRawInputFactory(TreeRootHolder treeRootHolder, BatchReportReader reportReader) {
+ public TrackerRawInputFactory(TreeRootHolder treeRootHolder, BatchReportReader reportReader,
+ CommonRuleEngine commonRuleEngine) {
this.treeRootHolder = treeRootHolder;
this.reportReader = reportReader;
+ this.commonRuleEngine = commonRuleEngine;
}
public Input<DefaultIssue> create(Component component) {
@@ -71,28 +77,36 @@ public class TrackerRawInputFactory {
@Override
protected List<DefaultIssue> loadIssues() {
List<BatchReport.Issue> reportIssues = reportReader.readComponentIssues(component.getRef());
- List<DefaultIssue> issues = new ArrayList<>();
+ List<DefaultIssue> result = new ArrayList<>();
+
+ for (DefaultIssue commonRuleIssue : commonRuleEngine.process(component)) {
+ result.add(init(commonRuleIssue));
+ }
if (!reportIssues.isEmpty()) {
// optimization - do not load line hashes if there are no issues
LineHashSequence lineHashSeq = getLineHashSequence();
for (BatchReport.Issue reportIssue : reportIssues) {
- DefaultIssue issue = toIssue(lineHashSeq, reportIssue);
- issues.add(issue);
+ if (isIssueOnUnsupportedCommonRule(reportIssue)) {
+ DefaultIssue issue = toIssue(lineHashSeq, reportIssue);
+ result.add(issue);
+ } else {
+ Loggers.get(getClass()).debug("Ignored issue from analysis report on rule {}:{}", reportIssue.getRuleRepository(), reportIssue.getRuleKey());
+ }
}
}
- return issues;
+ return result;
+ }
+
+ private boolean isIssueOnUnsupportedCommonRule(BatchReport.Issue issue) {
+ // issues on batch common rules are ignored. This feature
+ // is natively supported by compute engine since 5.2.
+ return !issue.getRuleRepository().startsWith(CommonRuleKeys.REPOSITORY_PREFIX);
}
private DefaultIssue toIssue(LineHashSequence lineHashSeq, BatchReport.Issue reportIssue) {
DefaultIssue issue = new DefaultIssue();
+ init(issue);
issue.setRuleKey(RuleKey.of(reportIssue.getRuleRepository(), reportIssue.getRuleKey()));
- issue.setResolution(null);
- issue.setStatus(Issue.STATUS_OPEN);
- issue.setComponentUuid(component.getUuid());
- issue.setComponentKey(component.getKey());
- issue.setProjectUuid(treeRootHolder.getRoot().getUuid());
- issue.setProjectKey(treeRootHolder.getRoot().getKey());
-
if (reportIssue.hasLine()) {
issue.setLine(reportIssue.getLine());
issue.setChecksum(lineHashSeq.getHashForLine(reportIssue.getLine()));
@@ -114,5 +128,15 @@ public class TrackerRawInputFactory {
}
return issue;
}
+
+ private DefaultIssue init(DefaultIssue issue) {
+ issue.setResolution(null);
+ issue.setStatus(Issue.STATUS_OPEN);
+ issue.setComponentUuid(component.getUuid());
+ issue.setComponentKey(component.getKey());
+ issue.setProjectUuid(treeRootHolder.getRoot().getUuid());
+ issue.setProjectKey(treeRootHolder.getRoot().getKey());
+ return issue;
+ }
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/AbstractCoverageRule.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/AbstractCoverageRule.java
new file mode 100644
index 00000000000..4439ed52502
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/AbstractCoverageRule.java
@@ -0,0 +1,70 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.computation.issue.commonrule;
+
+import com.google.common.base.Optional;
+import org.sonar.server.computation.component.Component;
+import org.sonar.server.computation.measure.Measure;
+import org.sonar.server.computation.measure.MeasureRepository;
+import org.sonar.server.computation.metric.Metric;
+import org.sonar.server.computation.qualityprofile.ActiveRule;
+import org.sonar.server.computation.qualityprofile.ActiveRulesHolder;
+
+public abstract class AbstractCoverageRule extends CommonRule {
+
+ private final MeasureRepository measureRepository;
+ private final Metric coverageMetric;
+ private final Metric uncoveredMetric;
+ private final Metric toCoverMetric;
+
+ public AbstractCoverageRule(ActiveRulesHolder activeRulesHolder, String ruleKey,
+ MeasureRepository measureRepository,
+ Metric coverageMetric, Metric uncoveredMetric, Metric toCoverMetric) {
+ super(activeRulesHolder, ruleKey);
+ this.measureRepository = measureRepository;
+ this.coverageMetric = coverageMetric;
+ this.uncoveredMetric = uncoveredMetric;
+ this.toCoverMetric = toCoverMetric;
+ }
+
+ @Override
+ protected CommonRuleIssue doProcessFile(Component file, ActiveRule activeRule) {
+ Optional<Measure> coverageMeasure = measureRepository.getRawMeasure(file, coverageMetric);
+ if (!file.getFileAttributes().isUnitTest() && coverageMeasure.isPresent()) {
+ double coverage = coverageMeasure.get().getDoubleValue();
+ // FIXME load threshold from activeRule
+ double minimumCoverage = 65.0;
+ if (coverage < minimumCoverage) {
+ Optional<Measure> uncoveredMeasure = measureRepository.getRawMeasure(file, uncoveredMetric);
+ Optional<Measure> toCoverMeasure = measureRepository.getRawMeasure(file, toCoverMetric);
+ double uncovered = uncoveredMeasure.isPresent() ? uncoveredMeasure.get().getDoubleValue() : 0.0;
+ double toCover = toCoverMeasure.isPresent() ? toCoverMeasure.get().getDoubleValue() : 0.0;
+
+ // effort to fix is the number of lines/conditions to cover for reaching threshold
+ int effortToFix = (int) Math.ceil((toCover * minimumCoverage / 100) - (toCover - uncovered));
+
+ return new CommonRuleIssue(effortToFix, formatMessage(effortToFix, minimumCoverage));
+ }
+ }
+ return null;
+ }
+
+ protected abstract String formatMessage(int effortToFix, double minCoverage);
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/BranchCoverageRule.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/BranchCoverageRule.java
new file mode 100644
index 00000000000..7b4df10ef02
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/BranchCoverageRule.java
@@ -0,0 +1,44 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.computation.issue.commonrule;
+
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.server.computation.measure.MeasureRepository;
+import org.sonar.server.computation.metric.MetricRepository;
+import org.sonar.server.computation.qualityprofile.ActiveRulesHolder;
+import org.sonar.server.rule.CommonRuleKeys;
+
+import static java.lang.String.format;
+
+public class BranchCoverageRule extends AbstractCoverageRule {
+
+ public BranchCoverageRule(ActiveRulesHolder activeRulesHolder, MetricRepository metricRepository, MeasureRepository measureRepository) {
+ super(activeRulesHolder, CommonRuleKeys.INSUFFICIENT_BRANCH_COVERAGE, measureRepository,
+ metricRepository.getByKey(CoreMetrics.BRANCH_COVERAGE_KEY),
+ metricRepository.getByKey(CoreMetrics.UNCOVERED_CONDITIONS_KEY),
+ metricRepository.getByKey(CoreMetrics.CONDITIONS_TO_COVER_KEY));
+ }
+
+ @Override
+ protected String formatMessage(int effortToFix, double minCoverage) {
+ // FIXME declare min threshold as int but not float ?
+ return format("%d more branches need to be covered by unit tests to reach the minimum threshold of %s%% branch coverage.", effortToFix, minCoverage);
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/CommentDensityRule.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/CommentDensityRule.java
new file mode 100644
index 00000000000..0346a6567ce
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/CommentDensityRule.java
@@ -0,0 +1,74 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.computation.issue.commonrule;
+
+import com.google.common.base.Optional;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.server.computation.component.Component;
+import org.sonar.server.computation.measure.Measure;
+import org.sonar.server.computation.measure.MeasureRepository;
+import org.sonar.server.computation.metric.Metric;
+import org.sonar.server.computation.metric.MetricRepository;
+import org.sonar.server.computation.qualityprofile.ActiveRule;
+import org.sonar.server.computation.qualityprofile.ActiveRulesHolder;
+import org.sonar.server.rule.CommonRuleKeys;
+
+import static java.lang.String.format;
+
+public class CommentDensityRule extends CommonRule {
+
+ private final MeasureRepository measureRepository;
+ private final Metric commentDensityMetric;
+ private final Metric commentLinesMetric;
+ private final Metric nclocMetric;
+
+ public CommentDensityRule(ActiveRulesHolder activeRulesHolder, MeasureRepository measureRepository, MetricRepository metricRepository) {
+ super(activeRulesHolder, CommonRuleKeys.INSUFFICIENT_COMMENT_DENSITY);
+ this.measureRepository = measureRepository;
+ this.commentDensityMetric = metricRepository.getByKey(CoreMetrics.COMMENT_LINES_DENSITY_KEY);
+ this.commentLinesMetric = metricRepository.getByKey(CoreMetrics.COMMENT_LINES_KEY);
+ this.nclocMetric = metricRepository.getByKey(CoreMetrics.NCLOC_KEY);
+ }
+
+ @Override
+ protected CommonRuleIssue doProcessFile(Component file, ActiveRule activeRule) {
+ Optional<Measure> commentDensityMeasure = measureRepository.getRawMeasure(file, commentDensityMetric);
+ Optional<Measure> commentLinesMeasure = measureRepository.getRawMeasure(file, commentLinesMetric);
+ Optional<Measure> nclocMeasure = measureRepository.getRawMeasure(file, nclocMetric);
+ // FIXME
+ double minCommentDensity = 25.0;
+ if (commentDensityMeasure.isPresent() && nclocMeasure.isPresent() && nclocMeasure.get().getIntValue() > 0 && commentDensityMeasure.get().getDoubleValue() < minCommentDensity) {
+ int commentLines = commentLinesMeasure.isPresent() ? commentLinesMeasure.get().getIntValue() : 0;
+ int ncloc = nclocMeasure.get().getIntValue();
+ int minExpectedCommentLines = (int) Math.ceil(minCommentDensity * ncloc / (100 - minCommentDensity));
+ int missingCommentLines = minExpectedCommentLines - commentLines;
+ if (missingCommentLines <= 0) {
+ throw new IllegalStateException(format("Bug in measures of comment lines - density=%s, comment_lines= %d, ncloc=%d, threshold=%s%%", commentDensityMeasure.get()
+ .getDoubleValue(), commentLines, nclocMeasure.get().getIntValue(), minCommentDensity));
+ }
+
+ // TODO declare min threshold as int but not float ?
+ String message = format("%d more comment lines need to be written to reach the minimum threshold of %s%% comment density.", missingCommentLines, minCommentDensity);
+ return new CommonRuleIssue(missingCommentLines, message);
+ }
+
+ return null;
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/CommonRule.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/CommonRule.java
new file mode 100644
index 00000000000..4fdea207acb
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/CommonRule.java
@@ -0,0 +1,79 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.computation.issue.commonrule;
+
+import com.google.common.base.Optional;
+import javax.annotation.CheckForNull;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.server.computation.component.Component;
+import org.sonar.server.computation.qualityprofile.ActiveRule;
+import org.sonar.server.computation.qualityprofile.ActiveRulesHolder;
+
+import static org.sonar.server.rule.CommonRuleKeys.commonRepositoryForLang;
+
+public abstract class CommonRule {
+
+ private final ActiveRulesHolder activeRulesHolder;
+ private final String key;
+
+ public CommonRule(ActiveRulesHolder activeRulesHolder, String key) {
+ this.activeRulesHolder = activeRulesHolder;
+ this.key = key;
+ }
+
+ @CheckForNull
+ public DefaultIssue processFile(Component file, String fileLanguage) {
+ DefaultIssue issue = null;
+ RuleKey ruleKey = RuleKey.of(commonRepositoryForLang(fileLanguage), key);
+ Optional<ActiveRule> activeRule = activeRulesHolder.get(ruleKey);
+ if (activeRule.isPresent()) {
+ CommonRuleIssue cri = doProcessFile(file, activeRule.get());
+ if (cri != null) {
+ issue = new DefaultIssue();
+ issue.setEffortToFix(cri.effortToFix);
+ issue.setMessage(cri.message);
+ issue.setRuleKey(ruleKey);
+ issue.setSeverity(activeRule.get().getSeverity());
+ issue.setLine(null);
+ issue.setChecksum("");
+ }
+ }
+ return issue;
+ }
+
+ /**
+ * Implementations must only set the effort to fix and message, so
+ * using the lite intermediary object {@link CommonRuleIssue}
+ */
+ @CheckForNull
+ protected abstract CommonRuleIssue doProcessFile(Component file, ActiveRule activeRule);
+
+ protected static class CommonRuleIssue {
+ private final double effortToFix;
+ // FIXME which format ? HTML or MD ?
+ private final String message;
+
+ public CommonRuleIssue(double effortToFix, String message) {
+ this.effortToFix = effortToFix;
+ this.message = message;
+ }
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/CommonRuleEngine.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/CommonRuleEngine.java
new file mode 100644
index 00000000000..79fde880341
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/CommonRuleEngine.java
@@ -0,0 +1,34 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.computation.issue.commonrule;
+
+import java.util.Collection;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.server.computation.component.Component;
+
+/**
+ * Generate the issues related to "common rules", which are
+ * the rules based on measure thresholds.
+ */
+public interface CommonRuleEngine {
+
+ Collection<DefaultIssue> process(Component component);
+
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/CommonRuleEngineImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/CommonRuleEngineImpl.java
new file mode 100644
index 00000000000..8fd70ebdd61
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/CommonRuleEngineImpl.java
@@ -0,0 +1,58 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.computation.issue.commonrule;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import javax.annotation.CheckForNull;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.server.computation.component.Component;
+
+public class CommonRuleEngineImpl implements CommonRuleEngine {
+
+ private final CommonRule[] commonRules;
+
+ public CommonRuleEngineImpl(CommonRule... commonRules) {
+ this.commonRules = commonRules;
+ }
+
+ @Override
+ public Collection<DefaultIssue> process(Component component) {
+ Collection<DefaultIssue> result = new ArrayList<>();
+ String fileLanguage = getFileLanguage(component);
+ if (fileLanguage != null) {
+ for (CommonRule commonRule : commonRules) {
+ DefaultIssue issue = commonRule.processFile(component, fileLanguage);
+ if (issue != null) {
+ result.add(issue);
+ }
+ }
+ }
+ return result;
+ }
+
+ @CheckForNull
+ private static String getFileLanguage(Component component) {
+ if (component.getType() == Component.Type.FILE) {
+ return component.getFileAttributes().getLanguageKey();
+ }
+ return null;
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/DuplicatedBlockRule.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/DuplicatedBlockRule.java
new file mode 100644
index 00000000000..7a60c9233bd
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/DuplicatedBlockRule.java
@@ -0,0 +1,56 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.computation.issue.commonrule;
+
+import com.google.common.base.Optional;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.server.computation.component.Component;
+import org.sonar.server.computation.measure.Measure;
+import org.sonar.server.computation.measure.MeasureRepository;
+import org.sonar.server.computation.metric.Metric;
+import org.sonar.server.computation.metric.MetricRepository;
+import org.sonar.server.computation.qualityprofile.ActiveRule;
+import org.sonar.server.computation.qualityprofile.ActiveRulesHolder;
+import org.sonar.server.rule.CommonRuleKeys;
+
+import static java.lang.String.format;
+
+public class DuplicatedBlockRule extends CommonRule {
+
+ private final MeasureRepository measureRepository;
+ private final Metric duplicatedBlocksMetric;
+
+ public DuplicatedBlockRule(ActiveRulesHolder activeRulesHolder, MeasureRepository measureRepository, MetricRepository metricRepository) {
+ super(activeRulesHolder, CommonRuleKeys.DUPLICATED_BLOCKS);
+ this.measureRepository = measureRepository;
+ this.duplicatedBlocksMetric = metricRepository.getByKey(CoreMetrics.DUPLICATED_BLOCKS_KEY);
+ }
+
+ @Override
+ protected CommonRuleIssue doProcessFile(Component file, ActiveRule activeRule) {
+ Optional<Measure> duplicatedBlocksMeasure = measureRepository.getRawMeasure(file, duplicatedBlocksMetric);
+ if (duplicatedBlocksMeasure.isPresent() && duplicatedBlocksMeasure.get().getIntValue() > 0) {
+ int duplicatedBlocks = duplicatedBlocksMeasure.get().getIntValue();
+ String message = format("%d duplicated blocks of code must be removed.", duplicatedBlocks);
+ return new CommonRuleIssue(duplicatedBlocks, message);
+ }
+ return null;
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/LineCoverageRule.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/LineCoverageRule.java
new file mode 100644
index 00000000000..e860f6d0c55
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/LineCoverageRule.java
@@ -0,0 +1,44 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.computation.issue.commonrule;
+
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.server.computation.measure.MeasureRepository;
+import org.sonar.server.computation.metric.MetricRepository;
+import org.sonar.server.computation.qualityprofile.ActiveRulesHolder;
+import org.sonar.server.rule.CommonRuleKeys;
+
+import static java.lang.String.format;
+
+public class LineCoverageRule extends AbstractCoverageRule {
+
+ public LineCoverageRule(ActiveRulesHolder activeRulesHolder, MetricRepository metricRepository, MeasureRepository measureRepository) {
+ super(activeRulesHolder, CommonRuleKeys.INSUFFICIENT_LINE_COVERAGE, measureRepository,
+ metricRepository.getByKey(CoreMetrics.LINE_COVERAGE_KEY),
+ metricRepository.getByKey(CoreMetrics.UNCOVERED_LINES_KEY),
+ metricRepository.getByKey(CoreMetrics.LINES_TO_COVER_KEY));
+ }
+
+ @Override
+ protected String formatMessage(int effortToFix, double minCoverage) {
+ // TODO declare min threshold as int but not float ?
+ return format("%d more lines of code need to be covered by unit tests to reach the minimum threshold of %s%% lines coverage.", effortToFix, minCoverage);
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/SkippedTestRule.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/SkippedTestRule.java
new file mode 100644
index 00000000000..84497190893
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/SkippedTestRule.java
@@ -0,0 +1,57 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.computation.issue.commonrule;
+
+import com.google.common.base.Optional;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.server.computation.component.Component;
+import org.sonar.server.computation.measure.Measure;
+import org.sonar.server.computation.measure.MeasureRepository;
+import org.sonar.server.computation.metric.Metric;
+import org.sonar.server.computation.metric.MetricRepository;
+import org.sonar.server.computation.qualityprofile.ActiveRule;
+import org.sonar.server.computation.qualityprofile.ActiveRulesHolder;
+import org.sonar.server.rule.CommonRuleKeys;
+
+import static java.lang.String.format;
+
+public class SkippedTestRule extends CommonRule {
+
+ private final MeasureRepository measureRepository;
+ private final Metric skippedTestsMetric;
+
+ public SkippedTestRule(ActiveRulesHolder activeRulesHolder, MeasureRepository measureRepository, MetricRepository metricRepository) {
+ super(activeRulesHolder, CommonRuleKeys.SKIPPED_UNIT_TESTS);
+ this.measureRepository = measureRepository;
+ this.skippedTestsMetric = metricRepository.getByKey(CoreMetrics.SKIPPED_TESTS_KEY);
+ }
+
+ @Override
+ protected CommonRuleIssue doProcessFile(Component file, ActiveRule activeRule) {
+ Optional<Measure> measure = measureRepository.getRawMeasure(file, skippedTestsMetric);
+
+ int skipped = measure.isPresent() ? measure.get().getIntValue() : 0;
+ if (skipped > 0) {
+ String message = format("Fix or remove skipped unit tests in file \"%s\".", file.getName());
+ return new CommonRuleIssue(skipped, message);
+ }
+ return null;
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/TestErrorRule.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/TestErrorRule.java
new file mode 100644
index 00000000000..2568d960202
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/TestErrorRule.java
@@ -0,0 +1,62 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.computation.issue.commonrule;
+
+import com.google.common.base.Optional;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.server.computation.component.Component;
+import org.sonar.server.computation.measure.Measure;
+import org.sonar.server.computation.measure.MeasureRepository;
+import org.sonar.server.computation.metric.Metric;
+import org.sonar.server.computation.metric.MetricRepository;
+import org.sonar.server.computation.qualityprofile.ActiveRule;
+import org.sonar.server.computation.qualityprofile.ActiveRulesHolder;
+import org.sonar.server.rule.CommonRuleKeys;
+
+import static java.lang.String.format;
+
+public class TestErrorRule extends CommonRule {
+
+ private final MeasureRepository measureRepository;
+ private final Metric testErrorMetric;
+ private final Metric testFailureMetric;
+
+ public TestErrorRule(ActiveRulesHolder activeRulesHolder, MeasureRepository measureRepository, MetricRepository metricRepository) {
+ super(activeRulesHolder, CommonRuleKeys.FAILED_UNIT_TESTS);
+ this.measureRepository = measureRepository;
+ this.testErrorMetric = metricRepository.getByKey(CoreMetrics.TEST_ERRORS_KEY);
+ this.testFailureMetric = metricRepository.getByKey(CoreMetrics.TEST_FAILURES_KEY);
+ }
+
+ @Override
+ protected CommonRuleIssue doProcessFile(Component file, ActiveRule activeRule) {
+ Optional<Measure> errorsMeasure = measureRepository.getRawMeasure(file, testErrorMetric);
+ Optional<Measure> failuresMeasure = measureRepository.getRawMeasure(file, testFailureMetric);
+
+ int errors = errorsMeasure.isPresent() ? errorsMeasure.get().getIntValue() : 0;
+ int failures = failuresMeasure.isPresent() ? failuresMeasure.get().getIntValue() : 0;
+ int total = errors + failures;
+ if (total > 0) {
+ String message = format("Fix failing unit tests on file \"%s\".", file.getName());
+ return new CommonRuleIssue(total, message);
+ }
+ return null;
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/package-info.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/package-info.java
new file mode 100644
index 00000000000..7078b4c243f
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/commonrule/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+@ParametersAreNonnullByDefault
+package org.sonar.server.computation.issue.commonrule;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/qualityprofile/ActiveRule.java b/server/sonar-server/src/main/java/org/sonar/server/computation/qualityprofile/ActiveRule.java
new file mode 100644
index 00000000000..669641f1063
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/qualityprofile/ActiveRule.java
@@ -0,0 +1,42 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.computation.qualityprofile;
+
+import javax.annotation.concurrent.Immutable;
+import org.sonar.api.rule.RuleKey;
+
+@Immutable
+public class ActiveRule {
+ private final RuleKey ruleKey;
+ private final String severity;
+
+ public ActiveRule(RuleKey ruleKey, String severity) {
+ this.ruleKey = ruleKey;
+ this.severity = severity;
+ }
+
+ public RuleKey getRuleKey() {
+ return ruleKey;
+ }
+
+ public String getSeverity() {
+ return severity;
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/qualityprofile/ActiveRulesHolder.java b/server/sonar-server/src/main/java/org/sonar/server/computation/qualityprofile/ActiveRulesHolder.java
new file mode 100644
index 00000000000..e61e83da332
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/qualityprofile/ActiveRulesHolder.java
@@ -0,0 +1,29 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.computation.qualityprofile;
+
+import com.google.common.base.Optional;
+import org.sonar.api.rule.RuleKey;
+
+public interface ActiveRulesHolder {
+
+ Optional<ActiveRule> get(RuleKey ruleKey);
+
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/qualityprofile/ActiveRulesHolderImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/qualityprofile/ActiveRulesHolderImpl.java
new file mode 100644
index 00000000000..2bad41cfecb
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/qualityprofile/ActiveRulesHolderImpl.java
@@ -0,0 +1,55 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.computation.qualityprofile;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import org.sonar.api.rule.RuleKey;
+
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.Objects.requireNonNull;
+
+public class ActiveRulesHolderImpl implements ActiveRulesHolder {
+
+ private Map<RuleKey, ActiveRule> activeRulesByKey = null;
+
+ @Override
+ public Optional<ActiveRule> get(RuleKey ruleKey) {
+ checkState(activeRulesByKey != null, "Active rules have not been initialized yet");
+ return Optional.fromNullable(activeRulesByKey.get(ruleKey));
+ }
+
+ public void set(Collection<ActiveRule> activeRules) {
+ requireNonNull(activeRules, "Active rules cannot be null");
+ checkState(activeRulesByKey == null, "Active rules have already been initialized");
+
+ Map<RuleKey, ActiveRule> temp = new HashMap<>();
+ for (ActiveRule activeRule : activeRules) {
+ ActiveRule previousValue = temp.put(activeRule.getRuleKey(), activeRule);
+ if (previousValue != null) {
+ throw new IllegalArgumentException("Active rule must not be declared multiple times: " + activeRule.getRuleKey());
+ }
+ }
+ activeRulesByKey = ImmutableMap.copyOf(temp);
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java
index b7cc944a4b2..1c7f45b74c6 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java
@@ -45,22 +45,26 @@ public class ComputationSteps {
ValidateProjectStep.class,
FeedDebtModelStep.class,
+ FeedActiveRulesStep.class,
// load project related stuffs
QualityGateLoadingStep.class,
FeedPeriodsStep.class,
// data computation
- IntegrateIssuesStep.class,
- CoreMetricFormulaExecutorStep.class,
+ CoverageMeasuresStep.class,
+ CommentMeasuresStep.class,
+
+ // must be executed after the measures required for common rules (coverage, comment density, duplications)
CustomMeasuresCopyStep.class,
CommentMeasuresStep.class,
DuplicationMeasuresStep.class,
+ IntegrateIssuesStep.class,
+ CoreMetricFormulaExecutorStep.class,
// SQALE measures depend on issues
SqaleMeasuresStep.class,
NewCoverageMeasuresStep.class,
- CoverageMeasuresStep.class,
// Must be executed after computation of all measures
FillMeasuresWithVariationsStep.class,
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/FeedActiveRulesStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/FeedActiveRulesStep.java
new file mode 100644
index 00000000000..38a1ac7fe55
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/FeedActiveRulesStep.java
@@ -0,0 +1,63 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.computation.step;
+
+import com.google.common.base.Function;
+import java.util.Collection;
+import javax.annotation.Nonnull;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rule.Severity;
+import org.sonar.server.computation.batch.BatchReportReader;
+import org.sonar.server.computation.qualityprofile.ActiveRule;
+import org.sonar.server.computation.qualityprofile.ActiveRulesHolderImpl;
+
+import static com.google.common.collect.FluentIterable.from;
+import static org.sonar.core.rule.RuleKeyFunctions.stringToRuleKey;
+
+public class FeedActiveRulesStep implements ComputationStep {
+
+ private final BatchReportReader batchReportReader;
+ private final ActiveRulesHolderImpl activeRulesHolder;
+
+ public FeedActiveRulesStep(BatchReportReader batchReportReader, ActiveRulesHolderImpl activeRulesHolder) {
+ this.batchReportReader = batchReportReader;
+ this.activeRulesHolder = activeRulesHolder;
+ }
+
+ @Override
+ public void execute() {
+ Collection<String> keys = batchReportReader.readMetadata().getActiveRuleKeyList();
+ activeRulesHolder.set(from(keys).transform(stringToRuleKey()).transform(ToActiveRule.INSTANCE).toList());
+ }
+
+ @Override
+ public String getDescription() {
+ return getClass().getSimpleName();
+ }
+
+ private enum ToActiveRule implements Function<RuleKey, ActiveRule> {
+ INSTANCE;
+ @Override
+ public ActiveRule apply(@Nonnull RuleKey ruleKey) {
+ // FIXME load severity
+ return new ActiveRule(ruleKey, Severity.MAJOR);
+ }
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/CommonRuleDefinitionsImpl.java b/server/sonar-server/src/main/java/org/sonar/server/rule/CommonRuleDefinitionsImpl.java
index 1c4d31b0b57..d5365ab2956 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/rule/CommonRuleDefinitionsImpl.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/rule/CommonRuleDefinitionsImpl.java
@@ -25,12 +25,14 @@ import org.sonar.api.rule.Severity;
import org.sonar.api.server.rule.RuleParamType;
import org.sonar.api.server.rule.RulesDefinition;
+import static org.sonar.server.rule.CommonRuleKeys.commonRepositoryForLang;
+
/**
* Declare the few rules that are automatically created by core for all languages. These rules
* check measure values against thresholds defined in Quality profiles.
*/
// this class must not be mixed with other RulesDefinition so it does implement the interface RulesDefinitions.
-// It can replace the common-rules that are still embedded within plugins.
+// It replaces the common-rules that are still embedded within plugins.
public class CommonRuleDefinitionsImpl implements CommonRuleDefinitions {
private final Languages languages;
@@ -42,7 +44,7 @@ public class CommonRuleDefinitionsImpl implements CommonRuleDefinitions {
@Override
public void define(RulesDefinition.Context context) {
for (Language language : languages.all()) {
- RulesDefinition.NewRepository repo = context.createRepository("common-" + language.getKey(), language.getKey());
+ RulesDefinition.NewRepository repo = context.createRepository(commonRepositoryForLang(language.getKey()), language.getKey());
repo.setName("Common " + language.getName());
defineBranchCoverageRule(repo);
defineLineCoverageRule(repo);
@@ -54,8 +56,8 @@ public class CommonRuleDefinitionsImpl implements CommonRuleDefinitions {
}
}
- private void defineBranchCoverageRule(RulesDefinition.NewRepository repo) {
- RulesDefinition.NewRule rule = repo.createRule("InsufficientBranchCoverage");
+ private static void defineBranchCoverageRule(RulesDefinition.NewRepository repo) {
+ RulesDefinition.NewRule rule = repo.createRule(CommonRuleKeys.INSUFFICIENT_BRANCH_COVERAGE);
rule.setName("Branches should have sufficient coverage by unit tests")
.addTags("bad-practice")
.setHtmlDescription("An issue is created on a file as soon as the branch coverage on this file is less than the required threshold."
@@ -70,8 +72,8 @@ public class CommonRuleDefinitionsImpl implements CommonRuleDefinitions {
.setType(RuleParamType.FLOAT);
}
- private void defineLineCoverageRule(RulesDefinition.NewRepository repo) {
- RulesDefinition.NewRule rule = repo.createRule("InsufficientLineCoverage");
+ private static void defineLineCoverageRule(RulesDefinition.NewRepository repo) {
+ RulesDefinition.NewRule rule = repo.createRule(CommonRuleKeys.INSUFFICIENT_LINE_COVERAGE);
rule.setName("Lines should have sufficient coverage by unit tests")
.addTags("bad-practice")
.setHtmlDescription("An issue is created on a file as soon as the line coverage on this file is less than the required threshold. " +
@@ -86,8 +88,8 @@ public class CommonRuleDefinitionsImpl implements CommonRuleDefinitions {
.setType(RuleParamType.FLOAT);
}
- private void defineCommentDensityRule(RulesDefinition.NewRepository repo) {
- RulesDefinition.NewRule rule = repo.createRule("InsufficientCommentDensity");
+ private static void defineCommentDensityRule(RulesDefinition.NewRepository repo) {
+ RulesDefinition.NewRule rule = repo.createRule(CommonRuleKeys.INSUFFICIENT_COMMENT_DENSITY);
rule.setName("Source files should have a sufficient density of comment lines")
.addTags("convention")
.setHtmlDescription("An issue is created on a file as soon as the density of comment lines on this file is less than the required threshold. " +
@@ -102,8 +104,8 @@ public class CommonRuleDefinitionsImpl implements CommonRuleDefinitions {
.setType(RuleParamType.FLOAT);
}
- private void defineDuplicatedBlocksRule(RulesDefinition.NewRepository repo) {
- RulesDefinition.NewRule rule = repo.createRule("DuplicatedBlocks");
+ private static void defineDuplicatedBlocksRule(RulesDefinition.NewRepository repo) {
+ RulesDefinition.NewRule rule = repo.createRule(CommonRuleKeys.DUPLICATED_BLOCKS);
rule.setName("Source files should not have any duplicated blocks")
.addTags("pitfall")
.setHtmlDescription("An issue is created on a file as soon as there is at least one block of duplicated code on this file")
@@ -113,8 +115,8 @@ public class CommonRuleDefinitionsImpl implements CommonRuleDefinitions {
.setSeverity(Severity.MAJOR);
}
- private void defineFailedUnitTestRule(RulesDefinition.NewRepository repo) {
- RulesDefinition.NewRule rule = repo.createRule("FailedUnitTests");
+ private static void defineFailedUnitTestRule(RulesDefinition.NewRepository repo) {
+ RulesDefinition.NewRule rule = repo.createRule(CommonRuleKeys.FAILED_UNIT_TESTS);
rule
.setName("Failed unit tests should be fixed")
.addTags("bug")
@@ -126,8 +128,8 @@ public class CommonRuleDefinitionsImpl implements CommonRuleDefinitions {
.setSeverity(Severity.MAJOR);
}
- private void defineSkippedUnitTestRule(RulesDefinition.NewRepository repo) {
- RulesDefinition.NewRule rule = repo.createRule("SkippedUnitTests");
+ private static void defineSkippedUnitTestRule(RulesDefinition.NewRepository repo) {
+ RulesDefinition.NewRule rule = repo.createRule(CommonRuleKeys.SKIPPED_UNIT_TESTS);
rule.setName("Skipped unit tests should be either removed or fixed")
.addTags("pitfall")
.setHtmlDescription("Skipped unit tests are considered as dead code. Either they should be activated again (and updated) or they should be removed.")
diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/CommonRuleKeys.java b/server/sonar-server/src/main/java/org/sonar/server/rule/CommonRuleKeys.java
new file mode 100644
index 00000000000..97d91a2a01a
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/rule/CommonRuleKeys.java
@@ -0,0 +1,40 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.rule;
+
+public class CommonRuleKeys {
+
+ public static final String REPOSITORY_PREFIX = "common-";
+
+ public static final String INSUFFICIENT_BRANCH_COVERAGE = "InsufficientBranchCoverage";
+ public static final String INSUFFICIENT_LINE_COVERAGE = "InsufficientLineCoverage";
+ public static final String INSUFFICIENT_COMMENT_DENSITY = "InsufficientCommentDensity";
+ public static final String DUPLICATED_BLOCKS = "DuplicatedBlocks";
+ public static final String FAILED_UNIT_TESTS = "FailedUnitTests";
+ public static final String SKIPPED_UNIT_TESTS = "SkippedUnitTests";
+
+ private CommonRuleKeys() {
+ // only static methods
+ }
+
+ public static String commonRepositoryForLang(String language) {
+ return REPOSITORY_PREFIX + language;
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/batch/TreeRootHolderRule.java b/server/sonar-server/src/test/java/org/sonar/server/computation/batch/TreeRootHolderRule.java
index 17b066cd5ff..f5fc2d96b05 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/batch/TreeRootHolderRule.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/batch/TreeRootHolderRule.java
@@ -27,11 +27,12 @@ import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.sonar.server.computation.component.Component;
import org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor;
+import org.sonar.server.computation.component.MutableTreeRootHolder;
import org.sonar.server.computation.component.TreeRootHolder;
import static org.sonar.server.computation.component.ComponentVisitor.Order.POST_ORDER;
-public class TreeRootHolderRule implements TestRule, TreeRootHolder {
+public class TreeRootHolderRule implements TestRule, MutableTreeRootHolder {
private Component root;
private Map<Integer, Component> componentsByRef = new HashMap<>();
@@ -74,7 +75,7 @@ public class TreeRootHolderRule implements TestRule, TreeRootHolder {
return component;
}
- public void setRoot(Component newRoot) {
+ public TreeRootHolderRule setRoot(Component newRoot) {
this.root = Objects.requireNonNull(newRoot);
new DepthTraversalTypeAwareVisitor(Component.Type.FILE, POST_ORDER) {
@Override
@@ -82,5 +83,6 @@ public class TreeRootHolderRule implements TestRule, TreeRootHolder {
componentsByRef.put(component.getRef(), component);
}
}.visit(root);
+ return this;
}
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/component/MutableTreeRootHolderRule.java b/server/sonar-server/src/test/java/org/sonar/server/computation/component/MutableTreeRootHolderRule.java
index eaf2a512a65..f2309b75722 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/component/MutableTreeRootHolderRule.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/component/MutableTreeRootHolderRule.java
@@ -23,7 +23,7 @@ import org.sonar.server.computation.batch.TreeRootHolderRule;
public class MutableTreeRootHolderRule extends TreeRootHolderRule implements MutableTreeRootHolder {
@Override
- public void setRoot(Component newRoot) {
- super.setRoot(newRoot);
+ public MutableTreeRootHolderRule setRoot(Component newRoot) {
+ return (MutableTreeRootHolderRule)super.setRoot(newRoot);
}
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/TrackerRawInputFactoryTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/TrackerRawInputFactoryTest.java
new file mode 100644
index 00000000000..c3f6f0e031b
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/TrackerRawInputFactoryTest.java
@@ -0,0 +1,154 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.computation.issue;
+
+import com.google.common.collect.Iterators;
+import java.util.Collection;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rule.Severity;
+import org.sonar.batch.protocol.Constants;
+import org.sonar.batch.protocol.output.BatchReport;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.tracking.Input;
+import org.sonar.server.computation.batch.BatchReportReaderRule;
+import org.sonar.server.computation.batch.TreeRootHolderRule;
+import org.sonar.server.computation.component.Component;
+import org.sonar.server.computation.component.DumbComponent;
+import org.sonar.server.computation.issue.commonrule.CommonRuleEngine;
+import org.sonar.server.rule.CommonRuleKeys;
+
+import static java.util.Arrays.asList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class TrackerRawInputFactoryTest {
+
+ static DumbComponent PROJECT = DumbComponent.builder(Component.Type.PROJECT, 1).setKey("PROJECT_KEY_2").setUuid("PROJECT_UUID_1").build();
+ static DumbComponent FILE = DumbComponent.builder(Component.Type.FILE, 2).setKey("FILE_KEY_2").setUuid("FILE_UUID_2").build();
+
+ @Rule
+ public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule().setRoot(PROJECT);
+
+ @Rule
+ public BatchReportReaderRule reportReader = new BatchReportReaderRule();
+
+ CommonRuleEngine commonRuleEngine = mock(CommonRuleEngine.class);
+
+ TrackerRawInputFactory underTest = new TrackerRawInputFactory(treeRootHolder, reportReader, commonRuleEngine);
+
+ @Test
+ public void load_source_hash_sequences() throws Exception {
+ reportReader.putFileSourceLines(FILE.getRef(), "line 1;", "line 2;");
+ Input<DefaultIssue> input = underTest.create(FILE);
+
+ assertThat(input.getLineHashSequence()).isNotNull();
+ assertThat(input.getLineHashSequence().getHashForLine(1)).isNotEmpty();
+ assertThat(input.getLineHashSequence().getHashForLine(2)).isNotEmpty();
+ assertThat(input.getLineHashSequence().getHashForLine(3)).isEmpty();
+
+ assertThat(input.getBlockHashSequence()).isNotNull();
+ }
+
+ @Test
+ public void load_source_hash_sequences_only_on_files() throws Exception {
+ Input<DefaultIssue> input = underTest.create(PROJECT);
+
+ assertThat(input.getLineHashSequence()).isNotNull();
+ assertThat(input.getBlockHashSequence()).isNotNull();
+ }
+
+ @Test
+ public void load_issues() throws Exception {
+ reportReader.putFileSourceLines(FILE.getRef(), "line 1;", "line 2;");
+ BatchReport.Issue reportIssue = BatchReport.Issue.newBuilder()
+ .setLine(2)
+ .setMsg("the message")
+ .setRuleRepository("java")
+ .setRuleKey("S001")
+ .setSeverity(Constants.Severity.BLOCKER)
+ .setEffortToFix(3.14)
+ .addTag("bug").addTag("performance")
+ .build();
+ reportReader.putIssues(FILE.getRef(), asList(reportIssue));
+ Input<DefaultIssue> input = underTest.create(FILE);
+
+ Collection<DefaultIssue> issues = input.getIssues();
+ assertThat(issues).hasSize(1);
+ DefaultIssue issue = Iterators.getOnlyElement(issues.iterator());
+
+ // fields set by analysis report
+ assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("java", "S001"));
+ assertThat(issue.severity()).isEqualTo(Severity.BLOCKER);
+ assertThat(issue.line()).isEqualTo(2);
+ assertThat(issue.effortToFix()).isEqualTo(3.14);
+ assertThat(issue.message()).isEqualTo("the message");
+ assertThat(issue.tags()).containsOnly("bug", "performance");
+
+ // fields set by compute engine
+ assertThat(issue.checksum()).isEqualTo(input.getLineHashSequence().getHashForLine(2));
+ assertInitializedIssue(issue);
+ }
+
+ @Test
+ public void ignore_report_issues_on_common_rules() throws Exception {
+ reportReader.putFileSourceLines(FILE.getRef(), "line 1;", "line 2;");
+ BatchReport.Issue reportIssue = BatchReport.Issue.newBuilder()
+ .setMsg("the message")
+ .setRuleRepository(CommonRuleKeys.commonRepositoryForLang("java"))
+ .setRuleKey("S001")
+ .setSeverity(Constants.Severity.BLOCKER)
+ .build();
+ reportReader.putIssues(FILE.getRef(), asList(reportIssue));
+
+ Input<DefaultIssue> input = underTest.create(FILE);
+
+ assertThat(input.getIssues()).isEmpty();
+ }
+
+ @Test
+ public void load_issues_of_compute_engine_common_rules() throws Exception {
+ reportReader.putFileSourceLines(FILE.getRef(), "line 1;", "line 2;");
+ DefaultIssue ceIssue = new DefaultIssue()
+ .setRuleKey(RuleKey.of(CommonRuleKeys.commonRepositoryForLang("java"), "InsufficientCoverage"))
+ .setMessage("not enough coverage")
+ .setEffortToFix(10.0);
+ when(commonRuleEngine.process(FILE)).thenReturn(asList(ceIssue));
+
+ Input<DefaultIssue> input = underTest.create(FILE);
+
+ assertThat(input.getIssues()).containsOnly(ceIssue);
+ assertInitializedIssue(input.getIssues().iterator().next());
+ }
+
+ private void assertInitializedIssue(DefaultIssue issue) {
+ assertThat(issue.componentKey()).isEqualTo(FILE.getKey());
+ assertThat(issue.componentUuid()).isEqualTo(FILE.getUuid());
+ assertThat(issue.resolution()).isNull();
+ assertThat(issue.status()).isEqualTo(Issue.STATUS_OPEN);
+ assertThat(issue.key()).isNull();
+ assertThat(issue.actionPlanKey()).isNull();
+ assertThat(issue.authorLogin()).isNull();
+ assertThat(issue.debt()).isNull();
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/commonrule/BranchCoverageRuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/commonrule/BranchCoverageRuleTest.java
new file mode 100644
index 00000000000..71b82064e31
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/commonrule/BranchCoverageRuleTest.java
@@ -0,0 +1,57 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.computation.issue.commonrule;
+
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.server.rule.CommonRuleKeys;
+
+public class BranchCoverageRuleTest extends CoverageRuleTest {
+
+ @Override
+ protected CommonRule createRule() {
+ return new BranchCoverageRule(activeRuleHolder, metricRepository, measureRepository);
+ }
+
+ @Override
+ protected RuleKey getRuleKey() {
+ return RuleKey.of("common-java", CommonRuleKeys.INSUFFICIENT_BRANCH_COVERAGE);
+ }
+
+ @Override
+ protected String getCoverageMetricKey() {
+ return CoreMetrics.BRANCH_COVERAGE_KEY;
+ }
+
+ @Override
+ protected String getToCoverMetricKey() {
+ return CoreMetrics.CONDITIONS_TO_COVER_KEY;
+ }
+
+ @Override
+ protected String getUncoveredMetricKey() {
+ return CoreMetrics.UNCOVERED_CONDITIONS_KEY;
+ }
+
+ @Override
+ protected String getExpectedIssueMessage() {
+ return "23 more branches need to be covered by unit tests to reach the minimum threshold of 65.0% branch coverage.";
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/commonrule/CommentDensityRuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/commonrule/CommentDensityRuleTest.java
new file mode 100644
index 00000000000..1c43abfe951
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/commonrule/CommentDensityRuleTest.java
@@ -0,0 +1,139 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.computation.issue.commonrule;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rule.Severity;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.server.computation.batch.TreeRootHolderRule;
+import org.sonar.server.computation.component.Component;
+import org.sonar.server.computation.component.DumbComponent;
+import org.sonar.server.computation.component.FileAttributes;
+import org.sonar.server.computation.measure.Measure;
+import org.sonar.server.computation.measure.MeasureRepositoryRule;
+import org.sonar.server.computation.metric.MetricRepositoryRule;
+import org.sonar.server.computation.qualityprofile.ActiveRule;
+import org.sonar.server.computation.qualityprofile.ActiveRulesHolderRule;
+import org.sonar.server.rule.CommonRuleKeys;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.server.computation.component.DumbComponent.DUMB_PROJECT;
+
+public class CommentDensityRuleTest {
+
+ static RuleKey RULE_KEY = RuleKey.of(CommonRuleKeys.commonRepositoryForLang("java"), CommonRuleKeys.INSUFFICIENT_COMMENT_DENSITY);
+
+ static DumbComponent FILE = DumbComponent.builder(Component.Type.FILE, 1)
+ .setFileAttributes(new FileAttributes(false, "java"))
+ .build();
+
+ @Rule
+ public ActiveRulesHolderRule activeRuleHolder = new ActiveRulesHolderRule();
+
+ @Rule
+ public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
+ .add(CoreMetrics.COMMENT_LINES_DENSITY)
+ .add(CoreMetrics.COMMENT_LINES)
+ .add(CoreMetrics.NCLOC);
+
+ @Rule
+ public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule().setRoot(DUMB_PROJECT);
+
+ @Rule
+ public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
+
+ CommentDensityRule underTest = new CommentDensityRule(activeRuleHolder, measureRepository, metricRepository);
+
+ @Test
+ public void no_issues_if_enough_comments() {
+ activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL));
+ measureRepository.addRawMeasure(FILE.getRef(), CoreMetrics.COMMENT_LINES_DENSITY_KEY, Measure.newMeasureBuilder().create(90.0));
+
+ DefaultIssue issue = underTest.processFile(FILE, "java");
+
+ assertThat(issue).isNull();
+ }
+
+ @Test
+ public void issue_if_not_enough_comments() {
+ activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL));
+ measureRepository.addRawMeasure(FILE.getRef(), CoreMetrics.COMMENT_LINES_DENSITY_KEY, Measure.newMeasureBuilder().create(10.0));
+ measureRepository.addRawMeasure(FILE.getRef(), CoreMetrics.COMMENT_LINES_KEY, Measure.newMeasureBuilder().create(40));
+ measureRepository.addRawMeasure(FILE.getRef(), CoreMetrics.NCLOC_KEY, Measure.newMeasureBuilder().create(360));
+
+ DefaultIssue issue = underTest.processFile(FILE, "java");
+
+ assertThat(issue.ruleKey()).isEqualTo(RULE_KEY);
+ assertThat(issue.severity()).isEqualTo(Severity.CRITICAL);
+ // min_comments = (min_percent * ncloc) / (1 - min_percent)
+ // -> threshold of 25% for 360 ncloc is 120 comment lines. 40 are already written.
+ assertThat(issue.effortToFix()).isEqualTo(120.0 - 40.0);
+ assertThat(issue.message()).isEqualTo("80 more comment lines need to be written to reach the minimum threshold of 25.0% comment density.");
+ }
+
+ @Test
+ public void issue_if_not_enough_comments__test_ceil() {
+ activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL));
+ measureRepository.addRawMeasure(FILE.getRef(), CoreMetrics.COMMENT_LINES_DENSITY_KEY, Measure.newMeasureBuilder().create(0.0));
+ measureRepository.addRawMeasure(FILE.getRef(), CoreMetrics.COMMENT_LINES_KEY, Measure.newMeasureBuilder().create(0));
+ measureRepository.addRawMeasure(FILE.getRef(), CoreMetrics.NCLOC_KEY, Measure.newMeasureBuilder().create(1));
+
+ DefaultIssue issue = underTest.processFile(FILE, "java");
+
+ assertThat(issue.ruleKey()).isEqualTo(RULE_KEY);
+ assertThat(issue.severity()).isEqualTo(Severity.CRITICAL);
+ // 1 ncloc requires 1 comment line to reach 25% of comment density
+ assertThat(issue.effortToFix()).isEqualTo(1.0);
+ assertThat(issue.message()).isEqualTo("1 more comment lines need to be written to reach the minimum threshold of 25.0% comment density.");
+ }
+
+ // /**
+ // * SQALE-110
+ // */
+ // @Test
+ // public void shouldFailIfMinimumCommentDensitySetTo100() {
+ // check.setMinimumCommentDensity(100);
+ //
+ // thrown.expect(IllegalArgumentException.class);
+ // thrown.expectMessage("100.0 is not a valid value for minimum required comment density for rule 'CommentDensityCheck' (must be >= 0 and < 100).");
+ //
+ // check.checkResource(resource, context, null, perspectives);
+ //
+ // verify(perspectives, times(0)).as(Issuable.class, resource);
+ // }
+ //
+ // /**
+ // * SQALE-110
+ // */
+ // @Test
+ // public void shouldFailIfMinimumCommentDensitySetToNegative() {
+ // check.setMinimumCommentDensity(-5);
+ //
+ // thrown.expect(IllegalArgumentException.class);
+ // thrown.expectMessage("-5.0 is not a valid value for minimum required comment density for rule 'CommentDensityCheck' (must be >= 0 and < 100).");
+ //
+ // check.checkResource(resource, context, null, mock(ResourcePerspectives.class));
+ //
+ // verify(perspectives, times(0)).as(Issuable.class, resource);
+ // }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/commonrule/CommonRuleEngineImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/commonrule/CommonRuleEngineImplTest.java
new file mode 100644
index 00000000000..5eef07383c7
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/commonrule/CommonRuleEngineImplTest.java
@@ -0,0 +1,75 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.computation.issue.commonrule;
+
+import java.util.Collection;
+import org.junit.Test;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.server.computation.component.Component;
+import org.sonar.server.computation.component.DumbComponent;
+import org.sonar.server.computation.component.FileAttributes;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+import static org.sonar.server.computation.component.DumbComponent.DUMB_PROJECT;
+
+public class CommonRuleEngineImplTest {
+
+ CommonRule rule1 = mock(CommonRule.class);
+ CommonRule rule2 = mock(CommonRule.class);
+ CommonRuleEngineImpl underTest = new CommonRuleEngineImpl(rule1, rule2);
+
+ @Test
+ public void process_files_with_known_language() throws Exception {
+ DumbComponent file = DumbComponent.builder(Component.Type.FILE, 1)
+ .setKey("FILE_KEY").setUuid("FILE_UUID")
+ .setFileAttributes(new FileAttributes(false, "java"))
+ .build();
+ DefaultIssue issue = new DefaultIssue();
+ when(rule1.processFile(file, "java")).thenReturn(issue);
+ when(rule2.processFile(file, "java")).thenReturn(null);
+
+ Collection<DefaultIssue> issues = underTest.process(file);
+ assertThat(issues).containsOnly(issue);
+ }
+
+ @Test
+ public void do_not_process_files_with_unknown_language() throws Exception {
+ DumbComponent file = DumbComponent.builder(Component.Type.FILE, 1)
+ .setKey("FILE_KEY").setUuid("FILE_UUID")
+ .setFileAttributes(new FileAttributes(false, null))
+ .build();
+
+ Collection<DefaultIssue> issues = underTest.process(file);
+
+ assertThat(issues).isEmpty();
+ verifyZeroInteractions(rule1, rule2);
+ }
+
+ @Test
+ public void do_not_process_non_files() throws Exception {
+ Collection<DefaultIssue> issues = underTest.process(DUMB_PROJECT);
+
+ assertThat(issues).isEmpty();
+ verifyZeroInteractions(rule1, rule2);
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/commonrule/CoverageRuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/commonrule/CoverageRuleTest.java
new file mode 100644
index 00000000000..fa33b25a88d
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/commonrule/CoverageRuleTest.java
@@ -0,0 +1,129 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.computation.issue.commonrule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rule.Severity;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.server.computation.batch.TreeRootHolderRule;
+import org.sonar.server.computation.component.Component;
+import org.sonar.server.computation.component.DumbComponent;
+import org.sonar.server.computation.component.FileAttributes;
+import org.sonar.server.computation.measure.Measure;
+import org.sonar.server.computation.measure.MeasureRepositoryRule;
+import org.sonar.server.computation.metric.MetricRepositoryRule;
+import org.sonar.server.computation.qualityprofile.ActiveRule;
+import org.sonar.server.computation.qualityprofile.ActiveRulesHolderRule;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.server.computation.component.DumbComponent.DUMB_PROJECT;
+
+public abstract class CoverageRuleTest {
+
+ static DumbComponent FILE = DumbComponent.builder(Component.Type.FILE, 1)
+ .setFileAttributes(new FileAttributes(false, "java"))
+ .build();
+
+ @Rule
+ public ActiveRulesHolderRule activeRuleHolder = new ActiveRulesHolderRule();
+
+ @Rule
+ public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
+ .add(CoreMetrics.LINE_COVERAGE)
+ .add(CoreMetrics.LINES_TO_COVER)
+ .add(CoreMetrics.UNCOVERED_LINES)
+ .add(CoreMetrics.BRANCH_COVERAGE)
+ .add(CoreMetrics.CONDITIONS_TO_COVER)
+ .add(CoreMetrics.UNCOVERED_CONDITIONS);
+
+ @Rule
+ public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
+
+ @Rule
+ public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
+
+ CommonRule underTest = createRule();
+
+ @Before
+ public void setUp() {
+ treeRootHolder.setRoot(DUMB_PROJECT);
+ }
+
+ protected abstract CommonRule createRule();
+
+ protected abstract RuleKey getRuleKey();
+
+ protected abstract String getCoverageMetricKey();
+
+ protected abstract String getToCoverMetricKey();
+
+ protected abstract String getUncoveredMetricKey();
+
+ @Test
+ public void no_issue_if_enough_coverage() {
+ activeRuleHolder.put(new ActiveRule(getRuleKey(), Severity.CRITICAL));
+ measureRepository.addRawMeasure(FILE.getRef(), getCoverageMetricKey(), Measure.newMeasureBuilder().create(90.0));
+
+ DefaultIssue issue = underTest.processFile(FILE, "java");
+
+ assertThat(issue).isNull();
+ }
+
+ @Test
+ public void issue_if_coverage_is_too_low() {
+ activeRuleHolder.put(new ActiveRule(getRuleKey(), Severity.CRITICAL));
+ measureRepository.addRawMeasure(FILE.getRef(), getCoverageMetricKey(), Measure.newMeasureBuilder().create(20.0));
+ measureRepository.addRawMeasure(FILE.getRef(), getUncoveredMetricKey(), Measure.newMeasureBuilder().create(40.0));
+ measureRepository.addRawMeasure(FILE.getRef(), getToCoverMetricKey(), Measure.newMeasureBuilder().create(50.0));
+
+ DefaultIssue issue = underTest.processFile(FILE, "java");
+
+ assertThat(issue.ruleKey()).isEqualTo(getRuleKey());
+ assertThat(issue.severity()).isEqualTo(Severity.CRITICAL);
+ // FIXME explain
+ assertThat(issue.effortToFix()).isEqualTo(23.0);
+ assertThat(issue.message()).isEqualTo(getExpectedIssueMessage());
+ }
+
+ protected abstract String getExpectedIssueMessage();
+
+ @Test
+ public void no_issue_if_coverage_is_not_set() {
+ activeRuleHolder.put(new ActiveRule(getRuleKey(), Severity.CRITICAL));
+
+ DefaultIssue issue = underTest.processFile(FILE, "java");
+
+ assertThat(issue).isNull();
+ }
+
+ @Test
+ public void ignored_if_rule_is_deactivated() {
+ // coverage is too low, but rule is not activated
+ measureRepository.addRawMeasure(FILE.getRef(), getCoverageMetricKey(), Measure.newMeasureBuilder().create(20.0));
+
+ DefaultIssue issue = underTest.processFile(FILE, "java");
+
+ assertThat(issue).isNull();
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/commonrule/DuplicatedBlockRuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/commonrule/DuplicatedBlockRuleTest.java
new file mode 100644
index 00000000000..8eff15bedaa
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/commonrule/DuplicatedBlockRuleTest.java
@@ -0,0 +1,87 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.computation.issue.commonrule;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rule.Severity;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.server.computation.batch.TreeRootHolderRule;
+import org.sonar.server.computation.component.Component;
+import org.sonar.server.computation.component.DumbComponent;
+import org.sonar.server.computation.component.FileAttributes;
+import org.sonar.server.computation.measure.Measure;
+import org.sonar.server.computation.measure.MeasureRepositoryRule;
+import org.sonar.server.computation.metric.MetricRepositoryRule;
+import org.sonar.server.computation.qualityprofile.ActiveRule;
+import org.sonar.server.computation.qualityprofile.ActiveRulesHolderRule;
+import org.sonar.server.rule.CommonRuleKeys;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.server.computation.component.DumbComponent.DUMB_PROJECT;
+
+public class DuplicatedBlockRuleTest {
+
+ static RuleKey RULE_KEY = RuleKey.of(CommonRuleKeys.commonRepositoryForLang("java"), CommonRuleKeys.DUPLICATED_BLOCKS);
+
+ static DumbComponent FILE = DumbComponent.builder(Component.Type.FILE, 1)
+ .setFileAttributes(new FileAttributes(false, "java"))
+ .build();
+
+ @Rule
+ public ActiveRulesHolderRule activeRuleHolder = new ActiveRulesHolderRule();
+
+ @Rule
+ public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
+ .add(CoreMetrics.DUPLICATED_BLOCKS);
+
+ @Rule
+ public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule().setRoot(DUMB_PROJECT);
+
+ @Rule
+ public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
+
+ DuplicatedBlockRule underTest = new DuplicatedBlockRule(activeRuleHolder, measureRepository, metricRepository);
+
+ @Test
+ public void no_issue_if_no_duplicated_blocks() throws Exception {
+ activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL));
+ measureRepository.addRawMeasure(FILE.getRef(), CoreMetrics.DUPLICATED_BLOCKS_KEY, Measure.newMeasureBuilder().create(0));
+
+ DefaultIssue issue = underTest.processFile(FILE, "java");
+
+ assertThat(issue).isNull();
+ }
+
+ @Test
+ public void issue_if_duplicated_blocks() throws Exception {
+ activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL));
+ measureRepository.addRawMeasure(FILE.getRef(), CoreMetrics.DUPLICATED_BLOCKS_KEY, Measure.newMeasureBuilder().create(3));
+
+ DefaultIssue issue = underTest.processFile(FILE, "java");
+
+ assertThat(issue.ruleKey()).isEqualTo(RULE_KEY);
+ assertThat(issue.severity()).isEqualTo(Severity.CRITICAL);
+ assertThat(issue.effortToFix()).isEqualTo(3.0);
+ assertThat(issue.message()).isEqualTo("3 duplicated blocks of code must be removed.");
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/commonrule/LineCoverageRuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/commonrule/LineCoverageRuleTest.java
new file mode 100644
index 00000000000..202b9a529bc
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/commonrule/LineCoverageRuleTest.java
@@ -0,0 +1,57 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.computation.issue.commonrule;
+
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.server.rule.CommonRuleKeys;
+
+public class LineCoverageRuleTest extends CoverageRuleTest {
+
+ @Override
+ protected CommonRule createRule() {
+ return new LineCoverageRule(activeRuleHolder, metricRepository, measureRepository);
+ }
+
+ @Override
+ protected RuleKey getRuleKey() {
+ return RuleKey.of("common-java", CommonRuleKeys.INSUFFICIENT_LINE_COVERAGE);
+ }
+
+ @Override
+ protected String getCoverageMetricKey() {
+ return CoreMetrics.LINE_COVERAGE_KEY;
+ }
+
+ @Override
+ protected String getToCoverMetricKey() {
+ return CoreMetrics.LINES_TO_COVER_KEY;
+ }
+
+ @Override
+ protected String getUncoveredMetricKey() {
+ return CoreMetrics.UNCOVERED_LINES_KEY;
+ }
+
+ @Override
+ protected String getExpectedIssueMessage() {
+ return "23 more lines of code need to be covered by unit tests to reach the minimum threshold of 65.0% lines coverage.";
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/commonrule/SkippedTestRuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/commonrule/SkippedTestRuleTest.java
new file mode 100644
index 00000000000..eaa9b121b19
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/commonrule/SkippedTestRuleTest.java
@@ -0,0 +1,97 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.computation.issue.commonrule;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rule.Severity;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.server.computation.batch.TreeRootHolderRule;
+import org.sonar.server.computation.component.Component;
+import org.sonar.server.computation.component.DumbComponent;
+import org.sonar.server.computation.component.FileAttributes;
+import org.sonar.server.computation.measure.Measure;
+import org.sonar.server.computation.measure.MeasureRepositoryRule;
+import org.sonar.server.computation.metric.MetricRepositoryRule;
+import org.sonar.server.computation.qualityprofile.ActiveRule;
+import org.sonar.server.computation.qualityprofile.ActiveRulesHolderRule;
+import org.sonar.server.rule.CommonRuleKeys;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.server.computation.component.DumbComponent.DUMB_PROJECT;
+
+public class SkippedTestRuleTest {
+
+ static RuleKey RULE_KEY = RuleKey.of(CommonRuleKeys.commonRepositoryForLang("java"), CommonRuleKeys.SKIPPED_UNIT_TESTS);
+
+ static DumbComponent FILE = DumbComponent.builder(Component.Type.FILE, 1)
+ .setFileAttributes(new FileAttributes(true, "java"))
+ .setName("FooTest.java")
+ .build();
+
+ @Rule
+ public ActiveRulesHolderRule activeRuleHolder = new ActiveRulesHolderRule();
+
+ @Rule
+ public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
+ .add(CoreMetrics.SKIPPED_TESTS);
+
+ @Rule
+ public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule().setRoot(DUMB_PROJECT);
+
+ @Rule
+ public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
+
+ CommonRule underTest = new SkippedTestRule(activeRuleHolder, measureRepository, metricRepository);
+
+ @Test
+ public void issue_if_skipped_tests() throws Exception {
+ activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL));
+ measureRepository.addRawMeasure(FILE.getRef(), CoreMetrics.SKIPPED_TESTS_KEY, Measure.newMeasureBuilder().create(2));
+
+ DefaultIssue issue = underTest.processFile(FILE, "java");
+
+ assertThat(issue.ruleKey()).isEqualTo(RULE_KEY);
+ assertThat(issue.severity()).isEqualTo(Severity.CRITICAL);
+ assertThat(issue.effortToFix()).isEqualTo(2.0);
+ assertThat(issue.message()).isEqualTo("Fix or remove skipped unit tests in file \"FooTest.java\".");
+ }
+
+ @Test
+ public void no_issues_if_zero_skipped_tests() throws Exception {
+ activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL));
+ measureRepository.addRawMeasure(FILE.getRef(), CoreMetrics.SKIPPED_TESTS_KEY, Measure.newMeasureBuilder().create(0));
+
+ DefaultIssue issue = underTest.processFile(FILE, "java");
+
+ assertThat(issue).isNull();
+ }
+
+ @Test
+ public void no_issues_if_measure_is_absent() throws Exception {
+ activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL));
+
+ DefaultIssue issue = underTest.processFile(FILE, "java");
+
+ assertThat(issue).isNull();
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/commonrule/TestErrorRuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/commonrule/TestErrorRuleTest.java
new file mode 100644
index 00000000000..3d8594d9744
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/commonrule/TestErrorRuleTest.java
@@ -0,0 +1,100 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.computation.issue.commonrule;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rule.Severity;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.server.computation.batch.TreeRootHolderRule;
+import org.sonar.server.computation.component.Component;
+import org.sonar.server.computation.component.DumbComponent;
+import org.sonar.server.computation.component.FileAttributes;
+import org.sonar.server.computation.measure.Measure;
+import org.sonar.server.computation.measure.MeasureRepositoryRule;
+import org.sonar.server.computation.metric.MetricRepositoryRule;
+import org.sonar.server.computation.qualityprofile.ActiveRule;
+import org.sonar.server.computation.qualityprofile.ActiveRulesHolderRule;
+import org.sonar.server.rule.CommonRuleKeys;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.server.computation.component.DumbComponent.DUMB_PROJECT;
+
+public class TestErrorRuleTest {
+
+ static RuleKey RULE_KEY = RuleKey.of(CommonRuleKeys.commonRepositoryForLang("java"), CommonRuleKeys.FAILED_UNIT_TESTS);
+
+ static DumbComponent FILE = DumbComponent.builder(Component.Type.FILE, 1)
+ .setFileAttributes(new FileAttributes(true, "java"))
+ .setName("FooTest.java")
+ .build();
+
+ @Rule
+ public ActiveRulesHolderRule activeRuleHolder = new ActiveRulesHolderRule();
+
+ @Rule
+ public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
+ .add(CoreMetrics.TEST_ERRORS)
+ .add(CoreMetrics.TEST_FAILURES);
+
+ @Rule
+ public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule().setRoot(DUMB_PROJECT);
+
+ @Rule
+ public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
+
+ CommonRule underTest = new TestErrorRule(activeRuleHolder, measureRepository, metricRepository);
+
+ @Test
+ public void issue_if_errors_or_failures() throws Exception {
+ activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL));
+ measureRepository.addRawMeasure(FILE.getRef(), CoreMetrics.TEST_ERRORS_KEY, Measure.newMeasureBuilder().create(2));
+ measureRepository.addRawMeasure(FILE.getRef(), CoreMetrics.TEST_FAILURES_KEY, Measure.newMeasureBuilder().create(1));
+
+ DefaultIssue issue = underTest.processFile(FILE, "java");
+
+ assertThat(issue.ruleKey()).isEqualTo(RULE_KEY);
+ assertThat(issue.severity()).isEqualTo(Severity.CRITICAL);
+ assertThat(issue.effortToFix()).isEqualTo(3.0);
+ assertThat(issue.message()).isEqualTo("Fix failing unit tests on file \"FooTest.java\".");
+ }
+
+ @Test
+ public void no_issues_if_zero_errors_and_failures() throws Exception {
+ activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL));
+ measureRepository.addRawMeasure(FILE.getRef(), CoreMetrics.TEST_ERRORS_KEY, Measure.newMeasureBuilder().create(0));
+ measureRepository.addRawMeasure(FILE.getRef(), CoreMetrics.TEST_FAILURES_KEY, Measure.newMeasureBuilder().create(0));
+
+ DefaultIssue issue = underTest.processFile(FILE, "java");
+
+ assertThat(issue).isNull();
+ }
+
+ @Test
+ public void no_issues_if_test_measures_are_absent() throws Exception {
+ activeRuleHolder.put(new ActiveRule(RULE_KEY, Severity.CRITICAL));
+
+ DefaultIssue issue = underTest.processFile(FILE, "java");
+
+ assertThat(issue).isNull();
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/qualityprofile/ActiveRulesHolderImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/qualityprofile/ActiveRulesHolderImplTest.java
new file mode 100644
index 00000000000..4497c8071de
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/qualityprofile/ActiveRulesHolderImplTest.java
@@ -0,0 +1,87 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.computation.qualityprofile;
+
+import com.google.common.base.Optional;
+import java.util.Collections;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rule.Severity;
+
+import static java.util.Arrays.asList;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ActiveRulesHolderImplTest {
+
+ static final RuleKey RULE_KEY = RuleKey.of("java", "S001");
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ ActiveRulesHolderImpl underTest = new ActiveRulesHolderImpl();
+
+ @Test
+ public void get_inactive_rule() throws Exception {
+ underTest.set(Collections.<ActiveRule>emptyList());
+ Optional<ActiveRule> activeRule = underTest.get(RULE_KEY);
+ assertThat(activeRule.isPresent()).isFalse();
+ }
+
+ @Test
+ public void get_active_rule() throws Exception {
+ underTest.set(asList(new ActiveRule(RULE_KEY, Severity.BLOCKER)));
+
+ Optional<ActiveRule> activeRule = underTest.get(RULE_KEY);
+ assertThat(activeRule.isPresent()).isTrue();
+ assertThat(activeRule.get().getRuleKey()).isEqualTo(RULE_KEY);
+ assertThat(activeRule.get().getSeverity()).isEqualTo(Severity.BLOCKER);
+ }
+
+ @Test
+ public void can_not_set_twice() throws Exception {
+ thrown.expect(IllegalStateException.class);
+ thrown.expectMessage("Active rules have already been initialized");
+
+ underTest.set(asList(new ActiveRule(RULE_KEY, Severity.BLOCKER)));
+ underTest.set(Collections.<ActiveRule>emptyList());
+
+ }
+
+ @Test
+ public void can_not_get_if_not_initialized() throws Exception {
+ thrown.expect(IllegalStateException.class);
+ thrown.expectMessage("Active rules have not been initialized yet");
+
+ underTest.get(RULE_KEY);
+ }
+
+ @Test
+ public void can_not_set_duplicated_rules() throws Exception {
+ thrown.expect(IllegalArgumentException.class);
+ thrown.expectMessage("Active rule must not be declared multiple times: java:S001");
+
+ underTest.set(asList(
+ new ActiveRule(RULE_KEY, Severity.BLOCKER),
+ new ActiveRule(RULE_KEY, Severity.MAJOR)
+ ));
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/qualityprofile/ActiveRulesHolderRule.java b/server/sonar-server/src/test/java/org/sonar/server/computation/qualityprofile/ActiveRulesHolderRule.java
new file mode 100644
index 00000000000..5277198b175
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/qualityprofile/ActiveRulesHolderRule.java
@@ -0,0 +1,46 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.computation.qualityprofile;
+
+import com.google.common.base.Optional;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.rules.ExternalResource;
+import org.sonar.api.rule.RuleKey;
+
+public class ActiveRulesHolderRule extends ExternalResource implements ActiveRulesHolder {
+
+ private final Map<RuleKey, ActiveRule> activeRulesByKey = new HashMap<>();
+
+ @Override
+ public Optional<ActiveRule> get(RuleKey ruleKey) {
+ return Optional.fromNullable(activeRulesByKey.get(ruleKey));
+ }
+
+ public ActiveRulesHolderRule put(ActiveRule activeRule) {
+ activeRulesByKey.put(activeRule.getRuleKey(), activeRule);
+ return this;
+ }
+
+ @Override
+ protected void after() {
+ activeRulesByKey.clear();
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/CommonRuleKeysTest.java b/server/sonar-server/src/test/java/org/sonar/server/rule/CommonRuleKeysTest.java
new file mode 100644
index 00000000000..974fbfd4750
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/rule/CommonRuleKeysTest.java
@@ -0,0 +1,39 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.rule;
+
+import org.junit.Test;
+import org.sonar.test.TestUtils;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class CommonRuleKeysTest {
+
+ @Test
+ public void wonderful_test_for_commonRepositoryForLang() throws Exception {
+ assertThat(CommonRuleKeys.commonRepositoryForLang("java")).isEqualTo("common-java");
+ }
+
+ @Test
+ public void wonderful_test_to_verify_that_this_class_is_an_helper_class() throws Exception {
+ assertThat(TestUtils.hasOnlyPrivateConstructors(CommonRuleKeys.class));
+
+ }
+}