aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Brandhof <simon.brandhof@sonarsource.com>2015-06-23 16:06:31 +0200
committerSimon Brandhof <simon.brandhof@sonarsource.com>2015-07-02 16:07:03 +0200
commit08cdf04215f4b0b3a4ccfe7902c36df4906688c3 (patch)
tree21d9f9f41edc21e323c55e5ab46c5f973d0b8570
parentbf4118d6a9ceb9ad24274cdc6537d4a607121815 (diff)
downloadsonarqube-08cdf04215f4b0b3a4ccfe7902c36df4906688c3.tar.gz
sonarqube-08cdf04215f4b0b3a4ccfe7902c36df4906688c3.zip
SONAR-6623 count issues
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainerImpl.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/CountIssuesListener.java273
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/DebtCalculator.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueListener.java14
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueListeners.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/period/PeriodsHolder.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/period/PeriodsHolderImpl.java6
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java7
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputeIssueMeasuresStep.java341
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/step/IntegrateIssuesStep.java (renamed from server/sonar-server/src/main/java/org/sonar/server/computation/issue/IntegrateIssuesStep.java)14
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/issue/CountIssuesListenerTest.java267
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputeIssueMeasuresStepTest.java412
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/computation/issue/ScmAccountToUserLoaderTest/charlie.json (renamed from server/sonar-server/src/test/resources/org/sonar/server/computation/issue/ScmAccountCacheLoaderTest/charlie.json)0
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/computation/issue/ScmAccountToUserLoaderTest/charlie_conflict.json (renamed from server/sonar-server/src/test/resources/org/sonar/server/computation/issue/ScmAccountCacheLoaderTest/charlie_conflict.json)0
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/compute/SeverityUtils.java66
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/issue/tracking/IssueTrackingTest.java2
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/workflow/UnsetLine.java29
17 files changed, 576 insertions, 865 deletions
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 f4c31846f1f..4bd929e44e1 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
@@ -45,6 +45,7 @@ import org.sonar.server.computation.debt.DebtModelHolderImpl;
import org.sonar.server.computation.event.EventRepositoryImpl;
import org.sonar.server.computation.formula.CoreFormulaRepositoryImpl;
import org.sonar.server.computation.issue.BaseIssuesLoader;
+import org.sonar.server.computation.issue.CountIssuesListener;
import org.sonar.server.computation.issue.DebtCalculator;
import org.sonar.server.computation.issue.DefaultAssignee;
import org.sonar.server.computation.issue.IssueAssigner;
@@ -176,6 +177,7 @@ public class ComputeEngineContainerImpl extends ComponentContainer implements Co
DebtCalculator.class,
IssueListeners.class,
IssueLifecycle.class,
+ CountIssuesListener.class,
UpdateConflictResolver.class,
TrackerBaseInputFactory.class,
TrackerRawInputFactory.class,
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/CountIssuesListener.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/CountIssuesListener.java
new file mode 100644
index 00000000000..ff6034f8a08
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/CountIssuesListener.java
@@ -0,0 +1,273 @@
+/*
+ * 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.HashMultiset;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Multiset;
+import java.util.HashMap;
+import java.util.Map;
+import javax.annotation.Nullable;
+import org.sonar.api.issue.Issue;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.tracking.Tracking;
+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.measure.MeasureVariations;
+import org.sonar.server.computation.metric.Metric;
+import org.sonar.server.computation.metric.MetricRepository;
+import org.sonar.server.computation.period.Period;
+import org.sonar.server.computation.period.PeriodsHolder;
+
+import static org.sonar.api.issue.Issue.STATUS_CONFIRMED;
+import static org.sonar.api.issue.Issue.STATUS_OPEN;
+import static org.sonar.api.issue.Issue.STATUS_REOPENED;
+import static org.sonar.api.measures.CoreMetrics.BLOCKER_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.CONFIRMED_ISSUES_KEY;
+import static org.sonar.api.measures.CoreMetrics.CRITICAL_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.FALSE_POSITIVE_ISSUES_KEY;
+import static org.sonar.api.measures.CoreMetrics.INFO_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.MAJOR_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.MINOR_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_BLOCKER_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_CRITICAL_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_INFO_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_MAJOR_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_MINOR_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.OPEN_ISSUES_KEY;
+import static org.sonar.api.measures.CoreMetrics.REOPENED_ISSUES_KEY;
+import static org.sonar.api.measures.CoreMetrics.VIOLATIONS_KEY;
+import static org.sonar.api.rule.Severity.BLOCKER;
+import static org.sonar.api.rule.Severity.CRITICAL;
+import static org.sonar.api.rule.Severity.INFO;
+import static org.sonar.api.rule.Severity.MAJOR;
+import static org.sonar.api.rule.Severity.MINOR;
+
+/**
+ * For each component, computes the measures related to number of issues:
+ * <ul>
+ * <li>unresolved issues</li>
+ * <li>false-positives</li>
+ * <li>open issues</li>
+ * <li>issues per status (open, reopen, confirmed)</li>
+ * <li>issues per severity (from info to blocker)</li>
+ * </ul>
+ * For each value, the variation on configured periods is also computed.
+ */
+public class CountIssuesListener extends IssueListener {
+
+ private final static Map<String, String> SEVERITY_TO_METRIC_KEY = ImmutableMap.of(
+ BLOCKER, BLOCKER_VIOLATIONS_KEY,
+ CRITICAL, CRITICAL_VIOLATIONS_KEY,
+ MAJOR, MAJOR_VIOLATIONS_KEY,
+ MINOR, MINOR_VIOLATIONS_KEY,
+ INFO, INFO_VIOLATIONS_KEY
+ );
+
+ private final static Map<String, String> SEVERITY_TO_NEW_METRIC_KEY = ImmutableMap.of(
+ BLOCKER, NEW_BLOCKER_VIOLATIONS_KEY,
+ CRITICAL, NEW_CRITICAL_VIOLATIONS_KEY,
+ MAJOR, NEW_MAJOR_VIOLATIONS_KEY,
+ MINOR, NEW_MINOR_VIOLATIONS_KEY,
+ INFO, NEW_INFO_VIOLATIONS_KEY
+ );
+
+ private final PeriodsHolder periodsHolder;
+ private final MetricRepository metricRepository;
+ private final MeasureRepository measureRepository;
+
+ private final Map<Integer, Counters> countersByComponentRef = new HashMap<>();
+ private Counters currentCounters;
+
+ public CountIssuesListener(PeriodsHolder periodsHolder,
+ MetricRepository metricRepository, MeasureRepository measureRepository) {
+ this.periodsHolder = periodsHolder;
+ this.metricRepository = metricRepository;
+ this.measureRepository = measureRepository;
+ }
+
+ @Override
+ public void beforeComponent(Component component, Tracking tracking) {
+ // TODO optimization no need to instantiate counter if no open issues
+ currentCounters = new Counters();
+ countersByComponentRef.put(component.getRef(), currentCounters);
+ }
+
+ @Override
+ public void onIssue(Component component, DefaultIssue issue) {
+ currentCounters.add(issue);
+ for (Period period : periodsHolder.getPeriods()) {
+ // Add one second to not take into account issues created during current analysis
+ if (issue.creationDate().getTime() >= period.getSnapshotDate() + 1000L) {
+ currentCounters.addOnPeriod(issue, period.getIndex());
+ }
+ }
+ }
+
+ @Override
+ public void afterComponent(Component component) {
+ // aggregate children counters
+ for (Component child : component.getChildren()) {
+ // no need to keep the children in memory. They can be garbage-collected.
+ Counters childCounters = countersByComponentRef.remove(child.getRef());
+ currentCounters.add(childCounters);
+ }
+
+ addMeasuresByStatus(component);
+ addMeasuresBySeverity(component);
+ addMeasuresByPeriod(component);
+ }
+
+ private void addMeasuresBySeverity(Component component) {
+ for (Map.Entry<String, String> entry : SEVERITY_TO_METRIC_KEY.entrySet()) {
+ String severity = entry.getKey();
+ String metricKey = entry.getValue();
+ addMeasure(component, metricKey, currentCounters.counter().severityBag.count(severity));
+ }
+ }
+
+ private void addMeasuresByStatus(Component component) {
+ addMeasure(component, VIOLATIONS_KEY, currentCounters.counter().unresolved);
+ addMeasure(component, OPEN_ISSUES_KEY, currentCounters.counter().open);
+ addMeasure(component, REOPENED_ISSUES_KEY, currentCounters.counter().reopened);
+ addMeasure(component, CONFIRMED_ISSUES_KEY, currentCounters.counter().confirmed);
+ addMeasure(component, FALSE_POSITIVE_ISSUES_KEY, currentCounters.counter().falsePositives);
+ }
+
+ private void addMeasure(Component component, String metricKey, int value) {
+ Metric metric = metricRepository.getByKey(metricKey);
+ measureRepository.add(component, metric, Measure.newMeasureBuilder().create(value));
+ }
+
+ private void addMeasuresByPeriod(Component component) {
+ if (!periodsHolder.getPeriods().isEmpty()) {
+ Double[] unresolvedVariations = new Double[PeriodsHolder.MAX_NUMBER_OF_PERIODS];
+ for (Period period : periodsHolder.getPeriods()) {
+ unresolvedVariations[period.getIndex() - 1] = new Double(currentCounters.counterForPeriod(period.getIndex()).unresolved);
+ }
+ measureRepository.add(component, metricRepository.getByKey(NEW_VIOLATIONS_KEY), Measure.newMeasureBuilder()
+ .setVariations(new MeasureVariations(unresolvedVariations))
+ .createNoValue());
+
+ for (Map.Entry<String, String> entry : SEVERITY_TO_NEW_METRIC_KEY.entrySet()) {
+ String severity = entry.getKey();
+ String metricKey = entry.getValue();
+ Double[] variations = new Double[PeriodsHolder.MAX_NUMBER_OF_PERIODS];
+ boolean set = false;
+ for (Period period : periodsHolder.getPeriods()) {
+ Multiset<String> bag = currentCounters.counterForPeriod(period.getIndex()).severityBag;
+ if (bag.contains(severity)) {
+ variations[period.getIndex() - 1] = new Double(bag.count(severity));
+ set = true;
+ }
+ }
+ if (set) {
+ Metric metric = metricRepository.getByKey(metricKey);
+ measureRepository.add(component, metric, Measure.newMeasureBuilder()
+ .setVariations(new MeasureVariations(variations))
+ .createNoValue());
+ }
+ }
+ }
+ }
+
+ /**
+ * Count issues by status, resolutions, rules and severities
+ */
+ private static class Counter {
+ private int unresolved = 0;
+ private int open = 0;
+ private int reopened = 0;
+ private int confirmed = 0;
+ private int falsePositives = 0;
+ private Multiset<String> severityBag = HashMultiset.create();
+
+ void add(Counter counter) {
+ unresolved += counter.unresolved;
+ open += counter.open;
+ reopened += counter.reopened;
+ confirmed += counter.confirmed;
+ falsePositives += counter.falsePositives;
+ severityBag.addAll(counter.severityBag);
+ }
+
+ void add(Issue issue) {
+ if (issue.resolution() == null) {
+ unresolved++;
+ severityBag.add(issue.severity());
+ } else if (Issue.RESOLUTION_FALSE_POSITIVE.equals(issue.resolution())) {
+ falsePositives++;
+ }
+ switch (issue.status()) {
+ case STATUS_OPEN:
+ open++;
+ break;
+ case STATUS_REOPENED:
+ reopened++;
+ break;
+ case STATUS_CONFIRMED:
+ confirmed++;
+ break;
+ default:
+ // Other statuses are ignored
+ }
+ }
+ }
+
+ /**
+ * List of {@link Counter} for regular value and periods.
+ */
+ private static class Counters {
+ private final Counter[] array = new Counter[1 + PeriodsHolder.MAX_NUMBER_OF_PERIODS];
+
+ Counters() {
+ array[0] = new Counter();
+ for (int i = 1; i <= PeriodsHolder.MAX_NUMBER_OF_PERIODS; i++) {
+ array[i] = new Counter();
+ }
+ }
+
+ void add(@Nullable Counters other) {
+ if (other != null) {
+ for (int i = 0; i < array.length; i++) {
+ array[i].add(other.array[i]);
+ }
+ }
+ }
+
+ void addOnPeriod(Issue issue, int periodIndex) {
+ array[periodIndex].add(issue);
+ }
+
+ void add(Issue issue) {
+ array[0].add(issue);
+ }
+
+ Counter counter() {
+ return array[0];
+ }
+
+ Counter counterForPeriod(int periodIndex) {
+ return array[periodIndex];
+ }
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/DebtCalculator.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/DebtCalculator.java
index 8dbd6aaa96e..afe30d47c87 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/DebtCalculator.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/DebtCalculator.java
@@ -25,7 +25,7 @@ import org.sonar.server.computation.component.Component;
public class DebtCalculator extends IssueListener {
@Override
- public void beforeIssue(Component component, DefaultIssue issue) {
+ public void onOpenIssueInitialization(Component component, DefaultIssue issue) {
if (issue.resolution() == null) {
// TODO
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueListener.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueListener.java
index 8ad03714432..ebd3d4eb098 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueListener.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueListener.java
@@ -25,11 +25,23 @@ import org.sonar.server.computation.component.Component;
public abstract class IssueListener {
+ /**
+ * This method is called for each component before processing its issues.
+ * The component does not necessarily have issues.
+ */
public void beforeComponent(Component component, Tracking tracking) {
}
- public void beforeIssue(Component component, DefaultIssue issue) {
+ /**
+ * This method is called when initializing an open issue. At that time
+ * any information related to tracking step are not available (line, assignee,
+ * resolution, status, creation date, uuid, ...).
+ * <p/>
+ * The need for this method is for example to calculate the issue debt
+ * before merging with base issue
+ */
+ public void onOpenIssueInitialization(Component component, DefaultIssue issue) {
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueListeners.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueListeners.java
index 9da6c8d4c97..7c92c0e4ad3 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueListeners.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueListeners.java
@@ -37,9 +37,9 @@ public class IssueListeners {
}
}
- public void beforeIssue(Component component, DefaultIssue issue) {
+ public void onOpenIssueInitialization(Component component, DefaultIssue issue) {
for (IssueListener listener : listeners) {
- listener.beforeIssue(component, issue);
+ listener.onOpenIssueInitialization(component, issue);
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/period/PeriodsHolder.java b/server/sonar-server/src/main/java/org/sonar/server/computation/period/PeriodsHolder.java
index 706d6d86fe7..f901194dce4 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/period/PeriodsHolder.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/period/PeriodsHolder.java
@@ -32,6 +32,8 @@ import org.sonar.api.CoreProperties;
*/
public interface PeriodsHolder {
+ int MAX_NUMBER_OF_PERIODS = 5;
+
/**
* Return the list of differential periods, ordered by increasing index.
*
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/period/PeriodsHolderImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/period/PeriodsHolderImpl.java
index 1d6346ad0c1..f671559674f 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/period/PeriodsHolderImpl.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/period/PeriodsHolderImpl.java
@@ -50,10 +50,10 @@ public class PeriodsHolderImpl implements PeriodsHolder {
*/
public void setPeriods(Iterable<Period> periods) {
requireNonNull(periods, "Periods cannot be null");
- checkArgument(Iterables.size(periods) <= 5, "There can not be more than 5 periods");
- checkState(this.periods == null, "Periods have already been initialized");
+ checkArgument(Iterables.size(periods) <= MAX_NUMBER_OF_PERIODS, String.format("There can not be more than %d periods", MAX_NUMBER_OF_PERIODS));
+ checkState(this.periods == null, "Periods have already been initialized");
- Period[] newPeriods = new Period[5];
+ Period[] newPeriods = new Period[MAX_NUMBER_OF_PERIODS];
for (Period period : from(periods).filter(CheckNotNull.INSTANCE)) {
int arrayIndex = period.getIndex() - 1;
checkArgument(newPeriods[arrayIndex] == null, "More than one period has the index " + period.getIndex());
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 003d8de45b2..49643fa299a 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
@@ -24,7 +24,6 @@ import com.google.common.collect.Iterables;
import java.util.Arrays;
import java.util.List;
import org.sonar.server.computation.container.ComputeEngineContainer;
-import org.sonar.server.computation.issue.IntegrateIssuesStep;
/**
* Ordered list of steps to be executed
@@ -48,14 +47,14 @@ public class ComputationSteps {
FeedDebtModelStep.class,
// load project related stuffs
- IntegrateIssuesStep.class,
QualityGateLoadingStep.class,
FeedPeriodsStep.class,
// data computation
- ComputeFormulaMeasuresStep.class,
- CustomMeasuresCopyStep.class,
+ IntegrateIssuesStep.class,
ComputeIssueMeasuresStep.class,
+ CustomMeasuresCopyStep.class,
+ ComputeFormulaMeasuresStep.class,
SqaleMeasuresStep.class,
NewCoverageMeasuresStep.class,
NewCoverageAggregationStep.class,
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputeIssueMeasuresStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputeIssueMeasuresStep.java
deleted file mode 100644
index f7060ab065b..00000000000
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputeIssueMeasuresStep.java
+++ /dev/null
@@ -1,341 +0,0 @@
-/*
- * 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 com.google.common.base.Optional;
-import com.google.common.base.Predicate;
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.HashMultiset;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.Multiset;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-import org.sonar.api.rule.Severity;
-import org.sonar.batch.protocol.output.BatchReport.Issue;
-import org.sonar.server.computation.batch.BatchReportReader;
-import org.sonar.server.computation.component.Component;
-import org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor;
-import org.sonar.server.computation.component.TreeRootHolder;
-import org.sonar.server.computation.measure.Measure;
-import org.sonar.server.computation.measure.MeasureRepository;
-import org.sonar.server.computation.measure.MeasureVariations;
-import org.sonar.server.computation.metric.Metric;
-import org.sonar.server.computation.metric.MetricRepository;
-import org.sonar.server.computation.period.Period;
-import org.sonar.server.computation.period.PeriodsHolder;
-
-import static com.google.common.collect.FluentIterable.from;
-import static org.sonar.api.issue.Issue.RESOLUTION_FALSE_POSITIVE;
-import static org.sonar.api.issue.Issue.STATUS_CONFIRMED;
-import static org.sonar.api.issue.Issue.STATUS_OPEN;
-import static org.sonar.api.issue.Issue.STATUS_REOPENED;
-import static org.sonar.api.measures.CoreMetrics.BLOCKER_VIOLATIONS_KEY;
-import static org.sonar.api.measures.CoreMetrics.CONFIRMED_ISSUES_KEY;
-import static org.sonar.api.measures.CoreMetrics.CRITICAL_VIOLATIONS_KEY;
-import static org.sonar.api.measures.CoreMetrics.FALSE_POSITIVE_ISSUES_KEY;
-import static org.sonar.api.measures.CoreMetrics.INFO_VIOLATIONS_KEY;
-import static org.sonar.api.measures.CoreMetrics.MAJOR_VIOLATIONS_KEY;
-import static org.sonar.api.measures.CoreMetrics.MINOR_VIOLATIONS_KEY;
-import static org.sonar.api.measures.CoreMetrics.NEW_BLOCKER_VIOLATIONS_KEY;
-import static org.sonar.api.measures.CoreMetrics.NEW_CRITICAL_VIOLATIONS_KEY;
-import static org.sonar.api.measures.CoreMetrics.NEW_INFO_VIOLATIONS_KEY;
-import static org.sonar.api.measures.CoreMetrics.NEW_MAJOR_VIOLATIONS_KEY;
-import static org.sonar.api.measures.CoreMetrics.NEW_MINOR_VIOLATIONS_KEY;
-import static org.sonar.api.measures.CoreMetrics.NEW_VIOLATIONS_KEY;
-import static org.sonar.api.measures.CoreMetrics.OPEN_ISSUES_KEY;
-import static org.sonar.api.measures.CoreMetrics.REOPENED_ISSUES_KEY;
-import static org.sonar.api.measures.CoreMetrics.VIOLATIONS_KEY;
-import static org.sonar.server.computation.component.Component.Type.FILE;
-import static org.sonar.server.computation.component.ComponentVisitor.Order.POST_ORDER;
-
-/**
- * Computes metrics related to number of issues.
- * - Total number of issues and new issues
- * - Number of issues by severity, and new issues by severity
- * - Number of false-positives
- */
-public class ComputeIssueMeasuresStep implements ComputationStep {
-
- private final BatchReportReader reportReader;
- private final TreeRootHolder treeRootHolder;
- private final PeriodsHolder periodsHolder;
- private final MeasureRepository measureRepository;
- private final MetricRepository metricRepository;
-
- private final static Map<String, String> SEVERITY_METRIC_KEY_BY_SEVERITY = ImmutableMap.of(
- Severity.BLOCKER, BLOCKER_VIOLATIONS_KEY,
- Severity.CRITICAL, CRITICAL_VIOLATIONS_KEY,
- Severity.MAJOR, MAJOR_VIOLATIONS_KEY,
- Severity.MINOR, MINOR_VIOLATIONS_KEY,
- Severity.INFO, INFO_VIOLATIONS_KEY
- );
-
- private final static Map<String, String> NEW_SEVERITY_METRIC_KEY_BY_SEVERITY = ImmutableMap.of(
- Severity.BLOCKER, NEW_BLOCKER_VIOLATIONS_KEY,
- Severity.CRITICAL, NEW_CRITICAL_VIOLATIONS_KEY,
- Severity.MAJOR, NEW_MAJOR_VIOLATIONS_KEY,
- Severity.MINOR, NEW_MINOR_VIOLATIONS_KEY,
- Severity.INFO, NEW_INFO_VIOLATIONS_KEY
- );
-
- public ComputeIssueMeasuresStep(PeriodsHolder periodsHolder, BatchReportReader reportReader, TreeRootHolder treeRootHolder, MeasureRepository measureRepository,
- MetricRepository metricRepository) {
- this.periodsHolder = periodsHolder;
- this.reportReader = reportReader;
- this.treeRootHolder = treeRootHolder;
- this.measureRepository = measureRepository;
- this.metricRepository = metricRepository;
- }
-
- @Override
- public void execute() {
- new DepthTraversalTypeAwareVisitor(FILE, POST_ORDER) {
- @Override
- public void visitAny(Component component) {
- List<Issue> issues = reportReader.readComponentIssues(component.getRef());
- List<Issue> unresolvedIssues = from(issues)
- .filter(UnresolvedIssue.INSTANCE)
- .toList();
- CountIssues countIssues = new CountIssues(unresolvedIssues);
- addIssuesMeasures(component, unresolvedIssues);
- addIssuesStatusMeasures(component, countIssues);
- addIssuesSeverityMeasures(component, countIssues);
- addFalsePositiveMeasures(component, issues);
- }
- }.visit(treeRootHolder.getRoot());
- }
-
- private void addIssuesMeasures(Component component, List<Issue> unresolvedIssues) {
- addMeasure(component, VIOLATIONS_KEY, unresolvedIssues.size());
- addNewMeasures(component, NEW_VIOLATIONS_KEY, unresolvedIssues);
- }
-
- private void addIssuesStatusMeasures(Component component, CountIssues countIssues) {
- addMeasure(component, OPEN_ISSUES_KEY, countIssues.openIssues);
- addMeasure(component, REOPENED_ISSUES_KEY, countIssues.reopenedIssues);
- addMeasure(component, CONFIRMED_ISSUES_KEY, countIssues.confirmedIssues);
- }
-
- private void addIssuesSeverityMeasures(Component component, CountIssues countIssues) {
- for (Map.Entry<String, String> entry : SEVERITY_METRIC_KEY_BY_SEVERITY.entrySet()) {
- String severity = entry.getKey();
- String metricKey = entry.getValue();
- addMeasure(component, metricKey, countIssues.severityBag.count(severity));
- }
- for (Map.Entry<String, String> entry : NEW_SEVERITY_METRIC_KEY_BY_SEVERITY.entrySet()) {
- String severity = entry.getKey();
- String metricKey = entry.getValue();
- addNewMeasures(component, metricKey, countIssues.issuesPerSeverity.get(severity));
- }
- }
-
- private void addFalsePositiveMeasures(Component component, List<Issue> issues) {
- addMeasure(component, FALSE_POSITIVE_ISSUES_KEY, from(issues).filter(FalsePositiveIssue.INSTANCE).size());
- }
-
- private void addNewMeasures(Component component, String metricKey, List<Issue> issues) {
- if (periodsHolder.getPeriods().isEmpty()) {
- return;
- }
- Metric metric = metricRepository.getByKey(metricKey);
- Double[] periodValues = new Double[]{null, null, null, null, null};
- for (Period period : periodsHolder.getPeriods()) {
- Collection<Measure> childrenMeasures = getChildrenMeasures(component, metric);
- double periodValue = sumIssuesOnPeriod(issues, period.getSnapshotDate()) + sumChildrenMeasuresOnPeriod(childrenMeasures, period.getIndex());
- periodValues[period.getIndex() - 1] = periodValue;
- }
- measureRepository.add(component, metric, Measure.newMeasureBuilder()
- .setVariations(new MeasureVariations(periodValues))
- .createNoValue());
- }
-
- private void addMeasure(Component component, String metricKey, int value) {
- Metric metric = metricRepository.getByKey(metricKey);
- int totalValue = value + sumChildrenMeasures(getChildrenMeasures(component, metric));
- measureRepository.add(component, metric, Measure.newMeasureBuilder().create(totalValue, null));
- }
-
- private Collection<Measure> getChildrenMeasures(Component component, final Metric metric) {
- return from(component.getChildren()).transform(new ComponentChildrenMeasures(metric)).toList();
- }
-
- private static int sumChildrenMeasures(Collection<Measure> measures) {
- SumMeasure sumMeasures = new SumMeasure();
- from(measures).filter(sumMeasures).size();
- return sumMeasures.getSum();
- }
-
- private static double sumChildrenMeasuresOnPeriod(Collection<Measure> measures, int periodIndex) {
- SumVariationMeasure sumMeasures = new SumVariationMeasure(periodIndex);
- from(measures).filter(sumMeasures).size();
- return sumMeasures.getSum();
- }
-
- private static int sumIssuesOnPeriod(Collection<Issue> issues, long periodDate) {
- // Add one second to not take into account issues created during current analysis
- long datePlusOneSecond = periodDate + 1000L;
- SumIssueAfterDate sumIssues = new SumIssueAfterDate(datePlusOneSecond);
- from(issues).filter(sumIssues).toList();
- return sumIssues.getSum();
- }
-
- private static class CountIssues {
- int openIssues = 0;
- int reopenedIssues = 0;
- int confirmedIssues = 0;
- Multiset<String> severityBag = HashMultiset.create();
- ListMultimap<String, Issue> issuesPerSeverity = ArrayListMultimap.create();
-
- public CountIssues(Iterable<Issue> issues) {
- for (Issue issue : issues) {
- countByStatus(issue.getStatus());
- severityBag.add(issue.getSeverity().name());
- issuesPerSeverity.put(issue.getSeverity().name(), issue);
- }
- }
-
- private void countByStatus(String status) {
- switch (status) {
- case STATUS_OPEN:
- openIssues++;
- break;
- case STATUS_REOPENED:
- reopenedIssues++;
- break;
- case STATUS_CONFIRMED:
- confirmedIssues++;
- break;
- default:
- // Other statuses are ignored
- }
- }
- }
-
- private static class SumMeasure implements Predicate<Measure> {
-
- private int sum = 0;
-
- @Override
- public boolean apply(@Nonnull Measure input) {
- sum += input.getIntValue();
- return true;
- }
-
- public int getSum() {
- return sum;
- }
- }
-
- private static class SumVariationMeasure implements Predicate<Measure> {
-
- private final int periodIndex;
- private double sum = 0d;
-
- public SumVariationMeasure(int periodIndex) {
- this.periodIndex = periodIndex;
- }
-
- @Override
- public boolean apply(@Nonnull Measure input) {
- if (input.hasVariations() && input.getVariations().hasVariation(periodIndex)) {
- sum += input.getVariations().getVariation(periodIndex);
- }
- return true;
- }
-
- public double getSum() {
- return sum;
- }
- }
-
- private static class SumIssueAfterDate implements Predicate<Issue> {
-
- private final long date;
- private int sum = 0;
-
- public SumIssueAfterDate(long date) {
- this.date = date;
- }
-
- @Override
- public boolean apply(@Nonnull Issue issue) {
- if (isAfter(issue, date)) {
- sum++;
- }
- return true;
- }
-
- public int getSum() {
- return sum;
- }
-
- private static boolean isAfter(Issue issue, long date) {
- // TODO should we truncate the date to the second as it was done in batch ?
- return issue.getCreationDate() > date;
- }
- }
-
- private enum FalsePositiveIssue implements Predicate<Issue> {
- INSTANCE;
-
- @Override
- public boolean apply(@Nonnull Issue issue) {
- return issue.hasResolution() && issue.getResolution().equals(RESOLUTION_FALSE_POSITIVE);
- }
- }
-
- private enum UnresolvedIssue implements Predicate<Issue> {
- INSTANCE;
-
- @Override
- public boolean apply(@Nonnull Issue issue) {
- return !issue.hasResolution();
- }
- }
-
- private class ComponentChildrenMeasures implements Function<Component, Measure> {
- private final Metric metric;
-
- public ComponentChildrenMeasures(Metric metric) {
- this.metric = metric;
- }
-
- @Nullable
- @Override
- public Measure apply(@Nonnull Component input) {
- Optional<Measure> childMeasure = measureRepository.getRawMeasure(input, metric);
- if (childMeasure.isPresent()) {
- return childMeasure.get();
- }
- return null;
- }
- }
-
- @Override
- public String getDescription() {
- return "Compute measures on issues";
- }
-}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IntegrateIssuesStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/IntegrateIssuesStep.java
index da507bb7ad2..fed2f288295 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IntegrateIssuesStep.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/IntegrateIssuesStep.java
@@ -17,7 +17,7 @@
* 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;
+package org.sonar.server.computation.step;
import com.google.common.collect.Sets;
import java.util.List;
@@ -28,7 +28,11 @@ import org.sonar.core.issue.tracking.Tracking;
import org.sonar.server.computation.component.Component;
import org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor;
import org.sonar.server.computation.component.TreeRootHolder;
-import org.sonar.server.computation.step.ComputationStep;
+import org.sonar.server.computation.issue.BaseIssuesLoader;
+import org.sonar.server.computation.issue.IssueCache;
+import org.sonar.server.computation.issue.IssueLifecycle;
+import org.sonar.server.computation.issue.IssueListeners;
+import org.sonar.server.computation.issue.TrackerExecution;
import org.sonar.server.util.cache.DiskCache;
import static org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor.Order.POST_ORDER;
@@ -87,7 +91,7 @@ public class IntegrateIssuesStep implements ComputationStep {
Set<DefaultIssue> issues = tracking.getUnmatchedRaws();
for (DefaultIssue issue : issues) {
issueLifecycle.initNewOpenIssue(issue);
- issueListeners.beforeIssue(component, issue);
+ issueListeners.onOpenIssueInitialization(component, issue);
process(component, issue, cacheAppender);
}
}
@@ -96,7 +100,7 @@ public class IntegrateIssuesStep implements ComputationStep {
for (Map.Entry<DefaultIssue, DefaultIssue> entry : tracking.getMatchedRaws().entrySet()) {
DefaultIssue raw = entry.getKey();
DefaultIssue base = entry.getValue();
- issueListeners.beforeIssue(component, raw);
+ issueListeners.onOpenIssueInitialization(component, raw);
issueLifecycle.mergeExistingOpenIssue(raw, base);
process(component, raw, cacheAppender);
}
@@ -104,7 +108,7 @@ public class IntegrateIssuesStep implements ComputationStep {
int line = entry.getKey();
DefaultIssue manualIssue = entry.getValue();
manualIssue.setLine(line == 0 ? null : line);
- issueListeners.beforeIssue(component, manualIssue);
+ issueListeners.onOpenIssueInitialization(component, manualIssue);
process(component, manualIssue, cacheAppender);
}
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/CountIssuesListenerTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/CountIssuesListenerTest.java
new file mode 100644
index 00000000000..56b65390a89
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/CountIssuesListenerTest.java
@@ -0,0 +1,267 @@
+/*
+ * 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 java.util.Date;
+import javax.annotation.Nullable;
+import org.assertj.core.data.Offset;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.tracking.Tracking;
+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.measure.MeasureRepository;
+import org.sonar.server.computation.measure.MeasureRepositoryImpl;
+import org.sonar.server.computation.measure.MeasureVariations;
+import org.sonar.server.computation.metric.Metric;
+import org.sonar.server.computation.metric.MetricImpl;
+import org.sonar.server.computation.metric.MetricRepository;
+import org.sonar.server.computation.period.Period;
+import org.sonar.server.computation.period.PeriodsHolderRule;
+import org.sonar.server.rule.RuleTesting;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.api.issue.Issue.RESOLUTION_FALSE_POSITIVE;
+import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
+import static org.sonar.api.issue.Issue.STATUS_CLOSED;
+import static org.sonar.api.issue.Issue.STATUS_CONFIRMED;
+import static org.sonar.api.issue.Issue.STATUS_OPEN;
+import static org.sonar.api.issue.Issue.STATUS_RESOLVED;
+import static org.sonar.api.measures.CoreMetrics.BLOCKER_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.CONFIRMED_ISSUES_KEY;
+import static org.sonar.api.measures.CoreMetrics.CRITICAL_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.FALSE_POSITIVE_ISSUES_KEY;
+import static org.sonar.api.measures.CoreMetrics.INFO_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.MAJOR_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.MINOR_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_BLOCKER_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_CRITICAL_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_INFO_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_MAJOR_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_MINOR_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.OPEN_ISSUES_KEY;
+import static org.sonar.api.measures.CoreMetrics.REOPENED_ISSUES_KEY;
+import static org.sonar.api.measures.CoreMetrics.VIOLATIONS_KEY;
+import static org.sonar.api.rule.Severity.BLOCKER;
+import static org.sonar.api.rule.Severity.CRITICAL;
+import static org.sonar.api.rule.Severity.MAJOR;
+import static org.sonar.server.computation.component.DumbComponent.builder;
+import static org.sonar.server.computation.metric.Metric.MetricType.INT;
+
+public class CountIssuesListenerTest {
+
+ static final Component FILE1 = builder(Component.Type.FILE, 1).build();
+ static final Component FILE2 = builder(Component.Type.FILE, 2).build();
+ static final Component FILE3 = builder(Component.Type.FILE, 3).build();
+ static final Component PROJECT = builder(Component.Type.PROJECT, 4).addChildren(FILE1, FILE2, FILE3).build();
+
+ static final Metric ISSUES_METRIC = new MetricImpl(1, VIOLATIONS_KEY, VIOLATIONS_KEY, INT);
+ static final Metric OPEN_ISSUES_METRIC = new MetricImpl(2, OPEN_ISSUES_KEY, OPEN_ISSUES_KEY, INT);
+ static final Metric REOPENED_ISSUES_METRIC = new MetricImpl(3, REOPENED_ISSUES_KEY, REOPENED_ISSUES_KEY, INT);
+ static final Metric CONFIRMED_ISSUES_METRIC = new MetricImpl(4, CONFIRMED_ISSUES_KEY, CONFIRMED_ISSUES_KEY, INT);
+ static final Metric BLOCKER_ISSUES_METRIC = new MetricImpl(5, BLOCKER_VIOLATIONS_KEY, BLOCKER_VIOLATIONS_KEY, INT);
+ static final Metric CRITICAL_ISSUES_METRIC = new MetricImpl(6, CRITICAL_VIOLATIONS_KEY, CRITICAL_VIOLATIONS_KEY, INT);
+ static final Metric MAJOR_ISSUES_METRIC = new MetricImpl(7, MAJOR_VIOLATIONS_KEY, MAJOR_VIOLATIONS_KEY, INT);
+ static final Metric MINOR_ISSUES_METRIC = new MetricImpl(8, MINOR_VIOLATIONS_KEY, MINOR_VIOLATIONS_KEY, INT);
+ static final Metric INFO_ISSUES_METRIC = new MetricImpl(9, INFO_VIOLATIONS_KEY, INFO_VIOLATIONS_KEY, INT);
+ static final Metric NEW_ISSUES_METRIC = new MetricImpl(10, NEW_VIOLATIONS_KEY, NEW_VIOLATIONS_KEY, INT);
+ static final Metric NEW_BLOCKER_ISSUES_METRIC = new MetricImpl(11, NEW_BLOCKER_VIOLATIONS_KEY, NEW_BLOCKER_VIOLATIONS_KEY, INT);
+ static final Metric NEW_CRITICAL_ISSUES_METRIC = new MetricImpl(12, NEW_CRITICAL_VIOLATIONS_KEY, NEW_CRITICAL_VIOLATIONS_KEY, INT);
+ static final Metric NEW_MAJOR_ISSUES_METRIC = new MetricImpl(13, NEW_MAJOR_VIOLATIONS_KEY, NEW_MAJOR_VIOLATIONS_KEY, INT);
+ static final Metric NEW_MINOR_ISSUES_METRIC = new MetricImpl(14, NEW_MINOR_VIOLATIONS_KEY, NEW_MINOR_VIOLATIONS_KEY, INT);
+ static final Metric NEW_INFO_ISSUES_METRIC = new MetricImpl(15, NEW_INFO_VIOLATIONS_KEY, NEW_INFO_VIOLATIONS_KEY, INT);
+ static final Metric FALSE_POSITIVE_ISSUES_METRIC = new MetricImpl(16, FALSE_POSITIVE_ISSUES_KEY, FALSE_POSITIVE_ISSUES_KEY, INT);
+
+ @Rule
+ public BatchReportReaderRule reportReader = new BatchReportReaderRule();
+
+ @Rule
+ public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
+
+ @Rule
+ public PeriodsHolderRule periodsHolder = new PeriodsHolderRule();
+
+ Tracking tracking = mock(Tracking.class);
+ MetricRepository metricRepository = mock(MetricRepository.class);
+ MeasureRepository measureRepository;
+ RuleCache ruleCache = mock(RuleCache.class);
+ CountIssuesListener sut;
+
+ @Before
+ public void setUp() throws Exception {
+ initMetrics();
+ measureRepository = new MeasureRepositoryImpl(null, reportReader, metricRepository, ruleCache);
+
+ sut = new CountIssuesListener(periodsHolder, metricRepository, measureRepository);
+ }
+
+ @Test
+ public void count_issues_by_status() throws Exception {
+ periodsHolder.setPeriods();
+
+ // bottom-up traversal -> from files to project
+ sut.beforeComponent(FILE1, tracking);
+ sut.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER));
+ sut.onIssue(FILE1, createIssue(RESOLUTION_FIXED, STATUS_CLOSED, MAJOR));
+ sut.onIssue(FILE1, createIssue(RESOLUTION_FALSE_POSITIVE, STATUS_RESOLVED, MAJOR));
+ sut.afterComponent(FILE1);
+
+ sut.beforeComponent(FILE2, tracking);
+ sut.onIssue(FILE2, createIssue(null, STATUS_CONFIRMED, BLOCKER));
+ sut.onIssue(FILE2, createIssue(null, STATUS_CONFIRMED, MAJOR));
+ sut.afterComponent(FILE2);
+
+ sut.beforeComponent(FILE3, tracking);
+ sut.afterComponent(FILE3);
+
+ sut.beforeComponent(PROJECT, tracking);
+ sut.afterComponent(PROJECT);
+
+ // count by status
+ assertThat(measureRepository.getRawMeasure(FILE1, ISSUES_METRIC).get().getIntValue()).isEqualTo(1);
+ assertThat(measureRepository.getRawMeasure(FILE1, OPEN_ISSUES_METRIC).get().getIntValue()).isEqualTo(1);
+ assertThat(measureRepository.getRawMeasure(FILE1, FALSE_POSITIVE_ISSUES_METRIC).get().getIntValue()).isEqualTo(1);
+ assertThat(measureRepository.getRawMeasure(FILE1, CONFIRMED_ISSUES_METRIC).get().getIntValue()).isEqualTo(0);
+
+ assertThat(measureRepository.getRawMeasure(FILE2, ISSUES_METRIC).get().getIntValue()).isEqualTo(2);
+ assertThat(measureRepository.getRawMeasure(FILE2, OPEN_ISSUES_METRIC).get().getIntValue()).isEqualTo(0);
+ assertThat(measureRepository.getRawMeasure(FILE2, FALSE_POSITIVE_ISSUES_METRIC).get().getIntValue()).isEqualTo(0);
+ assertThat(measureRepository.getRawMeasure(FILE2, CONFIRMED_ISSUES_METRIC).get().getIntValue()).isEqualTo(2);
+
+ assertThat(measureRepository.getRawMeasure(FILE3, ISSUES_METRIC).get().getIntValue()).isEqualTo(0);
+
+ assertThat(measureRepository.getRawMeasure(PROJECT, ISSUES_METRIC).get().getIntValue()).isEqualTo(3);
+ assertThat(measureRepository.getRawMeasure(PROJECT, OPEN_ISSUES_METRIC).get().getIntValue()).isEqualTo(1);
+ assertThat(measureRepository.getRawMeasure(PROJECT, FALSE_POSITIVE_ISSUES_METRIC).get().getIntValue()).isEqualTo(1);
+ assertThat(measureRepository.getRawMeasure(PROJECT, CONFIRMED_ISSUES_METRIC).get().getIntValue()).isEqualTo(2);
+ }
+
+ @Test
+ public void count_unresolved_issues_by_severity() throws Exception {
+ periodsHolder.setPeriods();
+
+ // bottom-up traversal -> from files to project
+ sut.beforeComponent(FILE1, tracking);
+ sut.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER));
+ // this resolved issue is ignored
+ sut.onIssue(FILE1, createIssue(RESOLUTION_FIXED, STATUS_CLOSED, MAJOR));
+ sut.afterComponent(FILE1);
+
+ sut.beforeComponent(FILE2, tracking);
+ sut.onIssue(FILE2, createIssue(null, STATUS_CONFIRMED, BLOCKER));
+ sut.onIssue(FILE2, createIssue(null, STATUS_CONFIRMED, MAJOR));
+ sut.afterComponent(FILE2);
+
+ sut.beforeComponent(PROJECT, tracking);
+ sut.afterComponent(PROJECT);
+
+ assertThat(measureRepository.getRawMeasure(FILE1, BLOCKER_ISSUES_METRIC).get().getIntValue()).isEqualTo(1);
+ assertThat(measureRepository.getRawMeasure(FILE1, CRITICAL_ISSUES_METRIC).get().getIntValue()).isEqualTo(0);
+ assertThat(measureRepository.getRawMeasure(FILE1, MAJOR_ISSUES_METRIC).get().getIntValue()).isEqualTo(0);
+
+ assertThat(measureRepository.getRawMeasure(FILE2, BLOCKER_ISSUES_METRIC).get().getIntValue()).isEqualTo(1);
+ assertThat(measureRepository.getRawMeasure(FILE2, CRITICAL_ISSUES_METRIC).get().getIntValue()).isEqualTo(0);
+ assertThat(measureRepository.getRawMeasure(FILE2, MAJOR_ISSUES_METRIC).get().getIntValue()).isEqualTo(1);
+
+ assertThat(measureRepository.getRawMeasure(PROJECT, BLOCKER_ISSUES_METRIC).get().getIntValue()).isEqualTo(2);
+ assertThat(measureRepository.getRawMeasure(PROJECT, CRITICAL_ISSUES_METRIC).get().getIntValue()).isEqualTo(0);
+ assertThat(measureRepository.getRawMeasure(PROJECT, MAJOR_ISSUES_METRIC).get().getIntValue()).isEqualTo(1);
+ }
+
+ @Test
+ public void count_new_issues() throws Exception {
+ Period period = newPeriod(3, 1500000000000L);
+ periodsHolder.setPeriods(period);
+
+ sut.beforeComponent(FILE1, tracking);
+ // created before -> existing issues
+ sut.onIssue(FILE1, createIssueAt(null, STATUS_OPEN, BLOCKER, period.getSnapshotDate() - 1000000L));
+ // created during the first analysis starting the period -> existing issues
+ sut.onIssue(FILE1, createIssueAt(null, STATUS_OPEN, BLOCKER, period.getSnapshotDate()));
+ // created after -> new issues
+ sut.onIssue(FILE1, createIssueAt(null, STATUS_OPEN, CRITICAL, period.getSnapshotDate() + 100000L));
+ sut.onIssue(FILE1, createIssueAt(RESOLUTION_FIXED, STATUS_CLOSED, MAJOR, period.getSnapshotDate() + 200000L));
+ sut.afterComponent(FILE1);
+
+ sut.beforeComponent(FILE2, tracking);
+ sut.afterComponent(FILE2);
+
+ sut.beforeComponent(PROJECT, tracking);
+ sut.afterComponent(PROJECT);
+
+ assertVariation(FILE1, NEW_ISSUES_METRIC, period.getIndex(), 1);
+ assertVariation(FILE1, NEW_CRITICAL_ISSUES_METRIC, period.getIndex(), 1);
+ assertThat(measureRepository.getRawMeasure(FILE1, NEW_BLOCKER_ISSUES_METRIC).isPresent()).isFalse();
+ assertThat(measureRepository.getRawMeasure(FILE1, NEW_MAJOR_ISSUES_METRIC).isPresent()).isFalse();
+
+ assertVariation(PROJECT, NEW_ISSUES_METRIC, period.getIndex(), 1);
+ assertVariation(PROJECT, NEW_CRITICAL_ISSUES_METRIC, period.getIndex(), 1);
+ assertThat(measureRepository.getRawMeasure(PROJECT, NEW_BLOCKER_ISSUES_METRIC).isPresent()).isFalse();
+ assertThat(measureRepository.getRawMeasure(PROJECT, NEW_MAJOR_ISSUES_METRIC).isPresent()).isFalse();
+ }
+
+ private void assertVariation(Component component, Metric metric, int periodIndex, int expectedVariation) {
+ MeasureVariations variations = measureRepository.getRawMeasure(component, metric).get().getVariations();
+ assertThat(variations.getVariation(periodIndex)).isEqualTo((double) expectedVariation, Offset.offset(0.01));
+ }
+
+ private static DefaultIssue createIssue(@Nullable String resolution, String status, String severity) {
+ return new DefaultIssue()
+ .setResolution(resolution).setStatus(status)
+ .setSeverity(severity).setRuleKey(RuleTesting.XOO_X1)
+ .setCreationDate(new Date());
+ }
+
+ private static DefaultIssue createIssueAt(@Nullable String resolution, String status, String severity, long creationDate) {
+ return new DefaultIssue()
+ .setResolution(resolution).setStatus(status)
+ .setSeverity(severity).setRuleKey(RuleTesting.XOO_X1)
+ .setCreationDate(new Date(creationDate));
+ }
+
+ private static Period newPeriod(int index, long date) {
+ return new Period(index, "mode", null, date, 42l);
+ }
+
+ private void initMetrics() {
+ when(metricRepository.getByKey(ISSUES_METRIC.getKey())).thenReturn(ISSUES_METRIC);
+ when(metricRepository.getByKey(OPEN_ISSUES_METRIC.getKey())).thenReturn(OPEN_ISSUES_METRIC);
+ when(metricRepository.getByKey(REOPENED_ISSUES_METRIC.getKey())).thenReturn(REOPENED_ISSUES_METRIC);
+ when(metricRepository.getByKey(CONFIRMED_ISSUES_METRIC.getKey())).thenReturn(CONFIRMED_ISSUES_METRIC);
+ when(metricRepository.getByKey(BLOCKER_ISSUES_METRIC.getKey())).thenReturn(BLOCKER_ISSUES_METRIC);
+ when(metricRepository.getByKey(CRITICAL_ISSUES_METRIC.getKey())).thenReturn(CRITICAL_ISSUES_METRIC);
+ when(metricRepository.getByKey(MAJOR_ISSUES_METRIC.getKey())).thenReturn(MAJOR_ISSUES_METRIC);
+ when(metricRepository.getByKey(MINOR_ISSUES_METRIC.getKey())).thenReturn(MINOR_ISSUES_METRIC);
+ when(metricRepository.getByKey(INFO_ISSUES_METRIC.getKey())).thenReturn(INFO_ISSUES_METRIC);
+ when(metricRepository.getByKey(NEW_ISSUES_METRIC.getKey())).thenReturn(NEW_ISSUES_METRIC);
+ when(metricRepository.getByKey(NEW_BLOCKER_ISSUES_METRIC.getKey())).thenReturn(NEW_BLOCKER_ISSUES_METRIC);
+ when(metricRepository.getByKey(NEW_CRITICAL_ISSUES_METRIC.getKey())).thenReturn(NEW_CRITICAL_ISSUES_METRIC);
+ when(metricRepository.getByKey(NEW_MAJOR_ISSUES_METRIC.getKey())).thenReturn(NEW_MAJOR_ISSUES_METRIC);
+ when(metricRepository.getByKey(NEW_MINOR_ISSUES_METRIC.getKey())).thenReturn(NEW_MINOR_ISSUES_METRIC);
+ when(metricRepository.getByKey(NEW_INFO_ISSUES_METRIC.getKey())).thenReturn(NEW_INFO_ISSUES_METRIC);
+ when(metricRepository.getByKey(FALSE_POSITIVE_ISSUES_METRIC.getKey())).thenReturn(FALSE_POSITIVE_ISSUES_METRIC);
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputeIssueMeasuresStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputeIssueMeasuresStepTest.java
deleted file mode 100644
index 3e09534b789..00000000000
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputeIssueMeasuresStepTest.java
+++ /dev/null
@@ -1,412 +0,0 @@
-/*
- * 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 java.util.Arrays;
-import javax.annotation.Nullable;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.rule.RuleKey;
-import org.sonar.api.utils.internal.Uuids;
-import org.sonar.batch.protocol.Constants;
-import org.sonar.batch.protocol.output.BatchReport;
-import org.sonar.batch.protocol.output.BatchReport.Issue;
-import org.sonar.core.rule.RuleDto;
-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.issue.RuleCache;
-import org.sonar.server.computation.measure.MeasureRepository;
-import org.sonar.server.computation.measure.MeasureRepositoryImpl;
-import org.sonar.server.computation.metric.Metric;
-import org.sonar.server.computation.metric.MetricImpl;
-import org.sonar.server.computation.metric.MetricRepository;
-import org.sonar.server.computation.period.Period;
-import org.sonar.server.computation.period.PeriodsHolderRule;
-import org.sonar.server.rule.RuleTesting;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.sonar.api.issue.Issue.RESOLUTION_FALSE_POSITIVE;
-import static org.sonar.api.issue.Issue.STATUS_CLOSED;
-import static org.sonar.api.issue.Issue.STATUS_CONFIRMED;
-import static org.sonar.api.issue.Issue.STATUS_OPEN;
-import static org.sonar.api.issue.Issue.STATUS_REOPENED;
-import static org.sonar.api.issue.Issue.STATUS_RESOLVED;
-import static org.sonar.api.issue.internal.DefaultIssue.RESOLUTION_FIXED;
-import static org.sonar.api.issue.internal.DefaultIssue.RESOLUTION_REMOVED;
-import static org.sonar.api.measures.CoreMetrics.BLOCKER_VIOLATIONS_KEY;
-import static org.sonar.api.measures.CoreMetrics.CONFIRMED_ISSUES_KEY;
-import static org.sonar.api.measures.CoreMetrics.CRITICAL_VIOLATIONS_KEY;
-import static org.sonar.api.measures.CoreMetrics.FALSE_POSITIVE_ISSUES_KEY;
-import static org.sonar.api.measures.CoreMetrics.INFO_VIOLATIONS_KEY;
-import static org.sonar.api.measures.CoreMetrics.MAJOR_VIOLATIONS_KEY;
-import static org.sonar.api.measures.CoreMetrics.MINOR_VIOLATIONS_KEY;
-import static org.sonar.api.measures.CoreMetrics.NEW_BLOCKER_VIOLATIONS_KEY;
-import static org.sonar.api.measures.CoreMetrics.NEW_CRITICAL_VIOLATIONS_KEY;
-import static org.sonar.api.measures.CoreMetrics.NEW_INFO_VIOLATIONS_KEY;
-import static org.sonar.api.measures.CoreMetrics.NEW_MAJOR_VIOLATIONS_KEY;
-import static org.sonar.api.measures.CoreMetrics.NEW_MINOR_VIOLATIONS_KEY;
-import static org.sonar.api.measures.CoreMetrics.NEW_VIOLATIONS_KEY;
-import static org.sonar.api.measures.CoreMetrics.OPEN_ISSUES_KEY;
-import static org.sonar.api.measures.CoreMetrics.REOPENED_ISSUES_KEY;
-import static org.sonar.api.measures.CoreMetrics.VIOLATIONS_KEY;
-import static org.sonar.api.rule.Severity.BLOCKER;
-import static org.sonar.api.rule.Severity.CRITICAL;
-import static org.sonar.api.rule.Severity.INFO;
-import static org.sonar.api.rule.Severity.MAJOR;
-import static org.sonar.api.rule.Severity.MINOR;
-import static org.sonar.server.computation.component.DumbComponent.builder;
-import static org.sonar.server.computation.metric.Metric.MetricType.INT;
-
-public class ComputeIssueMeasuresStepTest {
-
- static final Component FILE = builder(Component.Type.FILE, 2).build();
- static final Component PROJECT = builder(Component.Type.PROJECT, 1).addChildren(FILE).build();
-
- static final Metric ISSUES_METRIC = new MetricImpl(1, VIOLATIONS_KEY, VIOLATIONS_KEY, INT);
- static final Metric OPEN_ISSUES_METRIC = new MetricImpl(2, OPEN_ISSUES_KEY, OPEN_ISSUES_KEY, INT);
- static final Metric REOPENED_ISSUES_METRIC = new MetricImpl(3, REOPENED_ISSUES_KEY, REOPENED_ISSUES_KEY, INT);
- static final Metric CONFIRMED_ISSUES_METRIC = new MetricImpl(4, CONFIRMED_ISSUES_KEY, CONFIRMED_ISSUES_KEY, INT);
- static final Metric BLOCKER_ISSUES_METRIC = new MetricImpl(5, BLOCKER_VIOLATIONS_KEY, BLOCKER_VIOLATIONS_KEY, INT);
- static final Metric CRITICAL_ISSUES_METRIC = new MetricImpl(6, CRITICAL_VIOLATIONS_KEY, CRITICAL_VIOLATIONS_KEY, INT);
- static final Metric MAJOR_ISSUES_METRIC = new MetricImpl(7, MAJOR_VIOLATIONS_KEY, MAJOR_VIOLATIONS_KEY, INT);
- static final Metric MINOR_ISSUES_METRIC = new MetricImpl(8, MINOR_VIOLATIONS_KEY, MINOR_VIOLATIONS_KEY, INT);
- static final Metric INFO_ISSUES_METRIC = new MetricImpl(9, INFO_VIOLATIONS_KEY, INFO_VIOLATIONS_KEY, INT);
- static final Metric NEW_ISSUES_METRIC = new MetricImpl(10, NEW_VIOLATIONS_KEY, NEW_VIOLATIONS_KEY, INT);
- static final Metric NEW_BLOCKER_ISSUES_METRIC = new MetricImpl(11, NEW_BLOCKER_VIOLATIONS_KEY, NEW_BLOCKER_VIOLATIONS_KEY, INT);
- static final Metric NEW_CRITICAL_ISSUES_METRIC = new MetricImpl(12, NEW_CRITICAL_VIOLATIONS_KEY, NEW_CRITICAL_VIOLATIONS_KEY, INT);
- static final Metric NEW_MAJOR_ISSUES_METRIC = new MetricImpl(13, NEW_MAJOR_VIOLATIONS_KEY, NEW_MAJOR_VIOLATIONS_KEY, INT);
- static final Metric NEW_MINOR_ISSUES_METRIC = new MetricImpl(14, NEW_MINOR_VIOLATIONS_KEY, NEW_MINOR_VIOLATIONS_KEY, INT);
- static final Metric NEW_INFO_ISSUES_METRIC = new MetricImpl(15, NEW_INFO_VIOLATIONS_KEY, NEW_INFO_VIOLATIONS_KEY, INT);
- static final Metric FALSE_POSITIVE_ISSUES_METRIC = new MetricImpl(16, FALSE_POSITIVE_ISSUES_KEY, FALSE_POSITIVE_ISSUES_KEY, INT);
-
- static final RuleDto RULE_1 = RuleTesting.newDto(RuleKey.of("xoo", "x1")).setId(1);
- static final RuleDto RULE_2 = RuleTesting.newDto(RuleKey.of("xoo", "x2")).setId(2);
- static final RuleDto RULE_3 = RuleTesting.newDto(RuleKey.of("xoo", "x3")).setId(3);
- static final RuleDto RULE_4 = RuleTesting.newDto(RuleKey.of("xoo", "x4")).setId(4);
- static final RuleDto RULE_5 = RuleTesting.newDto(RuleKey.of("xoo", "x5")).setId(5);
- static final RuleDto RULE_6 = RuleTesting.newDto(RuleKey.of("xoo", "x6")).setId(6);
-
- @Rule
- public BatchReportReaderRule reportReader = new BatchReportReaderRule();
-
- @Rule
- public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
-
- @Rule
- public PeriodsHolderRule periodsHolder = new PeriodsHolderRule();
-
- MetricRepository metricRepository = mock(MetricRepository.class);
- RuleCache ruleCache = mock(RuleCache.class);
- MeasureRepository measureRepository;
-
- ComputeIssueMeasuresStep sut;
-
- @Before
- public void setUp() throws Exception {
- initMetrics();
- measureRepository = new MeasureRepositoryImpl(null, reportReader, metricRepository, ruleCache);
-
- sut = new ComputeIssueMeasuresStep(periodsHolder, reportReader, treeRootHolder, measureRepository, metricRepository);
- }
-
- @Test
- public void compute_total_issues_measure() throws Exception {
- treeRootHolder.setRoot(PROJECT);
- periodsHolder.setPeriods();
- addIssues(FILE.getRef(), createIssue(STATUS_OPEN, BLOCKER, RULE_1.getKey()));
-
- sut.execute();
-
- assertThat(measureRepository.getRawMeasure(PROJECT, ISSUES_METRIC).get().getIntValue()).isEqualTo(1);
- }
-
- @Test
- public void compute_measures_on_all_levels() throws Exception {
- Component file1 = builder(Component.Type.FILE, 5).build();
- Component file2 = builder(Component.Type.FILE, 4).build();
- Component file3 = builder(Component.Type.FILE, 3).build();
- Component directory = builder(Component.Type.DIRECTORY, 2).addChildren(file1, file2, file3).build();
- Component project = builder(Component.Type.PROJECT, 1).addChildren(directory).build();
- treeRootHolder.setRoot(project);
- periodsHolder.setPeriods();
-
- addIssues(file1.getRef(), createIssue(STATUS_OPEN, BLOCKER, RULE_1.getKey()));
- addIssues(file2.getRef(), createIssue(STATUS_REOPENED, CRITICAL, RULE_2.getKey()));
-
- sut.execute();
-
- assertThat(measureRepository.getRawMeasure(file1, ISSUES_METRIC).get().getIntValue()).isEqualTo(1);
- assertThat(measureRepository.getRawMeasure(file2, ISSUES_METRIC).get().getIntValue()).isEqualTo(1);
- assertThat(measureRepository.getRawMeasure(directory, ISSUES_METRIC).get().getIntValue()).isEqualTo(2);
- assertThat(measureRepository.getRawMeasure(project, ISSUES_METRIC).get().getIntValue()).isEqualTo(2);
- }
-
- @Test
- public void compute_measures_on_issue_statuses() throws Exception {
- treeRootHolder.setRoot(PROJECT);
- periodsHolder.setPeriods();
- addIssues(FILE.getRef(),
- createIssue(STATUS_OPEN, BLOCKER, RULE_1.getKey()),
- createIssue(STATUS_REOPENED, BLOCKER, RULE_2.getKey()),
- createIssue(STATUS_CONFIRMED, BLOCKER, RULE_3.getKey()),
- createIssue(STATUS_CONFIRMED, BLOCKER, RULE_4.getKey()));
-
- sut.execute();
-
- assertThat(measureRepository.getRawMeasure(PROJECT, OPEN_ISSUES_METRIC).get().getIntValue()).isEqualTo(1);
- assertThat(measureRepository.getRawMeasure(PROJECT, REOPENED_ISSUES_METRIC).get().getIntValue()).isEqualTo(1);
- assertThat(measureRepository.getRawMeasure(PROJECT, CONFIRMED_ISSUES_METRIC).get().getIntValue()).isEqualTo(2);
- }
-
- @Test
- public void compute_measures_on_issue_severities() throws Exception {
- treeRootHolder.setRoot(PROJECT);
- periodsHolder.setPeriods();
- addIssues(FILE.getRef(),
- createIssue(STATUS_OPEN, BLOCKER, RULE_1.getKey()),
- createIssue(STATUS_OPEN, CRITICAL, RULE_2.getKey()),
- createIssue(STATUS_OPEN, MAJOR, RULE_3.getKey()),
- createIssue(STATUS_OPEN, MINOR, RULE_4.getKey()),
- createIssue(STATUS_OPEN, INFO, RULE_5.getKey()),
- createIssue(STATUS_OPEN, INFO, RULE_6.getKey()));
-
- sut.execute();
-
- assertThat(measureRepository.getRawMeasure(PROJECT, BLOCKER_ISSUES_METRIC).get().getIntValue()).isEqualTo(1);
- assertThat(measureRepository.getRawMeasure(PROJECT, CRITICAL_ISSUES_METRIC).get().getIntValue()).isEqualTo(1);
- assertThat(measureRepository.getRawMeasure(PROJECT, MAJOR_ISSUES_METRIC).get().getIntValue()).isEqualTo(1);
- assertThat(measureRepository.getRawMeasure(PROJECT, MINOR_ISSUES_METRIC).get().getIntValue()).isEqualTo(1);
- assertThat(measureRepository.getRawMeasure(PROJECT, INFO_ISSUES_METRIC).get().getIntValue()).isEqualTo(2);
- }
-
- @Test
- public void compute_measures_on_false_positive_issue() throws Exception {
- treeRootHolder.setRoot(PROJECT);
- periodsHolder.setPeriods();
- addIssues(FILE.getRef(),
- createIssue(STATUS_OPEN, BLOCKER, RULE_1.getKey()),
- createIssue(STATUS_CLOSED, BLOCKER, RESOLUTION_FALSE_POSITIVE, RULE_2.getKey()),
- createIssue(STATUS_RESOLVED, BLOCKER, RESOLUTION_FIXED, RULE_3.getKey()),
- createIssue(STATUS_RESOLVED, BLOCKER, RESOLUTION_REMOVED, RULE_4.getKey()));
-
- sut.execute();
-
- assertThat(measureRepository.getRawMeasure(PROJECT, FALSE_POSITIVE_ISSUES_METRIC).get().getIntValue()).isEqualTo(1);
- }
-
- @Test
- public void compute_measures_on_new_issue() throws Exception {
- treeRootHolder.setRoot(PROJECT);
- addIssues(FILE.getRef(),
- // issue created before the period 3
- createIssue(STATUS_CONFIRMED, BLOCKER, null, RULE_1.getKey(), 1388552400000L),
- // issue created after period 3 but before current analysis
- createIssue(STATUS_OPEN, BLOCKER, null, RULE_1.getKey(), 1433131200000L));
- periodsHolder.setPeriods(newPeriod(3, 1420088400000L));
-
- sut.execute();
-
- // Only 1 new issues for period 3
- assertThat(measureRepository.getRawMeasure(PROJECT, NEW_ISSUES_METRIC).get().getVariations().getVariation3()).isEqualTo(1);
-
- // No variation on other periods
- assertThat(measureRepository.getRawMeasure(PROJECT, NEW_ISSUES_METRIC).get().getVariations().hasVariation1()).isFalse();
- assertThat(measureRepository.getRawMeasure(PROJECT, NEW_ISSUES_METRIC).get().getVariations().hasVariation2()).isFalse();
- assertThat(measureRepository.getRawMeasure(PROJECT, NEW_ISSUES_METRIC).get().getVariations().hasVariation4()).isFalse();
- assertThat(measureRepository.getRawMeasure(PROJECT, NEW_ISSUES_METRIC).get().getVariations().hasVariation5()).isFalse();
- }
-
- @Test
- public void do_not_take_into_account_issue_from_current_analysis_when_computing_measures_on_new_issue() throws Exception {
- treeRootHolder.setRoot(PROJECT);
- addIssues(FILE.getRef(),
- // issue created during current analysis -> should not be taking into account
- createIssue(STATUS_OPEN, BLOCKER, null, RULE_1.getKey(), 1420088400000L));
- periodsHolder.setPeriods(newPeriod(1, 1420088400000L));
-
- sut.execute();
-
- // No new issues
- assertThat(measureRepository.getRawMeasure(PROJECT, NEW_ISSUES_METRIC).get().getVariations().getVariation1()).isEqualTo(0);
- }
-
- @Test
- public void compute_measures_on_new_issue_on_every_variations() throws Exception {
- treeRootHolder.setRoot(PROJECT);
- addIssues(FILE.getRef(),
- // issue created the 2014-01-01, before all periods
- createIssue(STATUS_CONFIRMED, BLOCKER, null, RULE_1.getKey(), 1388552400000L),
- // issue created the 2015-01-15, before period 2
- createIssue(STATUS_CONFIRMED, BLOCKER, null, RULE_1.getKey(), 1421298000000L),
- // issue created the 2015-02-15, before period 3
- createIssue(STATUS_CONFIRMED, BLOCKER, null, RULE_1.getKey(), 1423976400000L),
- // issue created the 2015-03-15, before period 4
- createIssue(STATUS_CONFIRMED, BLOCKER, null, RULE_1.getKey(), 1426392000000L),
- // issue created the 2015-04-15, before period 5
- createIssue(STATUS_CONFIRMED, BLOCKER, null, RULE_1.getKey(), 1429070400000L),
- // issue created the 2015-06-01 -> Should not been taken into account by any period
- createIssue(STATUS_OPEN, BLOCKER, null, RULE_1.getKey(), 1433131200000L));
- periodsHolder.setPeriods(
- // 2015-01-01
- newPeriod(1, 1420088400000L),
- // 2015-02-01
- newPeriod(2, 1422766800000L),
- // 2015-03-01
- newPeriod(3, 1425186000000L),
- // 2015-04-01
- newPeriod(4, 1427860800000L),
- // 2015-05-01
- newPeriod(5, 1430452800000L));
-
- sut.execute();
-
- assertThat(measureRepository.getRawMeasure(PROJECT, NEW_ISSUES_METRIC).get().getVariations().getVariation1()).isEqualTo(5);
- assertThat(measureRepository.getRawMeasure(PROJECT, NEW_ISSUES_METRIC).get().getVariations().getVariation2()).isEqualTo(4);
- assertThat(measureRepository.getRawMeasure(PROJECT, NEW_ISSUES_METRIC).get().getVariations().getVariation3()).isEqualTo(3);
- assertThat(measureRepository.getRawMeasure(PROJECT, NEW_ISSUES_METRIC).get().getVariations().getVariation4()).isEqualTo(2);
- assertThat(measureRepository.getRawMeasure(PROJECT, NEW_ISSUES_METRIC).get().getVariations().getVariation5()).isEqualTo(1);
- }
-
- @Test
- public void compute_measures_on_new_issue_severities() throws Exception {
- treeRootHolder.setRoot(PROJECT);
- addIssues(FILE.getRef(),
- // issue created before the period 1
- createIssue(STATUS_CONFIRMED, BLOCKER, null, RULE_1.getKey(), 1388552400000L),
- // issues created after period 1 but before current analysis
- createIssue(STATUS_OPEN, BLOCKER, null, RULE_1.getKey(), 1433131200000L),
- createIssue(STATUS_OPEN, BLOCKER, null, RULE_2.getKey(), 1433131200000L),
- createIssue(STATUS_OPEN, CRITICAL, null, RULE_1.getKey(), 1433131200000L),
- createIssue(STATUS_OPEN, MAJOR, null, RULE_1.getKey(), 1433131200000L),
- createIssue(STATUS_OPEN, MINOR, null, RULE_1.getKey(), 1433131200000L),
- createIssue(STATUS_OPEN, INFO, null, RULE_1.getKey(), 1433131200000L));
- periodsHolder.setPeriods(newPeriod(1, 1420088400000L));
-
- sut.execute();
-
- assertThat(measureRepository.getRawMeasure(PROJECT, NEW_BLOCKER_ISSUES_METRIC).get().getVariations().getVariation1()).isEqualTo(2);
- assertThat(measureRepository.getRawMeasure(PROJECT, NEW_CRITICAL_ISSUES_METRIC).get().getVariations().getVariation1()).isEqualTo(1);
- assertThat(measureRepository.getRawMeasure(PROJECT, NEW_MAJOR_ISSUES_METRIC).get().getVariations().getVariation1()).isEqualTo(1);
- assertThat(measureRepository.getRawMeasure(PROJECT, NEW_MINOR_ISSUES_METRIC).get().getVariations().getVariation1()).isEqualTo(1);
- assertThat(measureRepository.getRawMeasure(PROJECT, NEW_INFO_ISSUES_METRIC).get().getVariations().getVariation1()).isEqualTo(1);
- }
-
- @Test
- public void compute_no_new_measures_when_no_period() throws Exception {
- treeRootHolder.setRoot(PROJECT);
- periodsHolder.setPeriods();
- addIssues(FILE.getRef(),
- createIssue(STATUS_CONFIRMED, BLOCKER, null, RULE_1.getKey(), 1388552400000L));
-
- sut.execute();
-
- assertThat(measureRepository.getRawMeasure(PROJECT, NEW_ISSUES_METRIC).isPresent()).isFalse();
- assertThat(measureRepository.getRawMeasure(PROJECT, NEW_BLOCKER_ISSUES_METRIC).isPresent()).isFalse();
- assertThat(measureRepository.getRawMeasure(PROJECT, NEW_CRITICAL_ISSUES_METRIC).isPresent()).isFalse();
- assertThat(measureRepository.getRawMeasure(PROJECT, NEW_MAJOR_ISSUES_METRIC).isPresent()).isFalse();
- assertThat(measureRepository.getRawMeasure(PROJECT, NEW_MINOR_ISSUES_METRIC).isPresent()).isFalse();
- assertThat(measureRepository.getRawMeasure(PROJECT, NEW_INFO_ISSUES_METRIC).isPresent()).isFalse();
- }
-
- @Test
- public void compute_measures_having_zero_value_if_no_issue() throws Exception {
- treeRootHolder.setRoot(PROJECT);
- periodsHolder.setPeriods();
-
- sut.execute();
-
- assertThat(measureRepository.getRawMeasure(PROJECT, ISSUES_METRIC).get().getIntValue()).isEqualTo(0);
- assertThat(measureRepository.getRawMeasure(PROJECT, OPEN_ISSUES_METRIC).get().getIntValue()).isEqualTo(0);
- assertThat(measureRepository.getRawMeasure(PROJECT, REOPENED_ISSUES_METRIC).get().getIntValue()).isEqualTo(0);
- assertThat(measureRepository.getRawMeasure(PROJECT, BLOCKER_ISSUES_METRIC).get().getIntValue()).isEqualTo(0);
- assertThat(measureRepository.getRawMeasure(PROJECT, CRITICAL_ISSUES_METRIC).get().getIntValue()).isEqualTo(0);
- assertThat(measureRepository.getRawMeasure(PROJECT, MAJOR_ISSUES_METRIC).get().getIntValue()).isEqualTo(0);
- assertThat(measureRepository.getRawMeasure(PROJECT, MINOR_ISSUES_METRIC).get().getIntValue()).isEqualTo(0);
- assertThat(measureRepository.getRawMeasure(PROJECT, INFO_ISSUES_METRIC).get().getIntValue()).isEqualTo(0);
- }
-
- @Test
- public void ignore_resolved_issues() throws Exception {
- treeRootHolder.setRoot(PROJECT);
- periodsHolder.setPeriods();
- addIssues(FILE.getRef(),
- createIssue(STATUS_CLOSED, BLOCKER, RESOLUTION_FALSE_POSITIVE, RULE_1.getKey()),
- createIssue(STATUS_RESOLVED, BLOCKER, RESOLUTION_FIXED, RULE_2.getKey()),
- createIssue(STATUS_RESOLVED, BLOCKER, RESOLUTION_REMOVED, RULE_3.getKey()));
-
- sut.execute();
-
- assertThat(measureRepository.getRawMeasure(PROJECT, ISSUES_METRIC).get().getIntValue()).isEqualTo(0);
- }
-
- private void addIssues(int componentRef, Issue... issues) {
- reportReader.putIssues(componentRef, Arrays.asList(issues));
- }
-
- private static Issue createIssue(String status, String severity, RuleKey ruleKey) {
- return createIssue(status, severity, null, ruleKey, 1000L);
- }
-
- private static Issue createIssue(String status, String severity, @Nullable String resolution, RuleKey ruleKey) {
- return createIssue(status, severity, resolution, ruleKey, 1000L);
- }
-
- private static Issue createIssue(String status, String severity, @Nullable String resolution, RuleKey ruleKey, long creationDate) {
- BatchReport.Issue.Builder issueBuilder = Issue.newBuilder()
- .setUuid(Uuids.create())
- .setStatus(status)
- .setRuleKey(ruleKey.rule())
- .setRuleRepository(ruleKey.repository())
- .setSeverity(Constants.Severity.valueOf(severity))
- .setCreationDate(creationDate);
- if (resolution != null) {
- issueBuilder.setResolution(resolution);
- }
- return issueBuilder.build();
- }
-
- private static Period newPeriod(int index, long date) {
- return new Period(index, "mode", null, date, 42l);
- }
-
- private void initMetrics() {
- when(metricRepository.getByKey(ISSUES_METRIC.getKey())).thenReturn(ISSUES_METRIC);
- when(metricRepository.getByKey(OPEN_ISSUES_METRIC.getKey())).thenReturn(OPEN_ISSUES_METRIC);
- when(metricRepository.getByKey(REOPENED_ISSUES_METRIC.getKey())).thenReturn(REOPENED_ISSUES_METRIC);
- when(metricRepository.getByKey(CONFIRMED_ISSUES_METRIC.getKey())).thenReturn(CONFIRMED_ISSUES_METRIC);
- when(metricRepository.getByKey(BLOCKER_ISSUES_METRIC.getKey())).thenReturn(BLOCKER_ISSUES_METRIC);
- when(metricRepository.getByKey(CRITICAL_ISSUES_METRIC.getKey())).thenReturn(CRITICAL_ISSUES_METRIC);
- when(metricRepository.getByKey(MAJOR_ISSUES_METRIC.getKey())).thenReturn(MAJOR_ISSUES_METRIC);
- when(metricRepository.getByKey(MINOR_ISSUES_METRIC.getKey())).thenReturn(MINOR_ISSUES_METRIC);
- when(metricRepository.getByKey(INFO_ISSUES_METRIC.getKey())).thenReturn(INFO_ISSUES_METRIC);
- when(metricRepository.getByKey(NEW_ISSUES_METRIC.getKey())).thenReturn(NEW_ISSUES_METRIC);
- when(metricRepository.getByKey(NEW_BLOCKER_ISSUES_METRIC.getKey())).thenReturn(NEW_BLOCKER_ISSUES_METRIC);
- when(metricRepository.getByKey(NEW_CRITICAL_ISSUES_METRIC.getKey())).thenReturn(NEW_CRITICAL_ISSUES_METRIC);
- when(metricRepository.getByKey(NEW_MAJOR_ISSUES_METRIC.getKey())).thenReturn(NEW_MAJOR_ISSUES_METRIC);
- when(metricRepository.getByKey(NEW_MINOR_ISSUES_METRIC.getKey())).thenReturn(NEW_MINOR_ISSUES_METRIC);
- when(metricRepository.getByKey(NEW_INFO_ISSUES_METRIC.getKey())).thenReturn(NEW_INFO_ISSUES_METRIC);
- when(metricRepository.getByKey(FALSE_POSITIVE_ISSUES_METRIC.getKey())).thenReturn(FALSE_POSITIVE_ISSUES_METRIC);
- }
-}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/computation/issue/ScmAccountCacheLoaderTest/charlie.json b/server/sonar-server/src/test/resources/org/sonar/server/computation/issue/ScmAccountToUserLoaderTest/charlie.json
index f509e6b39a5..f509e6b39a5 100644
--- a/server/sonar-server/src/test/resources/org/sonar/server/computation/issue/ScmAccountCacheLoaderTest/charlie.json
+++ b/server/sonar-server/src/test/resources/org/sonar/server/computation/issue/ScmAccountToUserLoaderTest/charlie.json
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/computation/issue/ScmAccountCacheLoaderTest/charlie_conflict.json b/server/sonar-server/src/test/resources/org/sonar/server/computation/issue/ScmAccountToUserLoaderTest/charlie_conflict.json
index 8f5af49f8ae..8f5af49f8ae 100644
--- a/server/sonar-server/src/test/resources/org/sonar/server/computation/issue/ScmAccountCacheLoaderTest/charlie_conflict.json
+++ b/server/sonar-server/src/test/resources/org/sonar/server/computation/issue/ScmAccountToUserLoaderTest/charlie_conflict.json
diff --git a/sonar-batch/src/main/java/org/sonar/batch/compute/SeverityUtils.java b/sonar-batch/src/main/java/org/sonar/batch/compute/SeverityUtils.java
deleted file mode 100644
index fd92039ce98..00000000000
--- a/sonar-batch/src/main/java/org/sonar/batch/compute/SeverityUtils.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.batch.compute;
-
-import org.sonar.api.measures.CoreMetrics;
-import org.sonar.api.measures.Metric;
-import org.sonar.api.rules.RulePriority;
-
-final class SeverityUtils {
- private SeverityUtils() {
- // only static methods
- }
-
- static Metric severityToIssueMetric(RulePriority severity) {
- Metric metric;
- if (severity.equals(RulePriority.BLOCKER)) {
- metric = CoreMetrics.BLOCKER_VIOLATIONS;
- } else if (severity.equals(RulePriority.CRITICAL)) {
- metric = CoreMetrics.CRITICAL_VIOLATIONS;
- } else if (severity.equals(RulePriority.MAJOR)) {
- metric = CoreMetrics.MAJOR_VIOLATIONS;
- } else if (severity.equals(RulePriority.MINOR)) {
- metric = CoreMetrics.MINOR_VIOLATIONS;
- } else if (severity.equals(RulePriority.INFO)) {
- metric = CoreMetrics.INFO_VIOLATIONS;
- } else {
- throw new IllegalArgumentException("Unsupported severity: " + severity);
- }
- return metric;
- }
-
- static Metric severityToNewMetricIssue(RulePriority severity) {
- Metric metric;
- if (severity.equals(RulePriority.BLOCKER)) {
- metric = CoreMetrics.NEW_BLOCKER_VIOLATIONS;
- } else if (severity.equals(RulePriority.CRITICAL)) {
- metric = CoreMetrics.NEW_CRITICAL_VIOLATIONS;
- } else if (severity.equals(RulePriority.MAJOR)) {
- metric = CoreMetrics.NEW_MAJOR_VIOLATIONS;
- } else if (severity.equals(RulePriority.MINOR)) {
- metric = CoreMetrics.NEW_MINOR_VIOLATIONS;
- } else if (severity.equals(RulePriority.INFO)) {
- metric = CoreMetrics.NEW_INFO_VIOLATIONS;
- } else {
- throw new IllegalArgumentException("Unsupported severity: " + severity);
- }
- return metric;
- }
-}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/tracking/IssueTrackingTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/tracking/IssueTrackingTest.java
index ae678c34bd3..616c7fb6ed4 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/issue/tracking/IssueTrackingTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/issue/tracking/IssueTrackingTest.java
@@ -246,7 +246,7 @@ public class IssueTrackingTest {
DefaultIssue newIssue = newDefaultIssue("1 branch need to be covered", 200, RuleKey.of("squid", "AvoidCycle"), null);
thrown
- .expectMessage("Invalid line number for issue DefaultIssue[key=<null>,componentUuid=<null>,componentKey=<null>,moduleUuid=<null>,moduleUuidPath=<null>,projectUuid=<null>,projectKey=<null>,ruleKey=squid:AvoidCycle,language=<null>,severity=<null>,manualSeverity=false,message=1 branch need to be covered,line=200,effortToFix=<null>,debt=<null>,status=OPEN,resolution=<null>,reporter=<null>,assignee=<null>,checksum=<null>,attributes=<null>,authorLogin=<null>,actionPlanKey=<null>,comments=<null>,tags=<null>,creationDate=<null>,updateDate=<null>,closeDate=<null>,currentChange=<null>,changes=<null>,isNew=true,endOfLife=false,onDisabledRule=false,isChanged=false,sendNotifications=false,selectedAt=<null>]. File has only 17 line(s)");
+ .expectMessage("Invalid line number for issue");
tracking.track(sourceHashHolder, Collections.<ServerIssue>emptyList(), newArrayList(newIssue));
}
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/workflow/UnsetLine.java b/sonar-core/src/main/java/org/sonar/core/issue/workflow/UnsetLine.java
deleted file mode 100644
index 8073e179280..00000000000
--- a/sonar-core/src/main/java/org/sonar/core/issue/workflow/UnsetLine.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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.core.issue.workflow;
-
-enum UnsetLine implements Function {
- INSTANCE;
- @Override
- public void execute(Context context) {
- context.setLine(null);
- }
-
-}