summaryrefslogtreecommitdiffstats
path: root/server/sonar-server
diff options
context:
space:
mode:
authorSimon Brandhof <simon.brandhof@sonarsource.com>2015-07-02 20:01:59 +0200
committerSimon Brandhof <simon.brandhof@sonarsource.com>2015-07-02 20:01:59 +0200
commitbe9b5a2ea93b7959cbaa4a630bff79467b3ba8d0 (patch)
tree03fad10de15f1cf9d64199e32bfa3b90158035ea /server/sonar-server
parente154f3cf3e73c0ba447fffef1d447dc729f8bf9b (diff)
parent1c0b9a97e3bc6d58c4697b4350b39adc81a6efc1 (diff)
downloadsonarqube-be9b5a2ea93b7959cbaa4a630bff79467b3ba8d0.tar.gz
sonarqube-be9b5a2ea93b7959cbaa4a630bff79467b3ba8d0.zip
Merge branch 'feature/ce_issue_tracking'
Diffstat (limited to 'server/sonar-server')
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/batch/BatchReportReader.java5
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/batch/BatchReportReaderImpl.java5
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/component/ComponentVisitor.java12
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/component/ProjectSettingsRepository.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainerImpl.java49
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/debt/Characteristic.java21
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/debt/DebtModelHolder.java25
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/debt/DebtModelHolderImpl.java50
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/debt/MutableDebtModelHolder.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/BaseIssuesLoader.java104
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/DebtAggregator.java171
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/DebtCalculator.java69
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/DefaultAssignee.java73
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueAssigner.java (renamed from server/sonar-server/src/main/java/org/sonar/server/computation/issue/SourceLinesCache.java)140
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueCache.java8
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueComputation.java166
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueCounter.java268
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueLifecycle.java100
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueVisitor.java48
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueVisitors.java51
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/NewDebtAggregator.java120
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/NewDebtCalculator.java130
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/Rule.java53
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/RuleCacheLoader.java26
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/RuleImpl.java155
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/RuleRepository.java28
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/RuleRepositoryImpl.java (renamed from server/sonar-server/src/main/java/org/sonar/server/computation/issue/RuleCache.java)19
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/RuleTagsCopier.java43
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/ScmAccountToUser.java (renamed from server/sonar-server/src/main/java/org/sonar/server/computation/issue/ScmAccountCache.java)7
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/ScmAccountToUserLoader.java (renamed from server/sonar-server/src/main/java/org/sonar/server/computation/issue/ScmAccountCacheLoader.java)38
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/TrackerBaseInputFactory.java72
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/TrackerExecution.java43
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/TrackerRawInputFactory.java118
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/measure/BatchMeasureToMeasure.java54
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/measure/MeasureRepositoryImpl.java5
-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.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java9
-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/FeedDebtModelStep.java19
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/step/IntegrateIssuesStep.java153
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/step/ParseReportStep.java100
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistIssuesStep.java35
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/step/SendIssueNotificationsStep.java13
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/AbstractChangeTagsAction.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/Action.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/ActionService.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/AssignAction.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/CommentAction.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java6
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/IssueBulkChangeService.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/IssueChangelog.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/IssueChangelogFormatter.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/IssueChangelogService.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/IssueCommentService.java6
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/IssueQueryService.java8
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/PlanAction.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/ServerIssueStorage.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/SetSeverityAction.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/TransitionAction.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/actionplan/ActionPlanService.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/db/IssueDao.java15
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/notification/IssueChangeNotification.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueJsonWriter.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/ws/ShowAction.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/source/db/FileSourceDao.java24
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/batch/BatchReportReaderImplTest.java21
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/batch/BatchReportReaderRule.java18
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/debt/CharacteristicTest.java7
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/debt/DebtModelHolderImplTest.java53
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/issue/DebtAggregatorTest.java155
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/issue/DebtCalculatorTest.java98
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/issue/DefaultAssigneeTest.java73
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/issue/DumbRule.java118
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueAssignerTest.java (renamed from server/sonar-server/src/test/java/org/sonar/server/computation/issue/SourceLinesCacheTest.java)86
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueComputationTest.java433
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueCounterTest.java266
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/issue/NewDebtAggregatorTest.java114
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/issue/NewDebtCalculatorTest.java125
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/issue/RuleCacheLoaderTest.java33
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/issue/RuleRepositoryImplTest.java (renamed from server/sonar-server/src/test/java/org/sonar/server/computation/issue/RuleCacheTest.java)24
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/issue/RuleRepositoryRule.java59
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/issue/RuleTagsCopierTest.java72
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/issue/ScmAccountToUserLoaderTest.java (renamed from server/sonar-server/src/test/java/org/sonar/server/computation/issue/ScmAccountCacheLoaderTest.java)22
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/measure/BatchMeasureToMeasureTest.java57
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureRepositoryImplTest.java14
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureRepositoryRule.java8
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/step/ComputeIssueMeasuresStepTest.java412
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/step/FeedDebtModelStepTest.java8
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/step/FillMeasuresWithVariationsStepTest.java68
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/step/ParseReportStepTest.java122
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistIssuesStepTest.java23
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistMeasuresStepTest.java143
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/step/SendIssueNotificationsStepTest.java6
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/ActionServiceTest.java4
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/AddTagsActionTest.java4
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/AssignActionTest.java4
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/CommentActionTest.java4
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java4
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/IssueChangelogFormatterTest.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/IssueChangelogServiceTest.java4
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/IssueCommentServiceMediumTest.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/IssueCommentServiceTest.java6
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/PlanActionTest.java4
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/RemoveTagsActionTest.java4
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/ServerIssueStorageTest.java6
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/SetSeverityActionTest.java4
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/TransitionActionTest.java4
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/actionplan/ActionPlanServiceTest.java4
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/notification/IssueChangeNotificationTest.java4
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationTest.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/notification/NewIssuesStatisticsTest.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueActionsWriterTest.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/ws/ShowActionTest.java6
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/computation/issue/RuleCacheLoaderTest/shared.xml4
-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--server/sonar-server/src/test/resources/org/sonar/server/computation/step/PersistIssuesStepTest/close_issue-result.xml2
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/computation/step/PersistIssuesStepTest/insert_new_issue.xml2
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/computation/step/PersistIssuesStepTest/shared.xml4
122 files changed, 3650 insertions, 2141 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/batch/BatchReportReader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/batch/BatchReportReader.java
index dc57d0aa6ae..2568cb4ed9a 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/batch/BatchReportReader.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/batch/BatchReportReader.java
@@ -36,8 +36,6 @@ public interface BatchReportReader {
List<BatchReport.Issue> readComponentIssues(int componentRef);
- BatchReport.Issues readDeletedComponentIssues(int deletedComponentRef);
-
List<BatchReport.Duplication> readComponentDuplications(int componentRef);
List<BatchReport.Symbols.Symbol> readComponentSymbols(int componentRef);
@@ -47,7 +45,8 @@ public interface BatchReportReader {
CloseableIterator<BatchReport.Coverage> readComponentCoverage(int fileRef);
/**
- * Reads file source line by line.
+ * Reads file source line by line. Throws an exception if the ref does not relate
+ * to a file
*/
CloseableIterator<String> readFileSource(int fileRef);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/batch/BatchReportReaderImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/batch/BatchReportReaderImpl.java
index c48e0289b1a..8f1f476e2ec 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/batch/BatchReportReaderImpl.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/batch/BatchReportReaderImpl.java
@@ -74,11 +74,6 @@ public class BatchReportReaderImpl implements BatchReportReader {
}
@Override
- public BatchReport.Issues readDeletedComponentIssues(int deletedComponentRef) {
- return delegate.readDeletedComponentIssues(deletedComponentRef);
- }
-
- @Override
public List<BatchReport.Duplication> readComponentDuplications(int componentRef) {
return delegate.readComponentDuplications(componentRef);
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/component/ComponentVisitor.java b/server/sonar-server/src/main/java/org/sonar/server/computation/component/ComponentVisitor.java
index 5804e0b2b55..4039933865c 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/component/ComponentVisitor.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/component/ComponentVisitor.java
@@ -23,6 +23,16 @@ public interface ComponentVisitor {
void visit(Component tree);
enum Order {
- PRE_ORDER, POST_ORDER
+ /**
+ * Each component is visited BEFORE its children. Top-down traversal of
+ * tree of components.
+ */
+ PRE_ORDER,
+
+ /**
+ * Each component is visited AFTER its children. Bottom-up traversal of
+ * tree of components.
+ */
+ POST_ORDER
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/component/ProjectSettingsRepository.java b/server/sonar-server/src/main/java/org/sonar/server/computation/component/ProjectSettingsRepository.java
index 467049c0e00..2a12477b8de 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/component/ProjectSettingsRepository.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/component/ProjectSettingsRepository.java
@@ -26,7 +26,7 @@ import org.sonar.server.properties.ProjectSettingsFactory;
/**
* Lazy loading of project settings.
*
- * Should be refactored to be able to load settings from any components.
+ * TODO Should be refactored to be able to load settings from any components.
*/
public class ProjectSettingsRepository {
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 81b8cd7fd97..ec4863798e0 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
@@ -31,6 +31,7 @@ import org.sonar.api.utils.log.Loggers;
import org.sonar.api.utils.log.Profiler;
import org.sonar.core.component.Module;
import org.sonar.core.issue.db.UpdateConflictResolver;
+import org.sonar.core.issue.tracking.Tracker;
import org.sonar.core.platform.ComponentContainer;
import org.sonar.server.computation.ComputationService;
import org.sonar.server.computation.ReportQueue;
@@ -43,13 +44,25 @@ import org.sonar.server.computation.component.TreeRootHolderImpl;
import org.sonar.server.computation.debt.DebtModelHolderImpl;
import org.sonar.server.computation.event.EventRepositoryImpl;
import org.sonar.server.computation.formula.FormulaRepositoryImpl;
+import org.sonar.server.computation.issue.BaseIssuesLoader;
+import org.sonar.server.computation.issue.DebtCalculator;
+import org.sonar.server.computation.issue.IssueCounter;
+import org.sonar.server.computation.issue.DebtAggregator;
+import org.sonar.server.computation.issue.DefaultAssignee;
+import org.sonar.server.computation.issue.IssueAssigner;
import org.sonar.server.computation.issue.IssueCache;
-import org.sonar.server.computation.issue.IssueComputation;
-import org.sonar.server.computation.issue.RuleCache;
+import org.sonar.server.computation.issue.IssueLifecycle;
+import org.sonar.server.computation.issue.IssueVisitors;
+import org.sonar.server.computation.issue.NewDebtAggregator;
+import org.sonar.server.computation.issue.NewDebtCalculator;
import org.sonar.server.computation.issue.RuleCacheLoader;
-import org.sonar.server.computation.issue.ScmAccountCache;
-import org.sonar.server.computation.issue.ScmAccountCacheLoader;
-import org.sonar.server.computation.issue.SourceLinesCache;
+import org.sonar.server.computation.issue.RuleRepositoryImpl;
+import org.sonar.server.computation.issue.RuleTagsCopier;
+import org.sonar.server.computation.issue.ScmAccountToUser;
+import org.sonar.server.computation.issue.ScmAccountToUserLoader;
+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.language.LanguageRepositoryImpl;
import org.sonar.server.computation.measure.MeasureRepositoryImpl;
import org.sonar.server.computation.measure.newcoverage.NewCoverageMetricKeysModule;
@@ -156,14 +169,30 @@ public class ComputeEngineContainerImpl extends ComponentContainer implements Co
NewCoverageMetricKeysModule.class,
// issues
- ScmAccountCacheLoader.class,
- ScmAccountCache.class,
- SourceLinesCache.class,
- IssueComputation.class,
- RuleCache.class,
RuleCacheLoader.class,
+ RuleRepositoryImpl.class,
+ ScmAccountToUserLoader.class,
+ ScmAccountToUser.class,
IssueCache.class,
+ DefaultAssignee.class,
+ IssueVisitors.class,
+ IssueLifecycle.class,
+
+ // order is important: DebtAggregator then NewDebtAggregator (new debt requires debt)
+ DebtCalculator.class,
+ DebtAggregator.class,
+ NewDebtCalculator.class,
+ NewDebtAggregator.class,
+ IssueAssigner.class,
+ RuleTagsCopier.class,
+ IssueCounter.class,
+
UpdateConflictResolver.class,
+ TrackerBaseInputFactory.class,
+ TrackerRawInputFactory.class,
+ Tracker.class,
+ TrackerExecution.class,
+ BaseIssuesLoader.class,
// views
ViewIndex.class,
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/debt/Characteristic.java b/server/sonar-server/src/main/java/org/sonar/server/computation/debt/Characteristic.java
index 5c1224c8df9..0606b2533ee 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/debt/Characteristic.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/debt/Characteristic.java
@@ -20,6 +20,7 @@
package org.sonar.server.computation.debt;
+import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
@@ -29,11 +30,13 @@ import static java.util.Objects.requireNonNull;
public class Characteristic {
private final int id;
private final String key;
+ private final Integer parentId;
- public Characteristic(int id, String key) {
+ public Characteristic(int id, String key, @Nullable Integer parentId) {
requireNonNull(key, "key cannot be null");
this.id = id;
this.key = key;
+ this.parentId = parentId;
}
public int getId() {
@@ -44,6 +47,11 @@ public class Characteristic {
return key;
}
+ @CheckForNull
+ public Integer getParentId() {
+ return parentId;
+ }
+
@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
@@ -66,10 +74,11 @@ public class Characteristic {
@Override
public String toString() {
- return "Characteristic{" +
- "id=" + id +
- ", key='" + key + '\'' +
- '}';
+ StringBuilder sb = new StringBuilder("Characteristic{");
+ sb.append("id=").append(id);
+ sb.append(", key='").append(key).append('\'');
+ sb.append(", parentId=").append(parentId);
+ sb.append('}');
+ return sb.toString();
}
-
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/debt/DebtModelHolder.java b/server/sonar-server/src/main/java/org/sonar/server/computation/debt/DebtModelHolder.java
index c79b7cb2860..0290164b18b 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/debt/DebtModelHolder.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/debt/DebtModelHolder.java
@@ -20,30 +20,17 @@
package org.sonar.server.computation.debt;
-import com.google.common.base.Optional;
-import java.util.Set;
+import java.util.List;
public interface DebtModelHolder {
/**
- * Return a characteristic by its key
+ * Return a characteristic by its id
*
- * @throws IllegalStateException if the holder is empty
+ * @throws IllegalStateException if no Characteristic with the specified id is found
+ * @throws IllegalStateException if the holder is not initialized yet
*/
- Optional<Characteristic> getCharacteristicByKey(String key);
-
- /**
- * Return the set of root characteristics
- *
- * @throws IllegalStateException if the holder is empty
- */
- Set<Characteristic> findRootCharacteristics();
-
- /**
- * Return the collection of sub characteristics from a root characteristic key
- *
- * @throws IllegalStateException if the holder is empty
- */
- Set<Characteristic> findSubCharacteristicsByRootKey(String rootCharacteristicKey);
+ Characteristic getCharacteristicById(int id);
+ List<Characteristic> getRootCharacteristics();
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/debt/DebtModelHolderImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/debt/DebtModelHolderImpl.java
index 6d0e0d1fbf1..a66b8354271 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/debt/DebtModelHolderImpl.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/debt/DebtModelHolderImpl.java
@@ -20,61 +20,51 @@
package org.sonar.server.computation.debt;
-import com.google.common.base.Optional;
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Multimap;
-import java.util.Collections;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
-import java.util.Set;
-import static com.google.common.base.Optional.fromNullable;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static java.util.Objects.requireNonNull;
public class DebtModelHolderImpl implements MutableDebtModelHolder {
- private final Multimap<Characteristic, Characteristic> subCharacteristicsByRootCharacteristic = ArrayListMultimap.create();
- private final Map<String, Characteristic> characteristicByKey = new HashMap<>();
+ private final List<Characteristic> rootCharacteristics = new ArrayList<>();
+ private final Map<Integer, Characteristic> characteristicById = new HashMap<>();
@Override
- public void addCharacteristics(Characteristic rootCharacteristic, Iterable<? extends Characteristic> subCharacteristics) {
+ public DebtModelHolderImpl addCharacteristics(Characteristic rootCharacteristic, Iterable<? extends Characteristic> subCharacteristics) {
requireNonNull(rootCharacteristic, "rootCharacteristic cannot be null");
requireNonNull(subCharacteristics, "subCharacteristics cannot be null");
checkArgument(subCharacteristics.iterator().hasNext(), "subCharacteristics cannot be empty");
- subCharacteristicsByRootCharacteristic.putAll(rootCharacteristic, subCharacteristics);
- characteristicByKey.put(rootCharacteristic.getKey(), rootCharacteristic);
+ rootCharacteristics.add(rootCharacteristic);
+ characteristicById.put(rootCharacteristic.getId(), rootCharacteristic);
for (Characteristic characteristic : subCharacteristics) {
- characteristicByKey.put(characteristic.getKey(), characteristic);
+ characteristicById.put(characteristic.getId(), characteristic);
}
+ return this;
}
@Override
- public Set<Characteristic> findRootCharacteristics() {
- checkCharacteristicsAreInitialized();
- return ImmutableSet.copyOf(subCharacteristicsByRootCharacteristic.keySet());
- }
-
- @Override
- public Set<Characteristic> findSubCharacteristicsByRootKey(String rootCharacteristicKey) {
- checkCharacteristicsAreInitialized();
- Characteristic rootCharacteristic = characteristicByKey.get(rootCharacteristicKey);
- if (rootCharacteristic == null) {
- return Collections.emptySet();
+ public Characteristic getCharacteristicById(int id) {
+ checkInitialized();
+ Characteristic characteristic = characteristicById.get(id);
+ if (characteristic == null) {
+ throw new IllegalStateException("Debt characteristic with id [" + id + "] does not exist");
}
- return ImmutableSet.copyOf(subCharacteristicsByRootCharacteristic.get(rootCharacteristic));
+ return characteristic;
}
@Override
- public Optional<Characteristic> getCharacteristicByKey(String key) {
- checkCharacteristicsAreInitialized();
- return fromNullable(characteristicByKey.get(key));
+ public List<Characteristic> getRootCharacteristics() {
+ checkInitialized();
+ return rootCharacteristics;
}
- private void checkCharacteristicsAreInitialized() {
- checkState(!characteristicByKey.isEmpty(), "Characteristics have not been initialized yet");
+ private void checkInitialized() {
+ checkState(!characteristicById.isEmpty(), "Characteristics have not been initialized yet");
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/debt/MutableDebtModelHolder.java b/server/sonar-server/src/main/java/org/sonar/server/computation/debt/MutableDebtModelHolder.java
index 665986425bf..45ab2d84fa2 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/debt/MutableDebtModelHolder.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/debt/MutableDebtModelHolder.java
@@ -31,5 +31,5 @@ public interface MutableDebtModelHolder extends DebtModelHolder {
* @throws NullPointerException if {@code rootCharacteristic} is {@code null}
* @throws NullPointerException if {@code subCharacteristics} is {@code null}
*/
- void addCharacteristics(Characteristic rootCharacteristic, Iterable<? extends Characteristic> subCharacteristics);
+ MutableDebtModelHolder addCharacteristics(Characteristic rootCharacteristic, Iterable<? extends Characteristic> subCharacteristics);
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/BaseIssuesLoader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/BaseIssuesLoader.java
new file mode 100644
index 00000000000..8ebfb64479e
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/BaseIssuesLoader.java
@@ -0,0 +1,104 @@
+/*
+ * 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.ImmutableMap;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.apache.ibatis.session.ResultContext;
+import org.apache.ibatis.session.ResultHandler;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rule.RuleStatus;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.db.IssueDto;
+import org.sonar.core.issue.db.IssueMapper;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.persistence.MyBatis;
+import org.sonar.server.computation.batch.BatchReportReader;
+import org.sonar.server.computation.component.TreeRootHolder;
+import org.sonar.server.db.DbClient;
+
+import static com.google.common.collect.FluentIterable.from;
+import static org.sonar.core.rule.RuleKeyFunctions.stringToRuleKey;
+
+/**
+ * Loads all the project open issues from database, including manual issues.
+ *
+ */
+public class BaseIssuesLoader {
+
+ private final Set<RuleKey> activeRuleKeys;
+ private final TreeRootHolder treeRootHolder;
+ private final DbClient dbClient;
+ private final RuleRepository ruleRepository;
+
+ public BaseIssuesLoader(BatchReportReader reportReader, TreeRootHolder treeRootHolder,
+ DbClient dbClient, RuleRepository ruleRepository) {
+ this.activeRuleKeys = from(reportReader.readMetadata().getActiveRuleKeyList()).transform(stringToRuleKey()).toSet();
+ this.treeRootHolder = treeRootHolder;
+ this.dbClient = dbClient;
+ this.ruleRepository = ruleRepository;
+ }
+
+ public List<DefaultIssue> loadForComponentUuid(String componentUuid) {
+ DbSession session = dbClient.openSession(false);
+ final List<DefaultIssue> result = new ArrayList<>();
+ try {
+ Map<String, String> params = ImmutableMap.of("componentUuid", componentUuid);
+ session.select(IssueMapper.class.getName() + ".selectNonClosedByComponentUuid", params, new ResultHandler() {
+ @Override
+ public void handleResult(ResultContext resultContext) {
+ DefaultIssue issue = ((IssueDto) resultContext.getResultObject()).toDefaultIssue();
+
+ // TODO this field should be set outside this class
+ if (!isActive(issue.ruleKey()) || ruleRepository.getByKey(issue.ruleKey()).getStatus() == RuleStatus.REMOVED) {
+ issue.setOnDisabledRule(true);
+ // TODO to be improved, why setOnDisabledRule(true) is not enough ?
+ issue.setBeingClosed(true);
+ }
+ // FIXME
+ issue.setSelectedAt(System.currentTimeMillis());
+ result.add(issue);
+ }
+ });
+ return result;
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
+
+ private boolean isActive(RuleKey ruleKey) {
+ return ruleKey.isManual() || activeRuleKeys.contains(ruleKey);
+ }
+
+ /**
+ * Uuids of all the components that have open issues on this project.
+ */
+ public Set<String> loadUuidsOfComponentsWithOpenIssues() {
+ DbSession session = dbClient.openSession(false);
+ try {
+ return dbClient.issueDao().selectComponentUuidsOfOpenIssuesForProjectUuid(session, treeRootHolder.getRoot().getUuid());
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/DebtAggregator.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/DebtAggregator.java
new file mode 100644
index 00000000000..c8c410a6503
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/DebtAggregator.java
@@ -0,0 +1,171 @@
+/*
+ * 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.HashMap;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.sonar.api.measures.CoreMetrics;
+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.debt.Characteristic;
+import org.sonar.server.computation.debt.DebtModelHolder;
+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 static com.google.common.collect.Maps.newHashMap;
+
+public class DebtAggregator extends IssueVisitor {
+
+ private final RuleRepository ruleRepository;
+ private final DebtModelHolder debtModelHolder;
+ private final MetricRepository metricRepository;
+ private final MeasureRepository measureRepository;
+
+ private final Map<Integer, Debt> debtsByComponentRef = new HashMap<>();
+ private Debt currentDebt;
+
+ public DebtAggregator(RuleRepository ruleRepository, DebtModelHolder debtModelHolder,
+ MetricRepository metricRepository, MeasureRepository measureRepository) {
+ this.ruleRepository = ruleRepository;
+ this.debtModelHolder = debtModelHolder;
+ this.metricRepository = metricRepository;
+ this.measureRepository = measureRepository;
+ }
+
+ @Override
+ public void beforeComponent(Component component, Tracking tracking) {
+ currentDebt = new Debt();
+ debtsByComponentRef.put(component.getRef(), currentDebt);
+
+ // aggregate children counters
+ for (Component child : component.getChildren()) {
+ // no need to keep the children in memory. They can be garbage-collected.
+ Debt childDebt = debtsByComponentRef.remove(child.getRef());
+ if (childDebt != null) {
+ currentDebt.add(childDebt);
+ }
+ }
+ }
+
+ @Override
+ public void onIssue(Component component, DefaultIssue issue) {
+ if (issue.resolution() == null) {
+ currentDebt.add(issue);
+ }
+ }
+
+ @Override
+ public void afterComponent(Component component) {
+ Metric metric = metricRepository.getByKey(CoreMetrics.TECHNICAL_DEBT_KEY);
+
+ // total value
+ measureRepository.add(component, metric, Measure.newMeasureBuilder().create(this.currentDebt.minutes));
+
+ // distribution by rule
+ for (Map.Entry<Integer, Long> entry : currentDebt.minutesByRuleId.entrySet()) {
+ int ruleId = entry.getKey();
+ long ruleDebt = entry.getValue();
+ // debt can't be zero.
+ measureRepository.add(component, metric, Measure.newMeasureBuilder().forRule(ruleId).create(ruleDebt));
+ }
+
+ // distribution by characteristic/sub-characteristic
+ for (Map.Entry<Integer, Long> entry : currentDebt.minutesByCharacteristicId.entrySet()) {
+ int characteristicId = entry.getKey();
+ long characteristicDebt = entry.getValue();
+ // debt can't be zero
+ measureRepository.add(component, metric, Measure.newMeasureBuilder().forCharacteristic(characteristicId).create(characteristicDebt));
+ }
+
+ if (!component.getType().isDeeperThan(Component.Type.MODULE)) {
+ for (Characteristic rootCharacteristic : debtModelHolder.getRootCharacteristics()) {
+ if (currentDebt.minutesByCharacteristicId.get(rootCharacteristic.getId()) == null) {
+ measureRepository.add(component, metric, Measure.newMeasureBuilder().forCharacteristic(rootCharacteristic.getId()).create(0L));
+ }
+ }
+ }
+
+ this.currentDebt = null;
+ }
+
+ private class Debt {
+ private long minutes = 0L;
+ private final SumMap<Integer> minutesByRuleId = new SumMap<>();
+ private final SumMap<Integer> minutesByCharacteristicId = new SumMap<>();
+
+ void add(DefaultIssue issue) {
+ Long issueMinutes = issue.debtInMinutes();
+ if (issueMinutes != null && issueMinutes != 0L) {
+ this.minutes += issueMinutes;
+
+ Rule rule = ruleRepository.getByKey(issue.ruleKey());
+ this.minutesByRuleId.add(rule.getId(), issueMinutes);
+
+ Integer subCharacteristicId = rule.getSubCharacteristicId();
+ if (subCharacteristicId != null) {
+ Characteristic characteristic = debtModelHolder.getCharacteristicById(subCharacteristicId);
+ this.minutesByCharacteristicId.add(characteristic.getId(), issueMinutes);
+ Integer characteristicParentId = characteristic.getParentId();
+ if (characteristicParentId != null) {
+ this.minutesByCharacteristicId.add(characteristicParentId, issueMinutes);
+ }
+ }
+ }
+ }
+
+ public void add(Debt debt) {
+ this.minutes += debt.minutes;
+ this.minutesByRuleId.add(debt.minutesByRuleId);
+ this.minutesByCharacteristicId.add(debt.minutesByCharacteristicId);
+ }
+ }
+
+ private static class SumMap<E> {
+ private final Map<E, Long> sumByKeys = newHashMap();
+
+ void add(SumMap<E> other) {
+ for (Map.Entry<E, Long> entry : other.entrySet()) {
+ add(entry.getKey(), entry.getValue());
+ }
+ }
+
+ void add(@Nullable E key, Long value) {
+ if (key != null) {
+ Long currentValue = sumByKeys.get(key);
+ sumByKeys.put(key, currentValue != null ? (currentValue + value) : value);
+ }
+ }
+
+ @CheckForNull
+ Long get(E key) {
+ return sumByKeys.get(key);
+ }
+
+ Set<Map.Entry<E, Long>> entrySet() {
+ return sumByKeys.entrySet();
+ }
+ }
+}
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
new file mode 100644
index 00000000000..0a7e5274809
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/DebtCalculator.java
@@ -0,0 +1,69 @@
+/*
+ * 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.base.Objects;
+import com.google.common.base.Strings;
+import javax.annotation.CheckForNull;
+import org.sonar.api.server.debt.DebtRemediationFunction;
+import org.sonar.api.server.debt.DebtRemediationFunction.Type;
+import org.sonar.api.utils.Duration;
+import org.sonar.api.utils.Durations;
+import org.sonar.core.issue.DefaultIssue;
+
+public class DebtCalculator {
+
+ private final RuleRepository ruleRepository;
+ private final Durations durations;
+
+ public DebtCalculator(RuleRepository ruleRepository, Durations durations) {
+ this.ruleRepository = ruleRepository;
+ this.durations = durations;
+ }
+
+ @CheckForNull
+ public Duration calculate(DefaultIssue issue) {
+ Rule rule = ruleRepository.getByKey(issue.ruleKey());
+ DebtRemediationFunction fn = rule.getRemediationFunction();
+ if (fn != null) {
+ verifyEffortToFix(issue, fn);
+
+ Duration debt = Duration.create(0);
+ if (fn.type().usesCoefficient() && !Strings.isNullOrEmpty(fn.coefficient())) {
+ int effortToFixValue = Objects.firstNonNull(issue.effortToFix(), 1).intValue();
+ // TODO convert to Duration directly in Rule#remediationFunction -> better performance + error handling
+ debt = durations.decode(fn.coefficient()).multiply(effortToFixValue);
+ }
+ if (fn.type().usesOffset() && !Strings.isNullOrEmpty(fn.offset())) {
+ // TODO convert to Duration directly in Rule#remediationFunction -> better performance + error handling
+ debt = debt.add(durations.decode(fn.offset()));
+ }
+ return debt;
+ }
+ return null;
+ }
+
+ private static void verifyEffortToFix(DefaultIssue issue, DebtRemediationFunction fn) {
+ if (Type.CONSTANT_ISSUE.equals(fn.type()) && issue.effortToFix() != null) {
+ throw new IllegalArgumentException("Rule '" + issue.getRuleKey() + "' can not use 'Constant/issue' remediation function " +
+ "because this rule does not have a fixed remediation cost.");
+ }
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/DefaultAssignee.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/DefaultAssignee.java
new file mode 100644
index 00000000000..7cdd0a5da73
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/DefaultAssignee.java
@@ -0,0 +1,73 @@
+/*
+ * 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.base.Strings;
+import javax.annotation.CheckForNull;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.server.computation.component.ProjectSettingsRepository;
+import org.sonar.server.computation.component.TreeRootHolder;
+import org.sonar.server.user.index.UserDoc;
+import org.sonar.server.user.index.UserIndex;
+
+/**
+ * The user who is optionally declared as being the assignee
+ * of all the issues which SCM author is not associated with any SonarQube user.
+ */
+public class DefaultAssignee {
+
+ private static final Logger LOG = Loggers.get(DefaultAssignee.class);
+
+ private final TreeRootHolder treeRootHolder;
+ private final UserIndex userIndex;
+ private final ProjectSettingsRepository settings;
+
+ private boolean loaded = false;
+ private String login = null;
+
+ public DefaultAssignee(TreeRootHolder treeRootHolder, UserIndex userIndex, ProjectSettingsRepository settings) {
+ this.treeRootHolder = treeRootHolder;
+ this.userIndex = userIndex;
+ this.settings = settings;
+ }
+
+ @CheckForNull
+ public String getLogin() {
+ if (!loaded) {
+ String configuredLogin = settings.getProjectSettings(treeRootHolder.getRoot().getKey()).getString(CoreProperties.DEFAULT_ISSUE_ASSIGNEE);
+ if (!Strings.isNullOrEmpty(configuredLogin) && isValidLogin(configuredLogin)) {
+ this.login = configuredLogin;
+ }
+ loaded = true;
+ }
+ return login;
+ }
+
+ private boolean isValidLogin(String s) {
+ UserDoc user = userIndex.getNullableByLogin(s);
+ if (user == null) {
+ LOG.info("Property {} is set with an unknown login: {}", CoreProperties.DEFAULT_ISSUE_ASSIGNEE, s);
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/SourceLinesCache.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueAssigner.java
index f8654fefeb8..7f49d983154 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/SourceLinesCache.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueAssigner.java
@@ -27,102 +27,103 @@ import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.apache.commons.lang.StringUtils;
import org.sonar.batch.protocol.output.BatchReport;
-import org.sonar.batch.protocol.output.BatchReport.Changesets.Changeset;
-import org.sonar.batch.protocol.output.BatchReport.Changesets.Changeset.Builder;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.tracking.Tracking;
import org.sonar.server.computation.batch.BatchReportReader;
+import org.sonar.server.computation.component.Component;
import org.sonar.server.source.index.SourceLineDoc;
import org.sonar.server.source.index.SourceLineIndex;
/**
- * Cache of the lines of the currently processed file. Only a <strong>single</strong> file
- * is kept in memory at a time. Data is loaded <strong>on demand</strong> (to avoid non necessary
- * loading).<br />
+ * Detect the SCM author and SQ assignee.
+ * <p/>
* It relies on:
* <ul>
* <li>the SCM information sent in the report for modified files</li>
- * <li>the source line index for non-modified files</li>
+ * <li>the Elasticsearch index of source lines for non-modified files</li>
* </ul>
- *
*/
-public class SourceLinesCache {
-
- private final SourceLineIndex index;
- private BatchReportReader reportReader;
+public class IssueAssigner extends IssueVisitor {
- private boolean loaded = false;
- private BatchReport.Changesets scm;
- private String currentFileUuid;
- private Integer currentFileReportRef;
+ private final SourceLineIndex sourceLineIndex;
+ private final BatchReportReader reportReader;
+ private final DefaultAssignee defaultAssigne;
+ private final ScmAccountToUser scmAccountToUser;
private long lastCommitDate = 0L;
private String lastCommitAuthor = null;
+ private BatchReport.Changesets scmChangesets = null;
- public SourceLinesCache(SourceLineIndex index) {
- this.index = index;
+ public IssueAssigner(SourceLineIndex sourceLineIndex, BatchReportReader reportReader,
+ ScmAccountToUser scmAccountToUser, DefaultAssignee defaultAssigne) {
+ this.sourceLineIndex = sourceLineIndex;
+ this.reportReader = reportReader;
+ this.scmAccountToUser = scmAccountToUser;
+ this.defaultAssigne = defaultAssigne;
}
- /**
- * Marks the currently processed component
- */
- void init(String fileUuid, @Nullable Integer fileReportRef, BatchReportReader reportReader) {
- this.loaded = false;
- this.currentFileUuid = fileUuid;
- this.currentFileReportRef = fileReportRef;
- this.lastCommitDate = 0L;
- this.lastCommitAuthor = null;
- this.reportReader = reportReader;
- clear();
+ @Override
+ public void beforeComponent(Component component, Tracking tracking) {
+ // optimization - do not load data if there are no new issues
+ if (!tracking.getUnmatchedRaws().isEmpty()) {
+ scmChangesets = loadScmChangesetsFromReport(component);
+ if (scmChangesets == null) {
+ scmChangesets = loadScmChangesetsFromIndex(component);
+ }
+ computeLastCommitDateAndAuthor();
+ }
+ }
+
+ @Override
+ public void onIssue(Component component, DefaultIssue issue) {
+ if (issue.isNew()) {
+ String scmAuthor = guessScmAuthor(issue.line());
+ issue.setAuthorLogin(scmAuthor);
+ if (scmAuthor != null) {
+ String assigneeLogin = scmAccountToUser.getNullable(scmAuthor);
+ if (assigneeLogin == null) {
+ issue.setAssignee(defaultAssigne.getLogin());
+ } else {
+ issue.setAssignee(assigneeLogin);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void afterComponent(Component component) {
+ lastCommitDate = 0L;
+ lastCommitAuthor = null;
+ scmChangesets = null;
}
/**
- * Last committer of the line, can be null.
+ * Get the SCM login of the last committer on the line. When line is zero,
+ * then get the last committer on the file.
*/
@CheckForNull
- public String lineAuthor(@Nullable Integer lineNumber) {
- loadIfNeeded();
-
- if (lineNumber == null) {
- // issue on file, approximately estimate that author is the last committer on the file
- return lastCommitAuthor;
- }
+ private String guessScmAuthor(@Nullable Integer line) {
String author = null;
- if (lineNumber <= scm.getChangesetIndexByLineCount()) {
- BatchReport.Changesets.Changeset changeset = scm.getChangeset(scm.getChangesetIndexByLine(lineNumber - 1));
+ if (line != null && line <= scmChangesets.getChangesetIndexByLineCount()) {
+ BatchReport.Changesets.Changeset changeset = scmChangesets.getChangeset(scmChangesets.getChangesetIndexByLine(line - 1));
author = changeset.hasAuthor() ? changeset.getAuthor() : null;
}
-
return StringUtils.defaultIfEmpty(author, lastCommitAuthor);
}
- private void loadIfNeeded() {
- checkState();
-
- if (!loaded) {
- scm = loadScmFromReport();
- loaded = scm != null;
- }
-
- if (!loaded) {
- scm = loadLinesFromIndexAndBuildScm();
- loaded = true;
- }
-
- computeLastCommitDateAndAuthor();
- }
-
- private BatchReport.Changesets loadScmFromReport() {
- return reportReader.readChangesets(currentFileReportRef);
+ private BatchReport.Changesets loadScmChangesetsFromReport(Component component) {
+ return reportReader.readChangesets(component.getRef());
}
- private BatchReport.Changesets loadLinesFromIndexAndBuildScm() {
- List<SourceLineDoc> lines = index.getLines(currentFileUuid);
+ private BatchReport.Changesets loadScmChangesetsFromIndex(Component component) {
+ List<SourceLineDoc> lines = sourceLineIndex.getLines(component.getUuid());
Map<String, BatchReport.Changesets.Changeset> changesetByRevision = new HashMap<>();
BatchReport.Changesets.Builder scmBuilder = BatchReport.Changesets.newBuilder()
- .setComponentRef(currentFileReportRef);
+ .setComponentRef(component.getRef());
for (SourceLineDoc sourceLine : lines) {
String scmRevision = sourceLine.scmRevision();
if (scmRevision == null || changesetByRevision.get(scmRevision) == null) {
- Builder changeSetBuilder = BatchReport.Changesets.Changeset.newBuilder();
+ BatchReport.Changesets.Changeset.Builder changeSetBuilder = BatchReport.Changesets.Changeset.newBuilder();
String scmAuthor = sourceLine.scmAuthor();
if (scmAuthor != null) {
changeSetBuilder.setAuthor(scmAuthor);
@@ -135,7 +136,7 @@ public class SourceLinesCache {
changeSetBuilder.setRevision(scmRevision);
}
- Changeset changeset = changeSetBuilder.build();
+ BatchReport.Changesets.Changeset changeset = changeSetBuilder.build();
scmBuilder.addChangeset(changeset);
scmBuilder.addChangesetIndexByLine(scmBuilder.getChangesetCount() - 1);
if (scmRevision != null) {
@@ -149,24 +150,11 @@ public class SourceLinesCache {
}
private void computeLastCommitDateAndAuthor() {
- for (BatchReport.Changesets.Changeset changeset : scm.getChangesetList()) {
+ for (BatchReport.Changesets.Changeset changeset : scmChangesets.getChangesetList()) {
if (changeset.hasAuthor() && changeset.hasDate() && changeset.getDate() > lastCommitDate) {
lastCommitDate = changeset.getDate();
lastCommitAuthor = changeset.getAuthor();
}
}
}
-
- private void checkState() {
- if (currentFileReportRef == null) {
- throw new IllegalStateException("Report component reference must not be null to use the cache");
- }
- }
-
- /**
- * Makes cache eligible to GC
- */
- public void clear() {
- scm = null;
- }
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueCache.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueCache.java
index 50fab5b12b3..916a68d4118 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueCache.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueCache.java
@@ -19,18 +19,16 @@
*/
package org.sonar.server.computation.issue;
-import org.sonar.api.issue.internal.DefaultIssue;
+import java.io.File;
+import java.io.IOException;
import org.sonar.api.utils.System2;
import org.sonar.api.utils.TempFolder;
+import org.sonar.core.issue.DefaultIssue;
import org.sonar.server.util.cache.DiskCache;
-import java.io.File;
-import java.io.IOException;
-
/**
* Cache of all the issues involved in the analysis. Their state is as it will be
* persisted in database (after issue tracking, auto-assignment, ...)
- *
*/
public class IssueCache extends DiskCache<DefaultIssue> {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueComputation.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueComputation.java
deleted file mode 100644
index 76079a64a85..00000000000
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueComputation.java
+++ /dev/null
@@ -1,166 +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.issue;
-
-import com.google.common.base.Strings;
-import com.google.common.collect.Sets;
-import java.util.Date;
-import javax.annotation.Nullable;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.FieldDiffs;
-import org.sonar.api.rule.RuleKey;
-import org.sonar.api.utils.Duration;
-import org.sonar.api.utils.KeyValueFormat;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-import org.sonar.batch.protocol.output.BatchReport;
-import org.sonar.core.rule.RuleDto;
-import org.sonar.server.computation.batch.BatchReportReader;
-import org.sonar.server.computation.component.ProjectSettingsRepository;
-import org.sonar.server.user.index.UserDoc;
-import org.sonar.server.user.index.UserIndex;
-import org.sonar.server.util.cache.DiskCache;
-
-public class IssueComputation {
-
- private static final Logger LOG = Loggers.get(IssueComputation.class);
-
- private final RuleCache ruleCache;
- private final ScmAccountCache scmAccountCache;
- private final SourceLinesCache linesCache;
- private final DiskCache<DefaultIssue>.DiskAppender diskIssuesAppender;
- private final UserIndex userIndex;
- private final ProjectSettingsRepository projectSettingsRepository;
- private final BatchReportReader reportReader;
- private boolean hasAssigneeBeenComputed = false;
- private String defaultAssignee = null;
-
- public IssueComputation(RuleCache ruleCache, SourceLinesCache linesCache, ScmAccountCache scmAccountCache,
- IssueCache issueCache, UserIndex userIndex, ProjectSettingsRepository projectSettingsRepository, BatchReportReader reportReader) {
- this.ruleCache = ruleCache;
- this.linesCache = linesCache;
- this.scmAccountCache = scmAccountCache;
- this.userIndex = userIndex;
- this.reportReader = reportReader;
- this.projectSettingsRepository = projectSettingsRepository;
- this.diskIssuesAppender = issueCache.newAppender();
- }
-
- public void processComponentIssues(Iterable<BatchReport.Issue> issues, String componentUuid, @Nullable Integer componentReportRef,
- String projectKey, String projectUuid) {
- linesCache.init(componentUuid, componentReportRef, reportReader);
- computeDefaultAssignee(projectSettingsRepository.getProjectSettings(projectKey).getString(CoreProperties.DEFAULT_ISSUE_ASSIGNEE));
- for (BatchReport.Issue reportIssue : issues) {
- DefaultIssue issue = toDefaultIssue(componentUuid, reportIssue, projectKey, projectUuid);
- if (issue.isNew()) {
- guessAuthor(issue);
- autoAssign(issue, defaultAssignee);
- copyRuleTags(issue);
- }
- diskIssuesAppender.append(issue);
- }
- linesCache.clear();
- }
-
- private DefaultIssue toDefaultIssue(String componentUuid, BatchReport.Issue issue, String projectKey, String projectUuid) {
- DefaultIssue target = new DefaultIssue();
- target.setKey(issue.getUuid());
- target.setComponentUuid(componentUuid);
- target.setRuleKey(RuleKey.of(issue.getRuleRepository(), issue.getRuleKey()));
- target.setSeverity(issue.getSeverity().name());
- target.setManualSeverity(issue.getManualSeverity());
- target.setMessage(issue.hasMsg() ? issue.getMsg() : null);
- target.setLine(issue.hasLine() ? issue.getLine() : null);
- target.setProjectUuid(projectUuid);
- target.setProjectKey(projectKey);
- target.setEffortToFix(issue.hasEffortToFix() ? issue.getEffortToFix() : null);
- target.setDebt(issue.hasDebtInMinutes() ? Duration.create(issue.getDebtInMinutes()) : null);
- if (issue.hasDiffFields()) {
- FieldDiffs fieldDiffs = FieldDiffs.parse(issue.getDiffFields());
- fieldDiffs.setCreationDate(new Date(reportReader.readMetadata().getAnalysisDate()));
- target.setCurrentChange(fieldDiffs);
- }
- target.setStatus(issue.getStatus());
- target.setTags(issue.getTagList());
- target.setResolution(issue.hasResolution() ? issue.getResolution() : null);
- target.setReporter(issue.hasReporter() ? issue.getReporter() : null);
- target.setAssignee(issue.hasAssignee() ? issue.getAssignee() : null);
- target.setChecksum(issue.hasChecksum() ? issue.getChecksum() : null);
- target.setAttributes(issue.hasAttributes() ? KeyValueFormat.parse(issue.getAttributes()) : null);
- target.setAuthorLogin(issue.hasAuthorLogin() ? issue.getAuthorLogin() : null);
- target.setActionPlanKey(issue.hasActionPlanKey() ? issue.getActionPlanKey() : null);
- target.setCreationDate(issue.hasCreationDate() ? new Date(issue.getCreationDate()) : null);
- target.setUpdateDate(issue.hasUpdateDate() ? new Date(issue.getUpdateDate()) : null);
- target.setCloseDate(issue.hasCloseDate() ? new Date(issue.getCloseDate()) : null);
- target.setChanged(issue.getIsChanged());
- target.setNew(issue.getIsNew());
- target.setSelectedAt(issue.hasSelectedAt() ? issue.getSelectedAt() : null);
- target.setSendNotifications(issue.getMustSendNotification());
- return target;
- }
-
- public void afterReportProcessing() {
- diskIssuesAppender.close();
- }
-
- private void guessAuthor(DefaultIssue issue) {
- // issue.authorLogin() can be not-null when old developer cockpit plugin (or other plugin)
- // is still installed and executed during analysis
- if (issue.authorLogin() == null) {
- issue.setAuthorLogin(linesCache.lineAuthor(issue.line()));
- }
- }
-
- private void autoAssign(DefaultIssue issue, @Nullable String defaultAssignee) {
- // issue.assignee() can be not-null if the issue-assign-plugin is
- // still installed and executed during analysis
- if (issue.assignee() == null) {
- String scmAccount = issue.authorLogin();
- if (scmAccount != null) {
- issue.setAssignee(scmAccountCache.getNullable(scmAccount));
- }
- if (issue.assignee() == null && defaultAssignee != null) {
- issue.setAssignee(defaultAssignee);
- }
- }
- }
-
- private void copyRuleTags(DefaultIssue issue) {
- RuleDto rule = ruleCache.get(issue.ruleKey());
- issue.setTags(Sets.union(rule.getTags(), rule.getSystemTags()));
- }
-
- private void computeDefaultAssignee(@Nullable String login) {
- if (hasAssigneeBeenComputed) {
- return;
- }
-
- hasAssigneeBeenComputed = true;
- if (!Strings.isNullOrEmpty(login)) {
- UserDoc user = userIndex.getNullableByLogin(login);
- if (user == null) {
- LOG.info("the {} property was set with an unknown login: {}", CoreProperties.DEFAULT_ISSUE_ASSIGNEE, login);
- } else {
- defaultAssignee = login;
- }
- }
- }
-}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueCounter.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueCounter.java
new file mode 100644
index 00000000000..8c27aefac35
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueCounter.java
@@ -0,0 +1,268 @@
+/*
+ * 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 IssueCounter extends IssueVisitor {
+
+ private static final 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 static final 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 IssueCounter(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);
+
+ // 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);
+ }
+ }
+
+ @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) {
+ addMeasuresByStatus(component);
+ addMeasuresBySeverity(component);
+ addMeasuresByPeriod(component);
+ currentCounters = null;
+ }
+
+ 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];
+ for (Period period : periodsHolder.getPeriods()) {
+ Multiset<String> bag = currentCounters.counterForPeriod(period.getIndex()).severityBag;
+ variations[period.getIndex() - 1] = new Double(bag.count(severity));
+ }
+ 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/IssueLifecycle.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueLifecycle.java
new file mode 100644
index 00000000000..31856ec5b6f
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueLifecycle.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;
+
+import java.util.Date;
+import javax.annotation.Nullable;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.utils.internal.Uuids;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.IssueChangeContext;
+import org.sonar.core.issue.IssueUpdater;
+import org.sonar.core.issue.workflow.IssueWorkflow;
+import org.sonar.server.computation.batch.BatchReportReader;
+
+/**
+ * Sets the appropriate fields when an issue is :
+ * <ul>
+ * <li>newly created</li>
+ * <li>merged the related base issue</li>
+ * <li>relocated (only manual issues)</li>
+ * </ul>
+ */
+public class IssueLifecycle {
+
+ private final IssueWorkflow workflow;
+ private final IssueChangeContext changeContext;
+ private final IssueUpdater updater;
+ private final DebtCalculator debtCalculator;
+
+ public IssueLifecycle(BatchReportReader reportReader, IssueWorkflow workflow, IssueUpdater updater, DebtCalculator debtCalculator) {
+ this.workflow = workflow;
+ this.updater = updater;
+ this.debtCalculator = debtCalculator;
+ this.changeContext = IssueChangeContext.createScan(new Date(reportReader.readMetadata().getAnalysisDate()));
+ }
+
+ public void initNewOpenIssue(DefaultIssue issue) {
+ issue.setKey(Uuids.create());
+ issue.setCreationDate(changeContext.date());
+ issue.setUpdateDate(changeContext.date());
+ issue.setStatus(Issue.STATUS_OPEN);
+ issue.setDebt(debtCalculator.calculate(issue));
+ }
+
+ public void mergeExistingOpenIssue(DefaultIssue raw, DefaultIssue base) {
+ raw.setNew(false);
+ raw.setKey(base.key());
+ raw.setCreationDate(base.creationDate());
+ raw.setUpdateDate(base.updateDate());
+ raw.setCloseDate(base.closeDate());
+ raw.setActionPlanKey(base.actionPlanKey());
+ raw.setResolution(base.resolution());
+ raw.setStatus(base.status());
+ raw.setAssignee(base.assignee());
+ raw.setAuthorLogin(base.authorLogin());
+ raw.setTags(base.tags());
+ raw.setDebt(debtCalculator.calculate(raw));
+ raw.setOnDisabledRule(base.isOnDisabledRule());
+ if (base.manualSeverity()) {
+ raw.setManualSeverity(true);
+ raw.setSeverity(base.severity());
+ } else {
+ updater.setPastSeverity(raw, base.severity(), changeContext);
+ }
+
+ // TODO attributes
+
+ // fields coming from raw
+ updater.setPastLine(raw, base.getLine());
+ updater.setPastMessage(raw, base.getMessage(), changeContext);
+ updater.setPastEffortToFix(raw, base.effortToFix(), changeContext);
+ updater.setPastTechnicalDebt(raw, base.debt(), changeContext);
+ raw.setSelectedAt(base.selectedAt());
+ }
+
+ public void moveOpenManualIssue(DefaultIssue manualIssue, @Nullable Integer toLine) {
+ updater.setLine(manualIssue, toLine);
+ }
+
+ public void doAutomaticTransition(DefaultIssue issue) {
+ workflow.doAutomaticTransition(issue, changeContext);
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueVisitor.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueVisitor.java
new file mode 100644
index 00000000000..6b3768dc26b
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueVisitor.java
@@ -0,0 +1,48 @@
+/*
+ * 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 org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.tracking.Tracking;
+import org.sonar.server.computation.component.Component;
+
+public abstract class IssueVisitor {
+
+ /**
+ * 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) {
+
+ }
+
+ /**
+ * This method is called for each issue of a component when tracking is done and issue is initialized.
+ * That means that the following fields are set: resolution, status, line, creation date, uuid
+ * and all the fields merged from base issues.
+ */
+ public void onIssue(Component component, DefaultIssue issue) {
+
+ }
+
+ public void afterComponent(Component component) {
+
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueVisitors.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueVisitors.java
new file mode 100644
index 00000000000..51ddee18a05
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueVisitors.java
@@ -0,0 +1,51 @@
+/*
+ * 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 org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.tracking.Tracking;
+import org.sonar.server.computation.component.Component;
+
+public class IssueVisitors {
+
+ private final IssueVisitor[] visitors;
+
+ public IssueVisitors(IssueVisitor[] visitors) {
+ this.visitors = visitors;
+ }
+
+ public void beforeComponent(Component component, Tracking tracking) {
+ for (IssueVisitor visitor : visitors) {
+ visitor.beforeComponent(component, tracking);
+ }
+ }
+
+ public void onIssue(Component component, DefaultIssue issue) {
+ for (IssueVisitor visitor : visitors) {
+ visitor.onIssue(component, issue);
+ }
+ }
+
+ public void afterComponent(Component component) {
+ for (IssueVisitor visitor : visitors) {
+ visitor.afterComponent(component);
+ }
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/NewDebtAggregator.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/NewDebtAggregator.java
new file mode 100644
index 00000000000..d26b324ff14
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/NewDebtAggregator.java
@@ -0,0 +1,120 @@
+/*
+ * 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.base.Objects;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ListMultimap;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.db.IssueChangeDto;
+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 org.sonar.server.db.DbClient;
+
+public class NewDebtAggregator extends IssueVisitor {
+
+ private final NewDebtCalculator calculator;
+ private final PeriodsHolder periodsHolder;
+ private final DbClient dbClient;
+ private final MetricRepository metricRepository;
+ private final MeasureRepository measureRepository;
+
+ private ListMultimap<String, IssueChangeDto> changesByIssueUuid = ArrayListMultimap.create();
+ private Map<Integer, DebtSum> sumsByComponentRef = new HashMap<>();
+ private DebtSum currentSum = null;
+
+ public NewDebtAggregator(NewDebtCalculator calculator, PeriodsHolder periodsHolder, DbClient dbClient,
+ MetricRepository metricRepository, MeasureRepository measureRepository) {
+ this.calculator = calculator;
+ this.periodsHolder = periodsHolder;
+ this.dbClient = dbClient;
+ this.metricRepository = metricRepository;
+ this.measureRepository = measureRepository;
+ }
+
+ @Override
+ public void beforeComponent(Component component, Tracking tracking) {
+ currentSum = new DebtSum();
+ sumsByComponentRef.put(component.getRef(), currentSum);
+ List<IssueChangeDto> changes = dbClient.issueChangeDao().selectChangelogOfNonClosedIssuesByComponent(component.getUuid());
+ for (IssueChangeDto change : changes) {
+ changesByIssueUuid.put(change.getIssueKey(), change);
+ }
+ for (Component child : component.getChildren()) {
+ DebtSum childSum = sumsByComponentRef.remove(child.getRef());
+ if (childSum != null) {
+ currentSum.add(childSum);
+ }
+ }
+ }
+
+ @Override
+ public void onIssue(Component component, DefaultIssue issue) {
+ if (issue.resolution() == null && issue.debtInMinutes() != null && !periodsHolder.getPeriods().isEmpty()) {
+ List<IssueChangeDto> changelog = changesByIssueUuid.get(issue.key());
+ for (Period period : periodsHolder.getPeriods()) {
+ long newDebt = calculator.calculate(issue, changelog, period);
+ currentSum.add(period.getIndex(), newDebt);
+ }
+ }
+ }
+
+ @Override
+ public void afterComponent(Component component) {
+ if (!currentSum.isEmpty) {
+ MeasureVariations variations = new MeasureVariations(currentSum.sums);
+ Metric metric = metricRepository.getByKey(CoreMetrics.NEW_TECHNICAL_DEBT_KEY);
+ measureRepository.add(component, metric, Measure.newMeasureBuilder().setVariations(variations).createNoValue());
+ }
+ changesByIssueUuid.clear();
+ currentSum = null;
+ }
+
+ private static class DebtSum {
+ private final Double[] sums = new Double[PeriodsHolder.MAX_NUMBER_OF_PERIODS];
+ private boolean isEmpty = true;
+
+ void add(int periodIndex, long newDebt) {
+ double previous = Objects.firstNonNull(sums[periodIndex - 1], 0d);
+ sums[periodIndex - 1] = previous + newDebt;
+ isEmpty = false;
+ }
+
+ void add(DebtSum other) {
+ for (int i = 0; i < sums.length; i++) {
+ Double otherValue = other.sums[i];
+ if (otherValue != null) {
+ add(i + 1, otherValue.longValue());
+ }
+ }
+ }
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/NewDebtCalculator.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/NewDebtCalculator.java
new file mode 100644
index 00000000000..ed7502e7b22
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/NewDebtCalculator.java
@@ -0,0 +1,130 @@
+/*
+ * 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.base.Function;
+import com.google.common.base.Objects;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Ordering;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.apache.commons.lang.time.DateUtils;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.FieldDiffs;
+import org.sonar.core.issue.IssueUpdater;
+import org.sonar.core.issue.db.IssueChangeDto;
+import org.sonar.server.computation.period.Period;
+
+import static com.google.common.collect.FluentIterable.from;
+
+/**
+ * Gets the issue debt that was introduced on a period. The algorithm
+ * is based on the issue changelog.
+ */
+public class NewDebtCalculator {
+
+ public long calculate(DefaultIssue issue, Collection<IssueChangeDto> debtChangelog, Period period) {
+
+ if (issue.creationDate().getTime() > period.getSnapshotDate() + 1000L) {
+ return Objects.firstNonNull(issue.debtInMinutes(), 0L);
+ }
+ return calculateFromChangelog(issue, debtChangelog, period.getSnapshotDate());
+ }
+
+ private long calculateFromChangelog(DefaultIssue issue, Collection<IssueChangeDto> debtChangelog, long periodDate) {
+ List<FieldDiffs> debtDiffs = from(debtChangelog).transform(ToFieldDiffs.INSTANCE).filter(HasDebtChange.INSTANCE).toSortedList(CHANGE_ORDERING);
+ FieldDiffs currentChange = issue.currentChange();
+ if (currentChange != null && HasDebtChange.INSTANCE.apply(currentChange)) {
+ debtDiffs = Lists.newArrayList(debtDiffs);
+ debtDiffs.add(currentChange);
+ }
+ long newDebt = issue.debtInMinutes();
+
+ for (Iterator<FieldDiffs> it = debtDiffs.iterator(); it.hasNext();) {
+ FieldDiffs diffs = it.next();
+ Date date = diffs.creationDate();
+ // TODO use longs
+ if (isBeforeOrEqual(date, new Date(periodDate))) {
+ // return new value from the change that is just before the period date
+ return subtract(newDebt, debtDiff(diffs).newValueLong());
+ }
+ if (!it.hasNext()) {
+ // return old value from the change that is just after the period date when there's no more element in changelog
+ return subtract(newDebt, debtDiff(diffs).oldValueLong());
+ }
+ }
+ // no changelog
+ return 0L;
+ }
+
+ /**
+ * SONAR-5059
+ */
+ @CheckForNull
+ private static long subtract(long newDebt, @Nullable Long with) {
+ if (with != null) {
+ return Math.max(0L, newDebt - with);
+ }
+ return newDebt;
+ }
+
+ private static boolean isBeforeOrEqual(@Nullable Date changeDate, Date periodDate) {
+ return (changeDate != null) && (DateUtils.truncatedCompareTo(changeDate, periodDate, Calendar.SECOND) <= 0);
+ }
+
+ private static FieldDiffs.Diff debtDiff(FieldDiffs diffs) {
+ return diffs.diffs().get(IssueUpdater.TECHNICAL_DEBT);
+ }
+
+ /**
+ * Changelog have to be sorted from newest to oldest.
+ * Null date should be the first as this happen when technical debt has changed since previous analysis.
+ */
+ private static final Comparator<FieldDiffs> CHANGE_ORDERING = Ordering.natural().reverse().nullsFirst().onResultOf(new Function<FieldDiffs, Date>() {
+ @Override
+ public Date apply(@Nonnull FieldDiffs dto) {
+ return dto.creationDate();
+ }
+ });
+
+ private enum ToFieldDiffs implements Function<IssueChangeDto, FieldDiffs> {
+ INSTANCE;
+ @Override
+ public FieldDiffs apply(@Nonnull IssueChangeDto dto) {
+ return dto.toFieldDiffs();
+ }
+ }
+
+ private enum HasDebtChange implements Predicate<FieldDiffs> {
+ INSTANCE;
+ @Override
+ public boolean apply(@Nonnull FieldDiffs diffs) {
+ return diffs.diffs().containsKey(IssueUpdater.TECHNICAL_DEBT);
+ }
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/Rule.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/Rule.java
new file mode 100644
index 00000000000..acf8bd333a5
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/Rule.java
@@ -0,0 +1,53 @@
+/*
+ * 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.Set;
+import javax.annotation.CheckForNull;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rule.RuleStatus;
+import org.sonar.api.server.debt.DebtRemediationFunction;
+
+public interface Rule {
+
+ int getId();
+
+ RuleKey getKey();
+
+ String getName();
+
+ RuleStatus getStatus();
+
+ /**
+ * Is activated in a Quality Profile associated to the project
+ */
+ boolean isActivated();
+
+ /**
+ * Get all tags, whatever system or user tags.
+ */
+ Set<String> getTags();
+
+ @CheckForNull
+ Integer getSubCharacteristicId();
+
+ @CheckForNull
+ DebtRemediationFunction getRemediationFunction();
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/RuleCacheLoader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/RuleCacheLoader.java
index e6439fd5baa..f655769e55d 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/RuleCacheLoader.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/RuleCacheLoader.java
@@ -19,36 +19,46 @@
*/
package org.sonar.server.computation.issue;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
import org.sonar.api.rule.RuleKey;
import org.sonar.core.persistence.DbSession;
import org.sonar.core.persistence.MyBatis;
import org.sonar.core.rule.RuleDto;
+import org.sonar.server.computation.batch.BatchReportReader;
import org.sonar.server.db.DbClient;
import org.sonar.server.util.cache.CacheLoader;
-import java.util.Collection;
-import java.util.Map;
+import static com.google.common.collect.FluentIterable.from;
+import static org.sonar.core.rule.RuleKeyFunctions.stringToRuleKey;
-public class RuleCacheLoader implements CacheLoader<RuleKey, RuleDto> {
+public class RuleCacheLoader implements CacheLoader<RuleKey, Rule> {
private final DbClient dbClient;
+ private final Set<RuleKey> activatedKeys;
- public RuleCacheLoader(DbClient dbClient) {
+ public RuleCacheLoader(DbClient dbClient, BatchReportReader batchReportReader) {
this.dbClient = dbClient;
+ this.activatedKeys = from(batchReportReader.readMetadata().getActiveRuleKeyList()).transform(stringToRuleKey()).toSet();
}
@Override
- public RuleDto load(RuleKey key) {
+ public Rule load(RuleKey key) {
DbSession session = dbClient.openSession(false);
try {
- return dbClient.ruleDao().getNullableByKey(session, key);
+ RuleDto dto = dbClient.ruleDao().getNullableByKey(session, key);
+ if (dto != null) {
+ return new RuleImpl(dto, activatedKeys.contains(key));
+ }
+ return null;
} finally {
MyBatis.closeQuietly(session);
}
}
@Override
- public Map<RuleKey, RuleDto> loadAll(Collection<? extends RuleKey> keys) {
- throw new UnsupportedOperationException("See RuleDao");
+ public Map<RuleKey, Rule> loadAll(Collection<? extends RuleKey> keys) {
+ throw new UnsupportedOperationException("Not implemented yet");
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/RuleImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/RuleImpl.java
new file mode 100644
index 00000000000..07548d5148f
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/RuleImpl.java
@@ -0,0 +1,155 @@
+/*
+ * 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.base.Objects;
+import java.util.Set;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rule.RuleStatus;
+import org.sonar.api.server.debt.DebtRemediationFunction;
+import org.sonar.api.server.debt.internal.DefaultDebtRemediationFunction;
+import org.sonar.core.rule.RuleDto;
+
+import static com.google.common.collect.Sets.union;
+
+@Immutable
+public class RuleImpl implements Rule {
+
+ private final int id;
+ private final RuleKey key;
+ private final String name;
+ private final RuleStatus status;
+ private final boolean isActivated;
+ private final Integer subCharacteristicId;
+ private final Set<String> tags;
+ private final DebtRemediationFunction remediationFunction;
+
+ public RuleImpl(RuleDto dto, boolean isActivated) {
+ this.id = dto.getId();
+ this.key = dto.getKey();
+ this.name = dto.getName();
+ this.status = dto.getStatus();
+ this.subCharacteristicId = effectiveCharacteristicId(dto);
+ this.tags = union(dto.getSystemTags(), dto.getTags());
+ this.remediationFunction = effectiveRemediationFunction(dto);
+ this.isActivated = isActivated;
+ }
+
+ @Override
+ public int getId() {
+ return id;
+ }
+
+ @Override
+ public RuleKey getKey() {
+ return key;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public RuleStatus getStatus() {
+ return status;
+ }
+
+ @Override
+ public boolean isActivated() {
+ return isActivated;
+ }
+
+ @Override
+ public Set<String> getTags() {
+ return tags;
+ }
+
+ @Override
+ public Integer getSubCharacteristicId() {
+ return subCharacteristicId;
+ }
+
+ @Override
+ public DebtRemediationFunction getRemediationFunction() {
+ return remediationFunction;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ RuleImpl rule = (RuleImpl) o;
+ return key.equals(rule.key);
+ }
+
+ @Override
+ public int hashCode() {
+ return key.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return Objects.toStringHelper(this)
+ .add("id", id)
+ .add("key", key)
+ .add("name", name)
+ .add("status", status)
+ .add("isActivated", isActivated)
+ .add("subCharacteristicId", subCharacteristicId)
+ .add("tags", tags)
+ .toString();
+ }
+
+ @CheckForNull
+ private static Integer effectiveCharacteristicId(RuleDto dto) {
+ if (isEnabledCharacteristicId(dto.getSubCharacteristicId())) {
+ return dto.getSubCharacteristicId();
+ }
+ if (isEnabledCharacteristicId(dto.getDefaultSubCharacteristicId())) {
+ return dto.getDefaultSubCharacteristicId();
+ }
+ return null;
+ }
+
+ private static boolean isEnabledCharacteristicId(@Nullable Integer id) {
+ return (id != null) && (id.intValue() != RuleDto.DISABLED_CHARACTERISTIC_ID);
+ }
+
+ @CheckForNull
+ private static DebtRemediationFunction effectiveRemediationFunction(RuleDto dto) {
+ String fn = dto.getRemediationFunction();
+ if (fn != null) {
+ return new DefaultDebtRemediationFunction(DebtRemediationFunction.Type.valueOf(fn), dto.getRemediationCoefficient(), dto.getRemediationOffset());
+ }
+ String defaultFn = dto.getDefaultRemediationFunction();
+ if (defaultFn != null) {
+ return new DefaultDebtRemediationFunction(DebtRemediationFunction.Type.valueOf(defaultFn), dto.getDefaultRemediationCoefficient(), dto.getDefaultRemediationOffset());
+ }
+ return null;
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/RuleRepository.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/RuleRepository.java
new file mode 100644
index 00000000000..3b86d2c24e5
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/RuleRepository.java
@@ -0,0 +1,28 @@
+/*
+ * 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 org.sonar.api.rule.RuleKey;
+
+public interface RuleRepository {
+
+ Rule getByKey(RuleKey key);
+
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/RuleCache.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/RuleRepositoryImpl.java
index 6b6a07a681d..ee3314cee5a 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/RuleCache.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/RuleRepositoryImpl.java
@@ -20,23 +20,18 @@
package org.sonar.server.computation.issue;
import org.sonar.api.rule.RuleKey;
-import org.sonar.core.rule.RuleDto;
import org.sonar.server.util.cache.MemoryCache;
-import javax.annotation.CheckForNull;
+public class RuleRepositoryImpl implements RuleRepository {
-/**
- * Cache of the rules involved during the current analysis
- */
-public class RuleCache extends MemoryCache<RuleKey, RuleDto> {
+ private final MemoryCache<RuleKey, Rule> cache;
- public RuleCache(RuleCacheLoader loader) {
- super(loader);
+ public RuleRepositoryImpl(RuleCacheLoader cacheLoader) {
+ this.cache = new MemoryCache<>(cacheLoader);
}
- @CheckForNull
- public String ruleName(RuleKey key) {
- RuleDto rule = getNullable(key);
- return rule != null ? rule.getName() : null;
+ @Override
+ public Rule getByKey(RuleKey key) {
+ return cache.get(key);
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/RuleTagsCopier.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/RuleTagsCopier.java
new file mode 100644
index 00000000000..c44f4710b9c
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/RuleTagsCopier.java
@@ -0,0 +1,43 @@
+/*
+ * 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 org.sonar.core.issue.DefaultIssue;
+import org.sonar.server.computation.component.Component;
+
+import static com.google.common.collect.Sets.union;
+
+public class RuleTagsCopier extends IssueVisitor {
+
+ private final RuleRepository ruleRepository;
+
+ public RuleTagsCopier(RuleRepository ruleRepository) {
+ this.ruleRepository = ruleRepository;
+ }
+
+ @Override
+ public void onIssue(Component component, DefaultIssue issue) {
+ if (issue.isNew()) {
+ // analyzer can provide some tags. They must be merged with rule tags
+ Rule rule = ruleRepository.getByKey(issue.ruleKey());
+ issue.setTags(union(issue.tags(), rule.getTags()));
+ }
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/ScmAccountCache.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/ScmAccountToUser.java
index 1170c2dd6a4..53babcf7bc1 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/ScmAccountCache.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/ScmAccountToUser.java
@@ -22,10 +22,11 @@ package org.sonar.server.computation.issue;
import org.sonar.server.util.cache.MemoryCache;
/**
- * Cache of dictionary {SCM account -> SQ user login}
+ * Cache of dictionary {SCM account -> SQ user login}. Kept in memory
+ * during the execution of Compute Engine.
*/
-public class ScmAccountCache extends MemoryCache<String,String> {
- public ScmAccountCache(ScmAccountCacheLoader loader) {
+public class ScmAccountToUser extends MemoryCache<String,String> {
+ public ScmAccountToUser(ScmAccountToUserLoader loader) {
super(loader);
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/ScmAccountCacheLoader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/ScmAccountToUserLoader.java
index 2d51c5b5526..6eb42c29ba8 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/ScmAccountCacheLoader.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/ScmAccountToUserLoader.java
@@ -19,35 +19,29 @@
*/
package org.sonar.server.computation.issue;
-import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.collect.Collections2;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.server.user.index.UserDoc;
import org.sonar.server.user.index.UserIndex;
import org.sonar.server.util.cache.CacheLoader;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
/**
* Loads the association between a SCM account and a SQ user
*/
-public class ScmAccountCacheLoader implements CacheLoader<String, String> {
+public class ScmAccountToUserLoader implements CacheLoader<String, String> {
- private final Logger log;
+ private static final Logger log = Loggers.get(ScmAccountToUserLoader.class);
private final UserIndex index;
- public ScmAccountCacheLoader(UserIndex index) {
- this(index, Loggers.get(ScmAccountCacheLoader.class));
- }
-
- @VisibleForTesting
- ScmAccountCacheLoader(UserIndex index, Logger log) {
- this.log = log;
+ public ScmAccountToUserLoader(UserIndex index) {
this.index = index;
}
@@ -60,12 +54,7 @@ public class ScmAccountCacheLoader implements CacheLoader<String, String> {
if (!users.isEmpty()) {
// multiple users are associated to the same SCM account, for example
// the same email
- Collection<String> logins = Collections2.transform(users, new Function<UserDoc, String>() {
- @Override
- public String apply(UserDoc input) {
- return input.login();
- }
- });
+ Collection<String> logins = Collections2.transform(users, UserDocToLogin.INSTANCE);
log.warn(String.format("Multiple users share the SCM account '%s': %s", scmAccount, Joiner.on(", ").join(logins)));
}
return null;
@@ -75,4 +64,13 @@ public class ScmAccountCacheLoader implements CacheLoader<String, String> {
public Map<String, String> loadAll(Collection<? extends String> scmAccounts) {
throw new UnsupportedOperationException("Loading by multiple scm accounts is not supported yet");
}
+
+ private enum UserDocToLogin implements Function<UserDoc, String> {
+ INSTANCE;
+ @Nullable
+ @Override
+ public String apply(@Nonnull UserDoc user) {
+ return user.login();
+ }
+ }
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/TrackerBaseInputFactory.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/TrackerBaseInputFactory.java
new file mode 100644
index 00000000000..8f5feb2573c
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/TrackerBaseInputFactory.java
@@ -0,0 +1,72 @@
+/*
+ * 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.List;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.tracking.Input;
+import org.sonar.core.issue.tracking.LazyInput;
+import org.sonar.core.issue.tracking.LineHashSequence;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.persistence.MyBatis;
+import org.sonar.server.computation.component.Component;
+import org.sonar.server.db.DbClient;
+
+/**
+ * Factory of {@link Input} of base data for issue tracking. Data are lazy-loaded.
+ */
+public class TrackerBaseInputFactory {
+
+ private final BaseIssuesLoader baseIssuesLoader;
+ private final DbClient dbClient;
+
+ public TrackerBaseInputFactory(BaseIssuesLoader baseIssuesLoader, DbClient dbClient) {
+ this.baseIssuesLoader = baseIssuesLoader;
+ this.dbClient = dbClient;
+ }
+
+ public Input<DefaultIssue> create(Component component) {
+ return new BaseLazyInput(component);
+ }
+
+ private class BaseLazyInput extends LazyInput<DefaultIssue> {
+ private final Component component;
+
+ private BaseLazyInput(Component component) {
+ this.component = component;
+ }
+
+ @Override
+ protected LineHashSequence loadLineHashSequence() {
+ DbSession session = dbClient.openSession(false);
+ try {
+ List<String> hashes = dbClient.fileSourceDao().selectLineHashes(session, component.getUuid());
+ return new LineHashSequence(hashes);
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
+
+ @Override
+ protected List<DefaultIssue> loadIssues() {
+ return baseIssuesLoader.loadForComponentUuid(component.getUuid());
+ }
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/TrackerExecution.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/TrackerExecution.java
new file mode 100644
index 00000000000..86e0f7c8417
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/TrackerExecution.java
@@ -0,0 +1,43 @@
+/*
+ * 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 org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.tracking.Tracker;
+import org.sonar.core.issue.tracking.Tracking;
+import org.sonar.server.computation.component.Component;
+
+public class TrackerExecution {
+
+ private final TrackerBaseInputFactory baseInputFactory;
+ private final TrackerRawInputFactory rawInputFactory;
+ private final Tracker<DefaultIssue, DefaultIssue> tracker;
+
+ public TrackerExecution(TrackerBaseInputFactory baseInputFactory, TrackerRawInputFactory rawInputFactory,
+ Tracker<DefaultIssue, DefaultIssue> tracker) {
+ this.baseInputFactory = baseInputFactory;
+ this.rawInputFactory = rawInputFactory;
+ this.tracker = tracker;
+ }
+
+ public Tracking<DefaultIssue, DefaultIssue> track(Component component) {
+ return tracker.track(rawInputFactory.create(component), baseInputFactory.create(component));
+ }
+}
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
new file mode 100644
index 00000000000..6f7cc4640cf
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/TrackerRawInputFactory.java
@@ -0,0 +1,118 @@
+/*
+ * 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.Lists;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.Collections;
+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.batch.protocol.output.BatchReport;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.tracking.Input;
+import org.sonar.core.issue.tracking.LazyInput;
+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;
+
+public class TrackerRawInputFactory {
+
+ private final TreeRootHolder treeRootHolder;
+ private final BatchReportReader reportReader;
+
+ public TrackerRawInputFactory(TreeRootHolder treeRootHolder, BatchReportReader reportReader) {
+ this.treeRootHolder = treeRootHolder;
+ this.reportReader = reportReader;
+ }
+
+ public Input<DefaultIssue> create(Component component) {
+ return new RawLazyInput(component);
+ }
+
+ private class RawLazyInput extends LazyInput<DefaultIssue> {
+ private final Component component;
+
+ private RawLazyInput(Component component) {
+ this.component = component;
+ }
+
+ @Override
+ protected LineHashSequence loadLineHashSequence() {
+ Iterable<String> lines;
+ if (component.getType() == Component.Type.FILE) {
+ lines = Lists.newArrayList(reportReader.readFileSource(component.getRef()));
+ } else {
+ lines = Collections.emptyList();
+ }
+ return LineHashSequence.createForLines(lines);
+ }
+
+ @Override
+ protected List<DefaultIssue> loadIssues() {
+ List<BatchReport.Issue> reportIssues = reportReader.readComponentIssues(component.getRef());
+ List<DefaultIssue> issues = new ArrayList<>();
+ 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);
+ }
+ }
+ return issues;
+ }
+
+ private DefaultIssue toIssue(LineHashSequence lineHashSeq, BatchReport.Issue reportIssue) {
+ DefaultIssue issue = new DefaultIssue();
+ 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()));
+ } else {
+ issue.setChecksum("");
+ }
+ if (reportIssue.hasMsg()) {
+ issue.setMessage(reportIssue.getMsg());
+ }
+ if (reportIssue.hasSeverity()) {
+ issue.setSeverity(reportIssue.getSeverity().name());
+ }
+ if (reportIssue.hasEffortToFix()) {
+ issue.setEffortToFix(reportIssue.getEffortToFix());
+ }
+ issue.setTags(Sets.newHashSet(reportIssue.getTagList()));
+ if (reportIssue.hasAttributes()) {
+ issue.setAttributes(KeyValueFormat.parse(reportIssue.getAttributes()));
+ }
+ return issue;
+ }
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/measure/BatchMeasureToMeasure.java b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/BatchMeasureToMeasure.java
index bdaeae33186..41a4e49cb77 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/measure/BatchMeasureToMeasure.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/BatchMeasureToMeasure.java
@@ -22,20 +22,12 @@ package org.sonar.server.computation.measure;
import com.google.common.base.Optional;
import java.util.Objects;
import javax.annotation.Nullable;
-import org.sonar.api.rule.RuleKey;
import org.sonar.batch.protocol.output.BatchReport;
-import org.sonar.core.rule.RuleDto;
-import org.sonar.server.computation.issue.RuleCache;
import org.sonar.server.computation.metric.Metric;
import static com.google.common.base.Optional.of;
public class BatchMeasureToMeasure {
- private final RuleCache ruleCache;
-
- public BatchMeasureToMeasure(RuleCache ruleCache) {
- this.ruleCache = ruleCache;
- }
public Optional<Measure> toMeasure(@Nullable BatchReport.Measure batchMeasure, Metric metric) {
Objects.requireNonNull(metric);
@@ -43,7 +35,7 @@ public class BatchMeasureToMeasure {
return Optional.absent();
}
- Measure.NewMeasureBuilder builder = createBuilder(batchMeasure);
+ Measure.NewMeasureBuilder builder = Measure.newMeasureBuilder();
String data = batchMeasure.hasStringValue() ? batchMeasure.getStringValue() : null;
switch (metric.getType().getValueType()) {
case INT:
@@ -65,20 +57,6 @@ public class BatchMeasureToMeasure {
}
}
- private Measure.NewMeasureBuilder createBuilder(BatchReport.Measure batchMeasure) {
- if (batchMeasure.hasCharactericId() && batchMeasure.hasRuleKey()) {
- throw new IllegalArgumentException("Measure with both characteristicId and ruleKey are not supported");
- }
- if (batchMeasure.hasCharactericId()) {
- return Measure.newMeasureBuilder().forCharacteristic(batchMeasure.getCharactericId());
- }
- if (batchMeasure.hasRuleKey()) {
- RuleDto ruleDto = ruleCache.get(RuleKey.parse(batchMeasure.getRuleKey()));
- return Measure.newMeasureBuilder().forRule(ruleDto.getId());
- }
- return Measure.newMeasureBuilder();
- }
-
private static Optional<Measure> toIntegerMeasure(Measure.NewMeasureBuilder builder, BatchReport.Measure batchMeasure, @Nullable String data) {
if (!batchMeasure.hasIntValue()) {
return toNoValueMeasure(builder, batchMeasure);
@@ -113,7 +91,7 @@ public class BatchMeasureToMeasure {
}
return of(setCommonProperties(builder, batchMeasure).create(batchMeasure.getStringValue()));
}
-
+
private static Optional<Measure> toLevelMeasure(Measure.NewMeasureBuilder builder, BatchReport.Measure batchMeasure) {
if (!batchMeasure.hasStringValue()) {
return toNoValueMeasure(builder, batchMeasure);
@@ -130,14 +108,7 @@ public class BatchMeasureToMeasure {
}
private static Measure.NewMeasureBuilder setCommonProperties(Measure.NewMeasureBuilder builder, BatchReport.Measure batchMeasure) {
- if (batchMeasure.hasAlertStatus()) {
- Optional<Measure.Level> qualityGateStatus = Measure.Level.toLevel(batchMeasure.getAlertStatus());
- if (qualityGateStatus.isPresent()) {
- String text = batchMeasure.hasAlertText() ? batchMeasure.getAlertText() : null;
- builder.setQualityGateStatus(new QualityGateStatus(qualityGateStatus.get(), text));
- }
- }
- if (hasAnyVariation(batchMeasure)) {
+ if (hasAnyVariation(batchMeasure)) {
builder.setVariations(createVariations(batchMeasure));
}
return builder;
@@ -145,20 +116,19 @@ public class BatchMeasureToMeasure {
private static boolean hasAnyVariation(BatchReport.Measure batchMeasure) {
return batchMeasure.hasVariationValue1()
- || batchMeasure.hasVariationValue2()
- || batchMeasure.hasVariationValue3()
- || batchMeasure.hasVariationValue4()
- || batchMeasure.hasVariationValue5();
+ || batchMeasure.hasVariationValue2()
+ || batchMeasure.hasVariationValue3()
+ || batchMeasure.hasVariationValue4()
+ || batchMeasure.hasVariationValue5();
}
private static MeasureVariations createVariations(BatchReport.Measure batchMeasure) {
return new MeasureVariations(
- batchMeasure.hasVariationValue1() ? batchMeasure.getVariationValue1() : null,
- batchMeasure.hasVariationValue2() ? batchMeasure.getVariationValue2() : null,
- batchMeasure.hasVariationValue3() ? batchMeasure.getVariationValue3() : null,
- batchMeasure.hasVariationValue4() ? batchMeasure.getVariationValue4() : null,
- batchMeasure.hasVariationValue5() ? batchMeasure.getVariationValue5() : null
- );
+ batchMeasure.hasVariationValue1() ? batchMeasure.getVariationValue1() : null,
+ batchMeasure.hasVariationValue2() ? batchMeasure.getVariationValue2() : null,
+ batchMeasure.hasVariationValue3() ? batchMeasure.getVariationValue3() : null,
+ batchMeasure.hasVariationValue4() ? batchMeasure.getVariationValue4() : null,
+ batchMeasure.hasVariationValue5() ? batchMeasure.getVariationValue5() : null);
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/measure/MeasureRepositoryImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/MeasureRepositoryImpl.java
index 26a486c58bd..8cdb33bcfb5 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/measure/MeasureRepositoryImpl.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/measure/MeasureRepositoryImpl.java
@@ -36,7 +36,6 @@ import org.sonar.core.rule.RuleDto;
import org.sonar.server.computation.batch.BatchReportReader;
import org.sonar.server.computation.component.Component;
import org.sonar.server.computation.debt.Characteristic;
-import org.sonar.server.computation.issue.RuleCache;
import org.sonar.server.computation.metric.Metric;
import org.sonar.server.computation.metric.MetricRepository;
import org.sonar.server.db.DbClient;
@@ -55,10 +54,10 @@ public class MeasureRepositoryImpl implements MeasureRepository {
private final Map<Integer, Map<MeasureKey, Measure>> measures = new HashMap<>();
public MeasureRepositoryImpl(DbClient dbClient, BatchReportReader reportReader,
- final MetricRepository metricRepository, final RuleCache ruleCache) {
+ MetricRepository metricRepository) {
this.dbClient = dbClient;
this.reportReader = reportReader;
- this.batchMeasureToMeasure = new BatchMeasureToMeasure(ruleCache);
+ this.batchMeasureToMeasure = new BatchMeasureToMeasure();
this.metricRepository = metricRepository;
}
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..8c4cb7c8f8f 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");
+ 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 f22c71b0d5b..1d7f0e161e5 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
@@ -46,17 +46,16 @@ public class ComputationSteps {
FeedDebtModelStep.class,
- // Read report
- ParseReportStep.class,
-
// load project related stuffs
QualityGateLoadingStep.class,
FeedPeriodsStep.class,
// data computation
- ComputeFormulaMeasuresStep.class,
+ IntegrateIssuesStep.class,
CustomMeasuresCopyStep.class,
- ComputeIssueMeasuresStep.class,
+ ComputeFormulaMeasuresStep.class,
+
+ // SQALE measures depend on issues
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/step/FeedDebtModelStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/FeedDebtModelStep.java
index ca491bc53c7..80e7899eb5e 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/FeedDebtModelStep.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/FeedDebtModelStep.java
@@ -22,8 +22,6 @@ package org.sonar.server.computation.step;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
-import com.google.common.base.Predicates;
-import com.google.common.collect.FluentIterable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -35,6 +33,9 @@ import org.sonar.server.computation.debt.Characteristic;
import org.sonar.server.computation.debt.MutableDebtModelHolder;
import org.sonar.server.db.DbClient;
+import static com.google.common.base.Predicates.not;
+import static com.google.common.collect.FluentIterable.from;
+
/**
* Populates the {@link org.sonar.server.computation.debt.DebtModelHolder}
*/
@@ -60,23 +61,23 @@ public class FeedDebtModelStep implements ComputationStep {
private void feedDebtModel(DbSession session) {
List<CharacteristicDto> characteristicDtos = dbClient.debtCharacteristicDao().selectEnabledCharacteristics(session);
- Map<Integer, CharacteristicDto> rootCharacteristicsById = FluentIterable.from(characteristicDtos)
+ Map<Integer, CharacteristicDto> rootCharacteristicsById = from(characteristicDtos)
.filter(IsRootPredicate.INSTANCE)
.uniqueIndex(CharacteristicDtoToId.INSTANCE);
- for (Map.Entry<Integer, Collection<CharacteristicDto>> entry : FluentIterable.from(characteristicDtos)
- .filter(Predicates.not(IsRootPredicate.INSTANCE))
+ for (Map.Entry<Integer, Collection<CharacteristicDto>> entry : from(characteristicDtos)
+ .filter(not(IsRootPredicate.INSTANCE))
.index(CharacteristicDtoToParentId.INSTANCE)
.asMap().entrySet()) {
mutableDebtModelHolder.addCharacteristics(
toCharacteristic(rootCharacteristicsById.get(entry.getKey())),
- FluentIterable.from(entry.getValue()).transform(CharacteristicDtoToCharacteristic.INSTANCE)
+ from(entry.getValue()).transform(CharacteristicDtoToCharacteristic.INSTANCE)
);
}
}
private static Characteristic toCharacteristic(CharacteristicDto dto) {
- return new Characteristic(dto.getId(), dto.getKey());
+ return new Characteristic(dto.getId(), dto.getKey(), dto.getParentId());
}
private enum CharacteristicDtoToId implements Function<CharacteristicDto, Integer> {
@@ -84,8 +85,8 @@ public class FeedDebtModelStep implements ComputationStep {
@Nullable
@Override
- public Integer apply(@Nonnull CharacteristicDto characteristicDto) {
- return characteristicDto.getId();
+ public Integer apply(@Nonnull CharacteristicDto dto) {
+ return dto.getId();
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/IntegrateIssuesStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/IntegrateIssuesStep.java
new file mode 100644
index 00000000000..c1c234d32e2
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/IntegrateIssuesStep.java
@@ -0,0 +1,153 @@
+/*
+ * 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.collect.Sets;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.sonar.api.utils.log.Loggers;
+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.component.DepthTraversalTypeAwareVisitor;
+import org.sonar.server.computation.component.TreeRootHolder;
+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.IssueVisitors;
+import org.sonar.server.computation.issue.TrackerExecution;
+import org.sonar.server.util.cache.DiskCache;
+
+import static org.sonar.server.computation.component.ComponentVisitor.Order.POST_ORDER;
+
+public class IntegrateIssuesStep implements ComputationStep {
+
+ private final TreeRootHolder treeRootHolder;
+ private final TrackerExecution tracker;
+ private final IssueCache issueCache;
+ private final BaseIssuesLoader baseIssuesLoader;
+ private final IssueLifecycle issueLifecycle;
+ private final IssueVisitors issueVisitors;
+
+ public IntegrateIssuesStep(TreeRootHolder treeRootHolder, TrackerExecution tracker, IssueCache issueCache,
+ BaseIssuesLoader baseIssuesLoader, IssueLifecycle issueLifecycle,
+ IssueVisitors issueVisitors) {
+ this.treeRootHolder = treeRootHolder;
+ this.tracker = tracker;
+ this.issueCache = issueCache;
+ this.baseIssuesLoader = baseIssuesLoader;
+ this.issueLifecycle = issueLifecycle;
+ this.issueVisitors = issueVisitors;
+ }
+
+ @Override
+ public void execute() {
+ // all the components that had issues before this analysis
+ final Set<String> unprocessedComponentUuids = Sets.newHashSet(baseIssuesLoader.loadUuidsOfComponentsWithOpenIssues());
+
+ new DepthTraversalTypeAwareVisitor(Component.Type.FILE, POST_ORDER) {
+ @Override
+ public void visitAny(Component component) {
+ processIssues(component);
+ unprocessedComponentUuids.remove(component.getUuid());
+ }
+ }.visit(treeRootHolder.getRoot());
+
+ closeIssuesForDeletedComponentUuids(unprocessedComponentUuids);
+ }
+
+ private void processIssues(Component component) {
+ Tracking<DefaultIssue, DefaultIssue> tracking = tracker.track(component);
+ DiskCache<DefaultIssue>.DiskAppender cacheAppender = issueCache.newAppender();
+ try {
+ issueVisitors.beforeComponent(component, tracking);
+ fillNewOpenIssues(component, tracking, cacheAppender);
+ fillExistingOpenIssues(component, tracking, cacheAppender);
+ closeUnmatchedBaseIssues(component, tracking, cacheAppender);
+ issueVisitors.afterComponent(component);
+ } finally {
+ cacheAppender.close();
+ }
+ }
+
+ private void fillNewOpenIssues(Component component, Tracking<DefaultIssue, DefaultIssue> tracking, DiskCache<DefaultIssue>.DiskAppender cacheAppender) {
+ Set<DefaultIssue> issues = tracking.getUnmatchedRaws();
+ for (DefaultIssue issue : issues) {
+ issueLifecycle.initNewOpenIssue(issue);
+ process(component, issue, cacheAppender);
+ }
+ }
+
+ private void fillExistingOpenIssues(Component component, Tracking<DefaultIssue, DefaultIssue> tracking, DiskCache<DefaultIssue>.DiskAppender cacheAppender) {
+ for (Map.Entry<DefaultIssue, DefaultIssue> entry : tracking.getMatchedRaws().entrySet()) {
+ DefaultIssue raw = entry.getKey();
+ DefaultIssue base = entry.getValue();
+ issueLifecycle.mergeExistingOpenIssue(raw, base);
+ process(component, raw, cacheAppender);
+ }
+ for (Map.Entry<Integer, DefaultIssue> entry : tracking.getOpenManualIssuesByLine().entries()) {
+ Integer line = entry.getKey();
+ DefaultIssue manualIssue = entry.getValue();
+ issueLifecycle.moveOpenManualIssue(manualIssue, line);
+ process(component, manualIssue, cacheAppender);
+ }
+ }
+
+ private void closeUnmatchedBaseIssues(Component component, Tracking<DefaultIssue, DefaultIssue> tracking, DiskCache<DefaultIssue>.DiskAppender cacheAppender) {
+ for (DefaultIssue issue : tracking.getUnmatchedBases()) {
+ Loggers.get(getClass()).info("--- close base " + issue);
+ // TODO should replace flag "beingClosed" by express call to transition "automaticClose"
+ issue.setBeingClosed(true);
+ // TODO manual issues -> was updater.setResolution(newIssue, Issue.RESOLUTION_REMOVED, changeContext);. Is it a problem ?
+ process(component, issue, cacheAppender);
+ }
+ }
+
+ private void process(Component component, DefaultIssue issue, DiskCache<DefaultIssue>.DiskAppender cacheAppender) {
+ issueLifecycle.doAutomaticTransition(issue);
+ issueVisitors.onIssue(component, issue);
+ cacheAppender.append(issue);
+ }
+
+ private void closeIssuesForDeletedComponentUuids(Set<String> deletedComponentUuids) {
+ DiskCache<DefaultIssue>.DiskAppender cacheAppender = issueCache.newAppender();
+ try {
+ for (String deletedComponentUuid : deletedComponentUuids) {
+ List<DefaultIssue> issues = baseIssuesLoader.loadForComponentUuid(deletedComponentUuid);
+ for (DefaultIssue issue : issues) {
+ issue.setBeingClosed(true);
+ // TODO should be renamed
+ issue.setOnDisabledRule(false);
+ issueLifecycle.doAutomaticTransition(issue);
+ cacheAppender.append(issue);
+ }
+ }
+ } finally {
+ cacheAppender.close();
+ }
+ }
+
+ @Override
+ public String getDescription() {
+ return "Integrate issues";
+ }
+
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ParseReportStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/ParseReportStep.java
deleted file mode 100644
index 8170712bc6b..00000000000
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/ParseReportStep.java
+++ /dev/null
@@ -1,100 +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.List;
-import org.sonar.batch.protocol.output.BatchReport;
-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.issue.IssueComputation;
-
-public class ParseReportStep implements ComputationStep {
-
- private final IssueComputation issueComputation;
- private final BatchReportReader reportReader;
- private final TreeRootHolder treeRootHolder;
-
- public ParseReportStep(IssueComputation issueComputation, BatchReportReader reportReader, TreeRootHolder treeRootHolder) {
- this.issueComputation = issueComputation;
- this.reportReader = reportReader;
- this.treeRootHolder = treeRootHolder;
- }
-
- @Override
- public void execute() {
- IssueDepthTraversalTypeAwareVisitor visitor = new IssueDepthTraversalTypeAwareVisitor();
- visitor.visit(treeRootHolder.getRoot());
- processDeletedComponents(visitor);
- issueComputation.afterReportProcessing();
- }
-
- private void processDeletedComponents(IssueDepthTraversalTypeAwareVisitor visitor) {
- int deletedComponentsCount = reportReader.readMetadata().getDeletedComponentsCount();
- for (int componentRef = 1; componentRef <= deletedComponentsCount; componentRef++) {
- BatchReport.Issues issues = reportReader.readDeletedComponentIssues(componentRef);
- issueComputation.processComponentIssues(issues.getIssueList(), issues.getComponentUuid(), null, visitor.projectKey, visitor.projectUuid);
- }
- }
-
- @Override
- public String getDescription() {
- return "Digest analysis report";
- }
-
- private class IssueDepthTraversalTypeAwareVisitor extends DepthTraversalTypeAwareVisitor {
- private String projectKey;
- private String projectUuid;
-
- public IssueDepthTraversalTypeAwareVisitor() {
- super(Component.Type.FILE, Order.PRE_ORDER);
- }
-
- @Override
- public void visitProject(Component tree) {
- projectKey = tree.getKey();
- projectUuid = tree.getUuid();
- executeForComponent(tree);
- }
-
- @Override
- public void visitModule(Component module) {
- executeForComponent(module);
- }
-
- @Override
- public void visitDirectory(Component directory) {
- executeForComponent(directory);
- }
-
- @Override
- public void visitFile(Component file) {
- executeForComponent(file);
- }
-
- private void executeForComponent(Component component) {
- int componentRef = component.getRef();
- List<BatchReport.Issue> issues = reportReader.readComponentIssues(componentRef);
- issueComputation.processComponentIssues(issues, component.getUuid(), componentRef, projectKey, projectUuid);
- }
- }
-}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistIssuesStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistIssuesStep.java
index df107b009c2..6ffda565d47 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistIssuesStep.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistIssuesStep.java
@@ -19,12 +19,11 @@
*/
package org.sonar.server.computation.step;
-import org.sonar.api.issue.Issue;
import org.sonar.api.issue.IssueComment;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.DefaultIssueComment;
-import org.sonar.api.issue.internal.FieldDiffs;
import org.sonar.api.utils.System2;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.DefaultIssueComment;
+import org.sonar.core.issue.FieldDiffs;
import org.sonar.core.issue.db.IssueChangeDto;
import org.sonar.core.issue.db.IssueChangeMapper;
import org.sonar.core.issue.db.IssueDto;
@@ -33,7 +32,7 @@ import org.sonar.core.issue.db.UpdateConflictResolver;
import org.sonar.core.persistence.DbSession;
import org.sonar.core.persistence.MyBatis;
import org.sonar.server.computation.issue.IssueCache;
-import org.sonar.server.computation.issue.RuleCache;
+import org.sonar.server.computation.issue.RuleRepository;
import org.sonar.server.db.DbClient;
import org.sonar.server.util.CloseableIterator;
@@ -42,15 +41,15 @@ public class PersistIssuesStep implements ComputationStep {
private final DbClient dbClient;
private final System2 system2;
private final UpdateConflictResolver conflictResolver;
- private final RuleCache ruleCache;
+ private final RuleRepository ruleRepository;
private final IssueCache issueCache;
public PersistIssuesStep(DbClient dbClient, System2 system2, UpdateConflictResolver conflictResolver,
- RuleCache ruleCache, IssueCache issueCache) {
+ RuleRepository ruleRepository, IssueCache issueCache) {
this.dbClient = dbClient;
this.system2 = system2;
this.conflictResolver = conflictResolver;
- this.ruleCache = ruleCache;
+ this.ruleRepository = ruleRepository;
this.issueCache = issueCache;
}
@@ -66,21 +65,17 @@ public class PersistIssuesStep implements ComputationStep {
DefaultIssue issue = issues.next();
boolean saved = false;
if (issue.isNew()) {
- Integer ruleId = ruleCache.get(issue.ruleKey()).getId();
- mapper.insert(IssueDto.toDtoForComputationInsert(issue, ruleId, system2.now()));
+ Integer ruleId = ruleRepository.getByKey(issue.ruleKey()).getId();
+ IssueDto dto = IssueDto.toDtoForComputationInsert(issue, ruleId, system2.now());
+ mapper.insert(dto);
saved = true;
} else if (issue.isChanged()) {
IssueDto dto = IssueDto.toDtoForUpdate(issue, system2.now());
- if (Issue.STATUS_CLOSED.equals(issue.status()) || issue.selectedAt() == null) {
- // Issue is closed by scan or changed by end-user
- mapper.update(dto);
- } else {
- int updateCount = mapper.updateIfBeforeSelectedDate(dto);
- if (updateCount == 0) {
- // End-user and scan changed the issue at the same time.
- // See https://jira.sonarsource.com/browse/SONAR-4309
- conflictResolver.resolve(issue, mapper);
- }
+ int updateCount = mapper.updateIfBeforeSelectedDate(dto);
+ if (updateCount == 0) {
+ // End-user and scan changed the issue at the same time.
+ // See https://jira.sonarsource.com/browse/SONAR-4309
+ conflictResolver.resolve(issue, mapper);
}
saved = true;
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/SendIssueNotificationsStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/SendIssueNotificationsStep.java
index 5129e8a4959..f55a33ac0ad 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/SendIssueNotificationsStep.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/SendIssueNotificationsStep.java
@@ -23,12 +23,12 @@ import com.google.common.collect.ImmutableSet;
import java.util.Date;
import java.util.Map;
import java.util.Set;
-import org.sonar.api.issue.internal.DefaultIssue;
+import org.sonar.core.issue.DefaultIssue;
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.IssueCache;
-import org.sonar.server.computation.issue.RuleCache;
+import org.sonar.server.computation.issue.RuleRepository;
import org.sonar.server.issue.notification.IssueChangeNotification;
import org.sonar.server.issue.notification.MyNewIssuesNotification;
import org.sonar.server.issue.notification.NewIssuesNotification;
@@ -49,14 +49,15 @@ public class SendIssueNotificationsStep implements ComputationStep {
static final Set<String> NOTIF_TYPES = ImmutableSet.of(IssueChangeNotification.TYPE, NewIssuesNotification.TYPE, MyNewIssuesNotification.MY_NEW_ISSUES_NOTIF_TYPE);
private final IssueCache issueCache;
- private final RuleCache rules;
+ private final RuleRepository rules;
private final TreeRootHolder treeRootHolder;
private final NotificationService service;
private final BatchReportReader reportReader;
private NewIssuesNotificationFactory newIssuesNotificationFactory;
- public SendIssueNotificationsStep(IssueCache issueCache, RuleCache rules, TreeRootHolder treeRootHolder, NotificationService service,
- BatchReportReader reportReader, NewIssuesNotificationFactory newIssuesNotificationFactory) {
+ public SendIssueNotificationsStep(IssueCache issueCache, RuleRepository rules, TreeRootHolder treeRootHolder,
+ NotificationService service, BatchReportReader reportReader,
+ NewIssuesNotificationFactory newIssuesNotificationFactory) {
this.issueCache = issueCache;
this.rules = rules;
this.treeRootHolder = treeRootHolder;
@@ -84,7 +85,7 @@ public class SendIssueNotificationsStep implements ComputationStep {
newIssuesStats.add(issue);
} else if (issue.isChanged() && issue.mustSendNotifications()) {
IssueChangeNotification changeNotification = new IssueChangeNotification();
- changeNotification.setRuleName(rules.ruleName(issue.ruleKey()));
+ changeNotification.setRuleName(rules.getByKey(issue.ruleKey()).getName());
changeNotification.setIssue(issue);
changeNotification.setProject(project.getKey(), projectName);
service.deliver(changeNotification);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/AbstractChangeTagsAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/AbstractChangeTagsAction.java
index aa1542a659d..73475e26ed8 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/AbstractChangeTagsAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/AbstractChangeTagsAction.java
@@ -26,7 +26,7 @@ import com.google.common.collect.Sets;
import org.sonar.api.server.ServerSide;
import org.sonar.api.issue.Issue;
import org.sonar.api.issue.condition.IsUnResolved;
-import org.sonar.api.issue.internal.DefaultIssue;
+import org.sonar.core.issue.DefaultIssue;
import org.sonar.api.server.rule.RuleTagFormat;
import org.sonar.core.issue.IssueUpdater;
import org.sonar.server.user.UserSession;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/Action.java b/server/sonar-server/src/main/java/org/sonar/server/issue/Action.java
index 40f6d03988b..57c4c21fd76 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/Action.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/Action.java
@@ -26,7 +26,7 @@ import com.google.common.collect.ImmutableList;
import org.sonar.api.server.ServerSide;
import org.sonar.api.issue.Issue;
import org.sonar.api.issue.condition.Condition;
-import org.sonar.api.issue.internal.IssueChangeContext;
+import org.sonar.core.issue.IssueChangeContext;
import org.sonar.server.user.UserSession;
import java.util.Collection;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ActionService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ActionService.java
index b5660789cad..f24fa6159da 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/ActionService.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ActionService.java
@@ -31,8 +31,8 @@ import org.sonar.api.issue.Issue;
import org.sonar.api.issue.action.Action;
import org.sonar.api.issue.action.Actions;
import org.sonar.api.issue.action.Function;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.IssueChangeContext;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.IssueChangeContext;
import org.sonar.core.issue.IssueUpdater;
import org.sonar.core.issue.db.IssueStorage;
import org.sonar.core.persistence.DbSession;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/AssignAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/AssignAction.java
index 0c15344639a..41e8b91708c 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/AssignAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/AssignAction.java
@@ -24,7 +24,7 @@ import com.google.common.base.Strings;
import org.sonar.api.server.ServerSide;
import org.sonar.api.issue.Issue;
import org.sonar.api.issue.condition.IsUnResolved;
-import org.sonar.api.issue.internal.DefaultIssue;
+import org.sonar.core.issue.DefaultIssue;
import org.sonar.api.user.User;
import org.sonar.api.user.UserFinder;
import org.sonar.core.issue.IssueUpdater;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/CommentAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/CommentAction.java
index 83356b5a1c1..d37a746aff9 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/CommentAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/CommentAction.java
@@ -24,7 +24,7 @@ import com.google.common.base.Strings;
import java.util.Collection;
import java.util.Map;
import org.sonar.api.issue.Issue;
-import org.sonar.api.issue.internal.DefaultIssue;
+import org.sonar.core.issue.DefaultIssue;
import org.sonar.api.server.ServerSide;
import org.sonar.core.issue.IssueUpdater;
import org.sonar.server.user.UserSession;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java
index 9f01f119669..fc42a798539 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java
@@ -45,9 +45,9 @@ import org.sonar.api.issue.ActionPlan;
import org.sonar.api.issue.Issue;
import org.sonar.api.issue.IssueComment;
import org.sonar.api.issue.action.Action;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.DefaultIssueComment;
-import org.sonar.api.issue.internal.FieldDiffs;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.DefaultIssueComment;
+import org.sonar.core.issue.FieldDiffs;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.server.ServerSide;
import org.sonar.api.user.User;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueBulkChangeService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueBulkChangeService.java
index 53c4539b16f..65ba2386cf4 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueBulkChangeService.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueBulkChangeService.java
@@ -35,8 +35,8 @@ import java.util.Set;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonar.api.issue.Issue;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.IssueChangeContext;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.IssueChangeContext;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rules.Rule;
import org.sonar.api.utils.log.Logger;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueChangelog.java b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueChangelog.java
index cd089010083..3d8ee6d3b66 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueChangelog.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueChangelog.java
@@ -20,7 +20,7 @@
package org.sonar.server.issue;
import com.google.common.collect.Maps;
-import org.sonar.api.issue.internal.FieldDiffs;
+import org.sonar.core.issue.FieldDiffs;
import org.sonar.api.user.User;
import javax.annotation.CheckForNull;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueChangelogFormatter.java b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueChangelogFormatter.java
index aebc35d8529..0dc55507993 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueChangelogFormatter.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueChangelogFormatter.java
@@ -25,7 +25,7 @@ import java.util.Locale;
import java.util.Map;
import org.sonar.api.server.ServerSide;
import org.sonar.api.i18n.I18n;
-import org.sonar.api.issue.internal.FieldDiffs;
+import org.sonar.core.issue.FieldDiffs;
import org.sonar.api.utils.Duration;
import org.sonar.api.utils.Durations;
import org.sonar.core.issue.IssueUpdater;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueChangelogService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueChangelogService.java
index 62c85672fd5..2009dfb10b8 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueChangelogService.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueChangelogService.java
@@ -23,7 +23,7 @@ import java.util.Collection;
import java.util.List;
import org.sonar.api.server.ServerSide;
import org.sonar.api.issue.Issue;
-import org.sonar.api.issue.internal.FieldDiffs;
+import org.sonar.core.issue.FieldDiffs;
import org.sonar.api.user.User;
import org.sonar.api.user.UserFinder;
import org.sonar.core.issue.db.IssueChangeDao;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueCommentService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueCommentService.java
index 4a1bb7c307d..8b1da808880 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueCommentService.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueCommentService.java
@@ -24,9 +24,9 @@ import com.google.common.base.Strings;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.server.ServerSide;
import org.sonar.api.issue.IssueComment;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.DefaultIssueComment;
-import org.sonar.api.issue.internal.IssueChangeContext;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.DefaultIssueComment;
+import org.sonar.core.issue.IssueChangeContext;
import org.sonar.api.utils.System2;
import org.sonar.core.issue.IssueUpdater;
import org.sonar.core.issue.db.IssueChangeDto;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueQueryService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueQueryService.java
index 134a9414a4b..15c65e354cd 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueQueryService.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueQueryService.java
@@ -50,6 +50,7 @@ import org.sonar.api.utils.System2;
import org.sonar.api.web.UserRole;
import org.sonar.core.component.ComponentDto;
import org.sonar.core.persistence.DbSession;
+import org.sonar.core.rule.RuleKeyFunctions;
import org.sonar.server.component.ComponentService;
import org.sonar.server.db.DbClient;
import org.sonar.server.issue.filter.IssueFilterParameters;
@@ -419,12 +420,7 @@ public class IssueQueryService {
@CheckForNull
private static Collection<RuleKey> stringsToRules(@Nullable Collection<String> rules) {
if (rules != null) {
- return newArrayList(Iterables.transform(rules, new Function<String, RuleKey>() {
- @Override
- public RuleKey apply(@Nullable String s) {
- return s != null ? RuleKey.parse(s) : null;
- }
- }));
+ return newArrayList(Iterables.transform(rules, RuleKeyFunctions.stringToRuleKey()));
}
return null;
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java
index 0f342ce585a..35e97cf20da 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java
@@ -24,8 +24,8 @@ import com.google.common.base.Strings;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.issue.ActionPlan;
import org.sonar.api.issue.Issue;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.IssueChangeContext;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.IssueChangeContext;
import org.sonar.server.notification.NotificationManager;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.Severity;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/PlanAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/PlanAction.java
index 690dca8de77..bf2993a12e2 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/PlanAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/PlanAction.java
@@ -25,7 +25,7 @@ import org.sonar.api.server.ServerSide;
import org.sonar.api.issue.ActionPlan;
import org.sonar.api.issue.Issue;
import org.sonar.api.issue.condition.IsUnResolved;
-import org.sonar.api.issue.internal.DefaultIssue;
+import org.sonar.core.issue.DefaultIssue;
import org.sonar.core.issue.IssueUpdater;
import org.sonar.server.issue.actionplan.ActionPlanService;
import org.sonar.server.user.UserSession;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ServerIssueStorage.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ServerIssueStorage.java
index eabaeb44089..74c9aedce06 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/ServerIssueStorage.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ServerIssueStorage.java
@@ -20,7 +20,7 @@
package org.sonar.server.issue;
import org.sonar.api.server.ServerSide;
-import org.sonar.api.issue.internal.DefaultIssue;
+import org.sonar.core.issue.DefaultIssue;
import org.sonar.api.rules.RuleFinder;
import org.sonar.core.component.ComponentDto;
import org.sonar.core.issue.db.IssueDto;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/SetSeverityAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/SetSeverityAction.java
index 174f943f7cd..6c75b9c1f01 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/SetSeverityAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/SetSeverityAction.java
@@ -26,7 +26,7 @@ import java.util.Map;
import org.sonar.api.issue.Issue;
import org.sonar.api.issue.condition.Condition;
import org.sonar.api.issue.condition.IsUnResolved;
-import org.sonar.api.issue.internal.DefaultIssue;
+import org.sonar.core.issue.DefaultIssue;
import org.sonar.api.server.ServerSide;
import org.sonar.api.web.UserRole;
import org.sonar.core.issue.IssueUpdater;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/TransitionAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/TransitionAction.java
index 6683c79382d..1f4c4b283ed 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/TransitionAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/TransitionAction.java
@@ -27,7 +27,7 @@ import java.util.Collection;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.issue.Issue;
-import org.sonar.api.issue.internal.DefaultIssue;
+import org.sonar.core.issue.DefaultIssue;
import org.sonar.api.server.ServerSide;
import org.sonar.core.issue.workflow.IssueWorkflow;
import org.sonar.core.issue.workflow.Transition;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/actionplan/ActionPlanService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/actionplan/ActionPlanService.java
index 7d9b9259753..a2dc02852a2 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/actionplan/ActionPlanService.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/actionplan/ActionPlanService.java
@@ -28,8 +28,8 @@ import java.util.Date;
import java.util.List;
import javax.annotation.CheckForNull;
import org.sonar.api.issue.ActionPlan;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.IssueChangeContext;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.IssueChangeContext;
import org.sonar.api.server.ServerSide;
import org.sonar.api.web.UserRole;
import org.sonar.core.issue.ActionPlanDeadlineComparator;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/db/IssueDao.java b/server/sonar-server/src/main/java/org/sonar/server/issue/db/IssueDao.java
index 958135fb50d..348e9464d6a 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/db/IssueDao.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/db/IssueDao.java
@@ -19,7 +19,9 @@
*/
package org.sonar.server.issue.db;
-import org.apache.ibatis.session.ResultHandler;
+import java.util.List;
+import java.util.Set;
+import javax.annotation.CheckForNull;
import org.sonar.core.issue.db.IssueDto;
import org.sonar.core.issue.db.IssueMapper;
import org.sonar.core.persistence.DaoComponent;
@@ -27,9 +29,6 @@ import org.sonar.core.persistence.DbSession;
import org.sonar.core.persistence.MyBatis;
import org.sonar.server.exceptions.NotFoundException;
-import javax.annotation.CheckForNull;
-import java.util.List;
-
public class IssueDao extends org.sonar.core.issue.db.IssueDao implements DaoComponent {
public IssueDao(MyBatis mybatis) {
@@ -57,12 +56,8 @@ public class IssueDao extends org.sonar.core.issue.db.IssueDao implements DaoCom
return mapper(session).selectByKeys(keys);
}
- public void selectNonClosedIssuesByModuleUuid(DbSession session, String moduleUuid, ResultHandler handler) {
- session.select("org.sonar.core.issue.db.IssueMapper.selectNonClosedIssuesByModuleUuid", moduleUuid, handler);
- }
-
- public void selectNonClosedIssuesByProjectUuid(DbSession session, String projectUuid, ResultHandler handler) {
- session.select("org.sonar.core.issue.db.IssueMapper.selectNonClosedIssuesByProjectUuid", projectUuid, handler);
+ public Set<String> selectComponentUuidsOfOpenIssuesForProjectUuid(DbSession session, String projectUuid) {
+ return mapper(session).selectComponentUuidsOfOpenIssuesForProjectUuid(projectUuid);
}
public void insert(DbSession session, IssueDto dto) {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/notification/IssueChangeNotification.java b/server/sonar-server/src/main/java/org/sonar/server/issue/notification/IssueChangeNotification.java
index 15b0d435f66..91e0ae311b1 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/notification/IssueChangeNotification.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/notification/IssueChangeNotification.java
@@ -21,8 +21,8 @@ package org.sonar.server.issue.notification;
import com.google.common.base.Strings;
import org.sonar.api.component.Component;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.FieldDiffs;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.FieldDiffs;
import org.sonar.api.notifications.Notification;
import javax.annotation.CheckForNull;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueJsonWriter.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueJsonWriter.java
index 8e6da6672af..11c94a2cce0 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueJsonWriter.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueJsonWriter.java
@@ -35,13 +35,13 @@ import javax.annotation.Nullable;
import org.sonar.api.issue.ActionPlan;
import org.sonar.api.issue.Issue;
import org.sonar.api.issue.IssueComment;
-import org.sonar.api.issue.internal.DefaultIssueComment;
import org.sonar.api.user.User;
import org.sonar.api.utils.DateUtils;
import org.sonar.api.utils.Duration;
import org.sonar.api.utils.Durations;
import org.sonar.api.utils.text.JsonWriter;
import org.sonar.core.component.ComponentDto;
+import org.sonar.core.issue.DefaultIssueComment;
import org.sonar.markdown.Markdown;
import org.sonar.server.user.UserSession;
import org.sonar.server.ws.JsonWriterUtils;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java
index 13860cdbaba..fa78cb3f86a 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java
@@ -38,7 +38,6 @@ import org.apache.commons.lang.BooleanUtils;
import org.sonar.api.i18n.I18n;
import org.sonar.api.issue.ActionPlan;
import org.sonar.api.issue.Issue;
-import org.sonar.api.issue.internal.DefaultIssueComment;
import org.sonar.api.resources.Language;
import org.sonar.api.resources.Languages;
import org.sonar.api.rule.RuleKey;
@@ -52,6 +51,7 @@ import org.sonar.api.user.UserFinder;
import org.sonar.api.utils.DateUtils;
import org.sonar.api.utils.text.JsonWriter;
import org.sonar.core.component.ComponentDto;
+import org.sonar.core.issue.DefaultIssueComment;
import org.sonar.core.persistence.DbSession;
import org.sonar.server.component.ws.ComponentJsonWriter;
import org.sonar.server.db.DbClient;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/ShowAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/ShowAction.java
index 7ab69a45769..aa13668a4b6 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/ShowAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/ShowAction.java
@@ -29,8 +29,8 @@ import org.sonar.api.i18n.I18n;
import org.sonar.api.issue.ActionPlan;
import org.sonar.api.issue.Issue;
import org.sonar.api.issue.IssueComment;
-import org.sonar.api.issue.internal.DefaultIssueComment;
-import org.sonar.api.issue.internal.FieldDiffs;
+import org.sonar.core.issue.DefaultIssueComment;
+import org.sonar.core.issue.FieldDiffs;
import org.sonar.api.server.debt.DebtCharacteristic;
import org.sonar.api.server.debt.internal.DefaultDebtCharacteristic;
import org.sonar.api.server.ws.Request;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/source/db/FileSourceDao.java b/server/sonar-server/src/main/java/org/sonar/server/source/db/FileSourceDao.java
index e17ae11c323..c1d8f0e6002 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/source/db/FileSourceDao.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/source/db/FileSourceDao.java
@@ -21,11 +21,13 @@
package org.sonar.server.source.db;
import com.google.common.base.Function;
+import com.google.common.base.Splitter;
import java.io.Reader;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
+import java.util.List;
import javax.annotation.CheckForNull;
import org.apache.commons.dbutils.DbUtils;
import org.apache.commons.io.IOUtils;
@@ -40,6 +42,7 @@ import org.sonar.core.source.db.FileSourceMapper;
@ServerSide
public class FileSourceDao implements DaoComponent {
+ private static final Splitter END_OF_LINE_SPLITTER = Splitter.on('\n');
private final MyBatis mybatis;
public FileSourceDao(MyBatis myBatis) {
@@ -66,6 +69,27 @@ public class FileSourceDao implements DaoComponent {
}
}
+ @CheckForNull
+ public List<String> selectLineHashes(DbSession dbSession, String fileUuid) {
+ Connection connection = dbSession.getConnection();
+ PreparedStatement pstmt = null;
+ ResultSet rs = null;
+ try {
+ pstmt = connection.prepareStatement("SELECT line_hashes FROM file_sources WHERE file_uuid=? AND data_type=?");
+ pstmt.setString(1, fileUuid);
+ pstmt.setString(2, Type.SOURCE);
+ rs = pstmt.executeQuery();
+ if (rs.next()) {
+ return END_OF_LINE_SPLITTER.splitToList(rs.getString(1));
+ }
+ return null;
+ } catch (SQLException e) {
+ throw new IllegalStateException("Fail to read FILE_SOURCES.LINE_HASHES of file " + fileUuid, e);
+ } finally {
+ DbUtils.closeQuietly(connection, pstmt, rs);
+ }
+ }
+
public <T> void readLineHashesStream(DbSession dbSession, String fileUuid, Function<Reader, T> function) {
Connection connection = dbSession.getConnection();
PreparedStatement pstmt = null;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/batch/BatchReportReaderImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/batch/BatchReportReaderImplTest.java
index 8286bcfc9fd..f09ed4bf395 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/batch/BatchReportReaderImplTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/batch/BatchReportReaderImplTest.java
@@ -42,7 +42,7 @@ public class BatchReportReaderImplTest {
private static final BatchReport.Measure MEASURE = BatchReport.Measure.newBuilder().build();
private static final BatchReport.Component COMPONENT = BatchReport.Component.newBuilder().setRef(COMPONENT_REF).build();
private static final BatchReport.Issue ISSUE = BatchReport.Issue.newBuilder().build();
- private static final BatchReport.Issues ISSUES = BatchReport.Issues.newBuilder().setComponentRef(COMPONENT_REF).setComponentUuid(COMPONENT_UUID).addIssue(ISSUE).build();
+ private static final BatchReport.Issues ISSUES = BatchReport.Issues.newBuilder().setComponentRef(COMPONENT_REF).addIssue(ISSUE).build();
private static final BatchReport.Duplication DUPLICATION = BatchReport.Duplication.newBuilder().build();
private static final BatchReport.Symbols.Symbol SYMBOL = BatchReport.Symbols.Symbol.newBuilder().build();
private static final BatchReport.SyntaxHighlighting SYNTAX_HIGHLIGHTING_1 = BatchReport.SyntaxHighlighting.newBuilder().build();
@@ -166,25 +166,6 @@ public class BatchReportReaderImplTest {
assertThat(underTest.readComponentIssues(COMPONENT_REF)).isNotSameAs(underTest.readComponentIssues(COMPONENT_REF));
}
- @Test(expected = IllegalStateException.class)
- public void readDeletedComponentIssues_throws_ISE_if_file_does_not_exist() {
- underTest.readDeletedComponentIssues(COMPONENT_REF);
- }
-
- @Test
- public void verify_readDeletedComponentIssues_returns_Issues() {
- writer.writeDeletedComponentIssues(COMPONENT_REF, COMPONENT_UUID, of(ISSUE));
-
- assertThat(underTest.readDeletedComponentIssues(COMPONENT_REF)).isEqualTo(ISSUES);
- }
-
- @Test
- public void readDeletedComponentIssues_it_not_cached() {
- writer.writeDeletedComponentIssues(COMPONENT_REF, COMPONENT_UUID, of(ISSUE));
-
- assertThat(underTest.readDeletedComponentIssues(COMPONENT_REF)).isNotSameAs(underTest.readDeletedComponentIssues(COMPONENT_REF));
- }
-
@Test
public void readComponentDuplications_returns_empty_list_if_file_does_not_exist() {
assertThat(underTest.readComponentDuplications(COMPONENT_REF)).isEmpty();
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/batch/BatchReportReaderRule.java b/server/sonar-server/src/test/java/org/sonar/server/computation/batch/BatchReportReaderRule.java
index 6a6baba1ed7..77a62c5610f 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/batch/BatchReportReaderRule.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/batch/BatchReportReaderRule.java
@@ -39,7 +39,6 @@ public class BatchReportReaderRule implements TestRule, BatchReportReader {
private Map<Integer, BatchReport.Changesets> changesets = new HashMap<>();
private Map<Integer, BatchReport.Component> components = new HashMap<>();
private Map<Integer, List<BatchReport.Issue>> issues = new HashMap<>();
- private Map<Integer, BatchReport.Issues> deletedIssues = new HashMap<>();
private Map<Integer, List<BatchReport.Duplication>> duplications = new HashMap<>();
private Map<Integer, List<BatchReport.Symbols.Symbol>> symbols = new HashMap<>();
private Map<Integer, List<BatchReport.SyntaxHighlighting>> syntaxHighlightings = new HashMap<>();
@@ -55,8 +54,7 @@ public class BatchReportReaderRule implements TestRule, BatchReportReader {
public void evaluate() throws Throwable {
try {
statement.evaluate();
- }
- finally {
+ } finally {
clear();
}
}
@@ -69,7 +67,6 @@ public class BatchReportReaderRule implements TestRule, BatchReportReader {
this.changesets.clear();
this.components.clear();
this.issues.clear();
- this.deletedIssues.clear();
this.duplications.clear();
this.symbols.clear();
this.syntaxHighlightings.clear();
@@ -133,19 +130,6 @@ public class BatchReportReaderRule implements TestRule, BatchReportReader {
}
@Override
- public BatchReport.Issues readDeletedComponentIssues(int deletedComponentRef) {
- BatchReport.Issues issues = this.deletedIssues.get(deletedComponentRef);
- if (issues == null) {
- throw new IllegalStateException("Unable to issues for deleted component #" + deletedComponentRef);
- }
- return issues;
- }
-
- public void putDeletedIssues(int componentRef, BatchReport.Issues issues) {
- this.deletedIssues.put(componentRef, issues);
- }
-
- @Override
public List<BatchReport.Duplication> readComponentDuplications(int componentRef) {
return nonNull(this.duplications.get(componentRef));
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/debt/CharacteristicTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/debt/CharacteristicTest.java
index 1499001ec1d..c1deba15921 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/debt/CharacteristicTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/debt/CharacteristicTest.java
@@ -33,14 +33,15 @@ public class CharacteristicTest {
@Test
public void test_getter_and_setter() throws Exception {
- Characteristic characteristic = new Characteristic(1, "PORTABILITY");
+ Characteristic characteristic = new Characteristic(1, "PORTABILITY", null);
assertThat(characteristic.getId()).isEqualTo(1);
assertThat(characteristic.getKey()).isEqualTo("PORTABILITY");
+ assertThat(characteristic.getParentId()).isNull();
}
@Test
public void test_to_string() throws Exception {
- assertThat(new Characteristic(1, "PORTABILITY").toString()).isEqualTo("Characteristic{id=1, key='PORTABILITY'}");
+ assertThat(new Characteristic(1, "PORTABILITY", null).toString()).isEqualTo("Characteristic{id=1, key='PORTABILITY', parentId=null}");
}
@Test
@@ -48,6 +49,6 @@ public class CharacteristicTest {
thrown.expect(NullPointerException.class);
thrown.expectMessage("key cannot be null");
- new Characteristic(1, null);
+ new Characteristic(1, null, null);
}
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/debt/DebtModelHolderImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/debt/DebtModelHolderImplTest.java
index 49917ee013f..09556a13363 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/debt/DebtModelHolderImplTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/debt/DebtModelHolderImplTest.java
@@ -17,7 +17,6 @@
* 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.debt;
import java.util.Arrays;
@@ -34,22 +33,23 @@ public class DebtModelHolderImplTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
- private static final Characteristic PORTABILITY = new Characteristic(1, "PORTABILITY");
- private static final Characteristic COMPILER_RELATED_PORTABILITY = new Characteristic(2, "COMPILER_RELATED_PORTABILITY");
- private static final Characteristic HARDWARE_RELATED_PORTABILITY = new Characteristic(3, "HARDWARE_RELATED_PORTABILITY");
+ private static final Characteristic PORTABILITY = new Characteristic(1, "PORTABILITY", null);
+ private static final Characteristic COMPILER_RELATED_PORTABILITY = new Characteristic(2, "COMPILER_RELATED_PORTABILITY", 1);
+ private static final Characteristic HARDWARE_RELATED_PORTABILITY = new Characteristic(3, "HARDWARE_RELATED_PORTABILITY", 1);
- private static final Characteristic MAINTAINABILITY = new Characteristic(4, "MAINTAINABILITY");
- private static final Characteristic READABILITY = new Characteristic(5, "READABILITY");
+ private static final Characteristic MAINTAINABILITY = new Characteristic(4, "MAINTAINABILITY", null);
+ private static final Characteristic READABILITY = new Characteristic(5, "READABILITY", null);
DebtModelHolderImpl sut = new DebtModelHolderImpl();
@Test
- public void add_characteristics() throws Exception {
+ public void add_and_get_characteristics() throws Exception {
sut.addCharacteristics(PORTABILITY, Arrays.asList(COMPILER_RELATED_PORTABILITY, HARDWARE_RELATED_PORTABILITY));
sut.addCharacteristics(MAINTAINABILITY, singletonList(READABILITY));
- assertThat(sut.findRootCharacteristics()).hasSize(2);
- assertThat(sut.findSubCharacteristicsByRootKey("PORTABILITY")).hasSize(2);
+ assertThat(sut.getRootCharacteristics()).hasSize(2);
+ assertThat(sut.getCharacteristicById(PORTABILITY.getId()).getKey()).isEqualTo("PORTABILITY");
+ assertThat(sut.getCharacteristicById(COMPILER_RELATED_PORTABILITY.getId()).getKey()).isEqualTo("COMPILER_RELATED_PORTABILITY");
}
@Test
@@ -77,51 +77,26 @@ public class DebtModelHolderImplTest {
}
@Test
- public void get_characteristic_by_key() throws Exception {
- sut.addCharacteristics(PORTABILITY, singletonList(COMPILER_RELATED_PORTABILITY));
-
- assertThat(sut.getCharacteristicByKey("PORTABILITY").get()).isEqualTo(PORTABILITY);
- assertThat(sut.getCharacteristicByKey("COMPILER_RELATED_PORTABILITY").get()).isEqualTo(COMPILER_RELATED_PORTABILITY);
- assertThat(sut.getCharacteristicByKey("UNKNOWN").isPresent()).isFalse();
- }
-
- @Test
- public void get_characteristic_by_key_throws_ISE_when_not_initialized() throws Exception {
- thrown.expect(IllegalStateException.class);
- thrown.expectMessage("Characteristics have not been initialized yet");
-
- sut.getCharacteristicByKey("PORTABILITY");
- }
-
- @Test
public void get_root_characteristics() throws Exception {
sut.addCharacteristics(PORTABILITY, Arrays.asList(COMPILER_RELATED_PORTABILITY, READABILITY));
sut.addCharacteristics(MAINTAINABILITY, singletonList(READABILITY));
- assertThat(sut.findRootCharacteristics()).hasSize(2);
+ assertThat(sut.getRootCharacteristics()).hasSize(2);
}
@Test
- public void get_root_characteristics_throws_ISE_when_not_initialized() throws Exception {
+ public void getCharacteristicById_throws_ISE_when_not_initialized() throws Exception {
thrown.expect(IllegalStateException.class);
thrown.expectMessage("Characteristics have not been initialized yet");
- sut.findRootCharacteristics();
- }
-
- @Test
- public void get_sub_characteristics_by_root_key() throws Exception {
- sut.addCharacteristics(PORTABILITY, Arrays.asList(COMPILER_RELATED_PORTABILITY, READABILITY));
-
- assertThat(sut.findSubCharacteristicsByRootKey("PORTABILITY")).hasSize(2);
- assertThat(sut.findSubCharacteristicsByRootKey("UNKNOWN")).isEmpty();
+ sut.getCharacteristicById(1);
}
@Test
- public void get_sub_characteristics_by_root_key_throws_a_ISE_when_not_initialized() throws Exception {
+ public void getRootCharacteristics_throws_ISE_when_not_initialized() throws Exception {
thrown.expect(IllegalStateException.class);
thrown.expectMessage("Characteristics have not been initialized yet");
- sut.findSubCharacteristicsByRootKey("PORTABILITY");
+ sut.getRootCharacteristics();
}
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/DebtAggregatorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/DebtAggregatorTest.java
new file mode 100644
index 00000000000..2542ac0c175
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/DebtAggregatorTest.java
@@ -0,0 +1,155 @@
+/*
+ * 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.base.Optional;
+import javax.annotation.CheckForNull;
+import org.junit.Test;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.utils.Duration;
+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.component.DumbComponent;
+import org.sonar.server.computation.debt.Characteristic;
+import org.sonar.server.computation.debt.DebtModelHolderImpl;
+import org.sonar.server.computation.debt.MutableDebtModelHolder;
+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.rule.RuleTesting;
+
+import static java.util.Arrays.asList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
+
+public class DebtAggregatorTest {
+
+ /**
+ * Root characteristic
+ */
+ public static final int PORTABILITY_ID = 1000;
+
+ /**
+ * Sub-characteristic of {@link #PORTABILITY_ID}
+ */
+ public static final int PORTABILITY_SOFT_ID = 1001;
+
+ /**
+ * Sub-characteristic of {@link #PORTABILITY_ID}
+ */
+ public static final int PORTABILITY_HARD_ID = 1002;
+
+ /**
+ * Root characteristic
+ */
+ public static final int RELIABILITY_ID = 1003;
+
+ Component file = DumbComponent.builder(Component.Type.FILE, 1).build();
+ Component project = DumbComponent.builder(Component.Type.PROJECT, 2).addChildren(file).build();
+
+ DumbRule rule = new DumbRule(RuleTesting.XOO_X1).setId(100).setSubCharacteristicId(PORTABILITY_SOFT_ID);
+
+ @org.junit.Rule
+ public RuleRepositoryRule ruleRepository = new RuleRepositoryRule().add(rule);
+
+ MutableDebtModelHolder debtModelHolder = new DebtModelHolderImpl()
+ .addCharacteristics(new Characteristic(PORTABILITY_ID, "PORTABILITY", null),
+ asList(new Characteristic(PORTABILITY_SOFT_ID, "PORTABILITY_HARDWARE", PORTABILITY_ID), new Characteristic(PORTABILITY_HARD_ID, "PORTABILITY_SOFTWARE", PORTABILITY_ID)))
+ .addCharacteristics(new Characteristic(RELIABILITY_ID, "RELIABILITY", null),
+ asList(new Characteristic(1004, "DATA_RELIABILITY", RELIABILITY_ID))
+ );
+
+ @org.junit.Rule
+ public MetricRepositoryRule metricRepository = new MetricRepositoryRule().add(200, CoreMetrics.TECHNICAL_DEBT);
+
+ @org.junit.Rule
+ public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create();
+
+ DebtAggregator underTest = new DebtAggregator(ruleRepository, debtModelHolder, metricRepository, measureRepository);
+
+ @Test
+ public void sum_debt_of_unresolved_issues() throws Exception {
+ DefaultIssue unresolved1 = new DefaultIssue().setDebt(Duration.create(10)).setRuleKey(rule.getKey());
+ DefaultIssue unresolved2 = new DefaultIssue().setDebt(Duration.create(30)).setRuleKey(rule.getKey());
+ DefaultIssue unresolvedWithoutDebt = new DefaultIssue().setRuleKey(rule.getKey());
+ DefaultIssue resolved = new DefaultIssue().setDebt(Duration.create(50)).setResolution(RESOLUTION_FIXED).setRuleKey(rule.getKey());
+
+ underTest.beforeComponent(file, mock(Tracking.class));
+ underTest.onIssue(file, unresolved1);
+ underTest.onIssue(file, unresolved2);
+ underTest.onIssue(file, unresolvedWithoutDebt);
+ underTest.onIssue(file, resolved);
+ underTest.afterComponent(file);
+
+ // total debt
+ assertThat(debtMeasure(file).get().getLongValue()).isEqualTo(10 + 30);
+
+ // debt by rule
+ assertThat(debtRuleMeasure(file, rule.getId()).get().getLongValue()).isEqualTo(10 + 30);
+
+ // debt by characteristic. Root characteristics with zero values are not saved for files.
+ assertThat(debtCharacteristicMeasure(file, PORTABILITY_ID).get().getLongValue()).isEqualTo(10 + 30);
+ assertThat(debtCharacteristicMeasure(file, PORTABILITY_SOFT_ID).get().getLongValue()).isEqualTo(10 + 30);
+ assertThat(debtCharacteristicMeasure(file, PORTABILITY_HARD_ID).isPresent()).isFalse();
+ assertThat(debtCharacteristicMeasure(file, RELIABILITY_ID).isPresent()).isFalse();
+ }
+
+ @Test
+ public void aggregate_debt_of_children() throws Exception {
+ DefaultIssue fileIssue = new DefaultIssue().setDebt(Duration.create(10)).setRuleKey(rule.getKey());
+ DefaultIssue projectIssue = new DefaultIssue().setDebt(Duration.create(30)).setRuleKey(rule.getKey());
+
+ underTest.beforeComponent(file, mock(Tracking.class));
+ underTest.onIssue(file, fileIssue);
+ underTest.afterComponent(file);
+ underTest.beforeComponent(project, mock(Tracking.class));
+ underTest.onIssue(project, projectIssue);
+ underTest.afterComponent(project);
+
+ // total debt of project
+ assertThat(debtMeasure(project).get().getLongValue()).isEqualTo(10 + 30);
+
+ // debt by rule
+ assertThat(debtRuleMeasure(project, rule.getId()).get().getLongValue()).isEqualTo(10 + 30);
+
+ // debt by characteristic. Root characteristics with zero values are stored for modules and projects.
+ assertThat(debtCharacteristicMeasure(project, PORTABILITY_ID).get().getLongValue()).isEqualTo(10 + 30);
+ assertThat(debtCharacteristicMeasure(project, PORTABILITY_SOFT_ID).get().getLongValue()).isEqualTo(10 + 30);
+ assertThat(debtCharacteristicMeasure(project, PORTABILITY_HARD_ID).isPresent()).isFalse();
+ assertThat(debtCharacteristicMeasure(project, RELIABILITY_ID).get().getLongValue()).isZero();
+ }
+
+ @CheckForNull
+ private Optional<Measure> debtMeasure(Component component) {
+ return measureRepository.getRawMeasure(component, metricRepository.getByKey(CoreMetrics.TECHNICAL_DEBT_KEY));
+ }
+
+ @CheckForNull
+ private Optional<Measure> debtRuleMeasure(Component component, int ruleId) {
+ return measureRepository.getRawRuleMeasure(component, metricRepository.getByKey(CoreMetrics.TECHNICAL_DEBT_KEY), ruleId);
+ }
+
+ @CheckForNull
+ private Optional<Measure> debtCharacteristicMeasure(Component component, int characteristicId) {
+ return measureRepository.getRawCharacteristicMeasure(component, metricRepository.getByKey(CoreMetrics.TECHNICAL_DEBT_KEY), characteristicId);
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/DebtCalculatorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/DebtCalculatorTest.java
new file mode 100644
index 00000000000..cf57d9aa682
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/DebtCalculatorTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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 org.junit.Test;
+import org.sonar.api.config.Settings;
+import org.sonar.api.i18n.I18n;
+import org.sonar.api.server.debt.DebtRemediationFunction;
+import org.sonar.api.server.debt.internal.DefaultDebtRemediationFunction;
+import org.sonar.api.utils.Durations;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.server.rule.RuleTesting;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+public class DebtCalculatorTest {
+
+ DumbRule rule = new DumbRule(RuleTesting.XOO_X1);
+ DefaultIssue issue = new DefaultIssue().setRuleKey(rule.getKey());
+
+ @org.junit.Rule
+ public RuleRepositoryRule ruleRepository = new RuleRepositoryRule().add(rule);
+
+ DebtCalculator underTest = new DebtCalculator(ruleRepository, new Durations(new Settings(), mock(I18n.class)));
+
+ @Test
+ public void no_debt_if_function_is_not_defined() throws Exception {
+ DefaultIssue issue = new DefaultIssue().setRuleKey(rule.getKey());
+
+ assertThat(underTest.calculate(issue)).isNull();
+ }
+
+ @Test
+ public void default_effort_to_fix_is_one_for_linear_function() throws Exception {
+ int coefficient = 2;
+ rule.setFunction(new DefaultDebtRemediationFunction(DebtRemediationFunction.Type.LINEAR, coefficient + "min", null));
+
+ assertThat(underTest.calculate(issue).toMinutes()).isEqualTo(coefficient * 1);
+ }
+
+ @Test
+ public void linear_function() throws Exception {
+ double effortToFix = 3.0;
+ int coefficient = 2;
+ issue.setEffortToFix(effortToFix);
+ rule.setFunction(new DefaultDebtRemediationFunction(DebtRemediationFunction.Type.LINEAR, coefficient + "min", null));
+
+ assertThat(underTest.calculate(issue).toMinutes()).isEqualTo((int) (coefficient * effortToFix));
+ }
+
+ @Test
+ public void constant_function() throws Exception {
+ int constant = 2;
+ issue.setEffortToFix(null);
+ rule.setFunction(new DefaultDebtRemediationFunction(DebtRemediationFunction.Type.CONSTANT_ISSUE, null, constant + "min"));
+
+ assertThat(underTest.calculate(issue).toMinutes()).isEqualTo(2);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void effort_to_fix_must_not_be_set_with_constant_function() throws Exception {
+ int constant = 2;
+ issue.setEffortToFix(3.0);
+ rule.setFunction(new DefaultDebtRemediationFunction(DebtRemediationFunction.Type.CONSTANT_ISSUE, null, constant + "min"));
+
+ underTest.calculate(issue);
+ }
+
+ @Test
+ public void linear_with_offset_function() throws Exception {
+ double effortToFix = 3.0;
+ int coefficient = 2;
+ int offset = 5;
+ issue.setEffortToFix(effortToFix);
+ rule.setFunction(new DefaultDebtRemediationFunction(
+ DebtRemediationFunction.Type.LINEAR_OFFSET, coefficient + "min", offset + "min"));
+
+ assertThat(underTest.calculate(issue).toMinutes()).isEqualTo((int) ((coefficient * effortToFix) + offset));
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/DefaultAssigneeTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/DefaultAssigneeTest.java
new file mode 100644
index 00000000000..e1e846bb729
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/DefaultAssigneeTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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 org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.config.Settings;
+import org.sonar.server.computation.batch.TreeRootHolderRule;
+import org.sonar.server.computation.component.ProjectSettingsRepository;
+import org.sonar.server.user.index.UserDoc;
+import org.sonar.server.user.index.UserIndex;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class DefaultAssigneeTest {
+
+ public static final String PROJECT_KEY = "PROJECT_KEY";
+
+ TreeRootHolderRule rootHolder = mock(TreeRootHolderRule.class, Mockito.RETURNS_DEEP_STUBS);
+ UserIndex userIndex = mock(UserIndex.class);
+ Settings settings = new Settings();
+ ProjectSettingsRepository settingsRepository = mock(ProjectSettingsRepository.class);
+
+ DefaultAssignee underTest = new DefaultAssignee(rootHolder, userIndex, settingsRepository);
+
+ @Before
+ public void before() {
+ when(rootHolder.getRoot().getKey()).thenReturn(PROJECT_KEY);
+ when(settingsRepository.getProjectSettings(PROJECT_KEY)).thenReturn(settings);
+ }
+
+ @Test
+ public void no_default_assignee() throws Exception {
+ assertThat(underTest.getLogin()).isNull();
+ }
+
+ @Test
+ public void default_assignee() throws Exception {
+ settings.setProperty(CoreProperties.DEFAULT_ISSUE_ASSIGNEE, "erik");
+ when(userIndex.getNullableByLogin("erik")).thenReturn(new UserDoc().setLogin("erik"));
+
+ assertThat(underTest.getLogin()).isEqualTo("erik");
+ }
+
+ @Test
+ public void configured_login_does_not_exist() throws Exception {
+ settings.setProperty(CoreProperties.DEFAULT_ISSUE_ASSIGNEE, "erik");
+ when(userIndex.getNullableByLogin("erik")).thenReturn(null);
+
+ assertThat(underTest.getLogin()).isNull();
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/DumbRule.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/DumbRule.java
new file mode 100644
index 00000000000..79be4f1dafa
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/DumbRule.java
@@ -0,0 +1,118 @@
+/*
+ * 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.HashSet;
+import java.util.Set;
+import javax.annotation.Nullable;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rule.RuleStatus;
+import org.sonar.api.server.debt.DebtRemediationFunction;
+
+import static java.util.Objects.requireNonNull;
+
+public class DumbRule implements Rule {
+ private Integer id;
+ private RuleKey key;
+ private String name;
+ private RuleStatus status = RuleStatus.READY;
+ private boolean isActivated = false;
+ private Set<String> tags = new HashSet<>();
+ private Integer subCharacteristicId;
+ private DebtRemediationFunction function;
+
+ public DumbRule(RuleKey key) {
+ this.key = key;
+ }
+
+ @Override
+ public int getId() {
+ return requireNonNull(id);
+ }
+
+ @Override
+ public RuleKey getKey() {
+ return requireNonNull(key);
+ }
+
+ @Override
+ public String getName() {
+ return requireNonNull(name);
+ }
+
+ @Override
+ public RuleStatus getStatus() {
+ return requireNonNull(status);
+ }
+
+ @Override
+ public boolean isActivated() {
+ return isActivated;
+ }
+
+ @Override
+ public Set<String> getTags() {
+ return requireNonNull(tags);
+ }
+
+ @Override
+ public Integer getSubCharacteristicId() {
+ return subCharacteristicId;
+ }
+
+ @Override
+ public DebtRemediationFunction getRemediationFunction() {
+ return function;
+ }
+
+ public DumbRule setId(Integer id) {
+ this.id = id;
+ return this;
+ }
+
+ public DumbRule setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public DumbRule setStatus(RuleStatus status) {
+ this.status = status;
+ return this;
+ }
+
+ public DumbRule setIsActivated(boolean isActivated) {
+ this.isActivated = isActivated;
+ return this;
+ }
+
+ public DumbRule setSubCharacteristicId(@Nullable Integer subCharacteristicId) {
+ this.subCharacteristicId = subCharacteristicId;
+ return this;
+ }
+
+ public DumbRule setFunction(@Nullable DebtRemediationFunction function) {
+ this.function = function;
+ return this;
+ }
+
+ public void setTags(Set<String> tags) {
+ this.tags = tags;
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/SourceLinesCacheTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueAssignerTest.java
index 4638933135b..d5f8ab5a2e8 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/SourceLinesCacheTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueAssignerTest.java
@@ -24,32 +24,36 @@ import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
-import org.junit.experimental.categories.Category;
import org.sonar.api.config.Settings;
import org.sonar.batch.protocol.output.BatchReport;
+import org.sonar.core.issue.tracking.Tracking;
import org.sonar.server.computation.batch.BatchReportReaderRule;
+import org.sonar.server.computation.component.Component;
+import org.sonar.server.computation.component.DumbComponent;
import org.sonar.server.es.EsTester;
import org.sonar.server.source.index.SourceLineDoc;
import org.sonar.server.source.index.SourceLineIndex;
import org.sonar.server.source.index.SourceLineIndexDefinition;
-import org.sonar.test.DbTests;
-import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
-@Category(DbTests.class)
-public class SourceLinesCacheTest {
+public class IssueAssignerTest {
@ClassRule
public static EsTester esTester = new EsTester().addDefinitions(new SourceLineIndexDefinition(new Settings()));
+
@Rule
public BatchReportReaderRule reportReader = new BatchReportReaderRule();
+ ScmAccountToUser scmAccountToUser = mock(ScmAccountToUser.class);
+ DefaultAssignee defaultAssignee = mock(DefaultAssignee.class);
+ Component file = DumbComponent.builder(Component.Type.FILE, 1).build();
- SourceLinesCache sut;
+ IssueAssigner underTest;
@Before
public void setUp() throws Exception {
esTester.truncateIndices();
- sut = new SourceLinesCache(new SourceLineIndex(esTester.client()));
+ underTest = new IssueAssigner(new SourceLineIndex(esTester.client()), reportReader, scmAccountToUser, defaultAssignee);
}
@Test
@@ -63,41 +67,43 @@ public class SourceLinesCacheTest {
.addChangesetIndexByLine(1)
.build());
- sut.init("ANY_UUID", 123_456_789, reportReader);
-
- assertThat(sut.lineAuthor(1)).isEqualTo("charb");
- assertThat(sut.lineAuthor(2)).isEqualTo("charb");
- assertThat(sut.lineAuthor(3)).isEqualTo("wolinski");
- // compute last author
- assertThat(sut.lineAuthor(4)).isEqualTo("wolinski");
- assertThat(sut.lineAuthor(null)).isEqualTo("wolinski");
- }
-
- @Test
- public void line_author_from_index() throws Exception {
- esTester.putDocuments(SourceLineIndexDefinition.INDEX, SourceLineIndexDefinition.TYPE,
- newSourceLine("cabu", "123-456-789", 123_456_789, 1),
- newSourceLine("cabu", "123-456-789", 123_456_789, 2),
- newSourceLine("cabu", "123-123-789", 123_456_789, 3),
- newSourceLine("wolinski", "987-654-321", 987_654_321, 4),
- newSourceLine("cabu", "123-456-789", 123_456_789, 5)
- );
-
- sut.init("DEFAULT_UUID", 123, reportReader);
-
- assertThat(sut.lineAuthor(1)).isEqualTo("cabu");
- assertThat(sut.lineAuthor(2)).isEqualTo("cabu");
- assertThat(sut.lineAuthor(3)).isEqualTo("cabu");
- assertThat(sut.lineAuthor(4)).isEqualTo("wolinski");
- assertThat(sut.lineAuthor(5)).isEqualTo("cabu");
- assertThat(sut.lineAuthor(6)).isEqualTo("wolinski");
+ underTest.beforeComponent(file, mock(Tracking.class));
+// underTest.onIssue(file, issue);
+// sut.init("ANY_UUID", 123_456_789, reportReader);
+//
+// assertThat(sut.lineAuthor(1)).isEqualTo("charb");
+// assertThat(sut.lineAuthor(2)).isEqualTo("charb");
+// assertThat(sut.lineAuthor(3)).isEqualTo("wolinski");
+// // compute last author
+// assertThat(sut.lineAuthor(4)).isEqualTo("wolinski");
+// assertThat(sut.lineAuthor(null)).isEqualTo("wolinski");
}
- @Test(expected = IllegalStateException.class)
- public void fail_when_component_ref_is_not_filled() {
- sut.init("ANY_UUID", null, reportReader);
- sut.lineAuthor(0);
- }
+// @Test
+// public void line_author_from_index() throws Exception {
+// esTester.putDocuments(SourceLineIndexDefinition.INDEX, SourceLineIndexDefinition.TYPE,
+// newSourceLine("cabu", "123-456-789", 123_456_789, 1),
+// newSourceLine("cabu", "123-456-789", 123_456_789, 2),
+// newSourceLine("cabu", "123-123-789", 123_456_789, 3),
+// newSourceLine("wolinski", "987-654-321", 987_654_321, 4),
+// newSourceLine("cabu", "123-456-789", 123_456_789, 5)
+// );
+//
+// sut.init("DEFAULT_UUID", 123, reportReader);
+//
+// assertThat(sut.lineAuthor(1)).isEqualTo("cabu");
+// assertThat(sut.lineAuthor(2)).isEqualTo("cabu");
+// assertThat(sut.lineAuthor(3)).isEqualTo("cabu");
+// assertThat(sut.lineAuthor(4)).isEqualTo("wolinski");
+// assertThat(sut.lineAuthor(5)).isEqualTo("cabu");
+// assertThat(sut.lineAuthor(6)).isEqualTo("wolinski");
+// }
+//
+// @Test(expected = IllegalStateException.class)
+// public void fail_when_component_ref_is_not_filled() {
+// sut.init("ANY_UUID", null, reportReader);
+// sut.lineAuthor(0);
+// }
private BatchReport.Changesets.Changeset.Builder newChangeset(String author, String revision, long date) {
return BatchReport.Changesets.Changeset.newBuilder()
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueComputationTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueComputationTest.java
index e84f8688413..f07db4e1b6d 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueComputationTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueComputationTest.java
@@ -17,210 +17,229 @@
* 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.ImmutableSet;
-import com.google.common.collect.Iterators;
-import java.io.IOException;
-import java.util.Arrays;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.config.Settings;
-import org.sonar.api.issue.Issue;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.rule.RuleKey;
-import org.sonar.api.utils.System2;
-import org.sonar.api.utils.log.LogTester;
-import org.sonar.batch.protocol.output.BatchReport;
-import org.sonar.core.rule.RuleDto;
-import org.sonar.server.computation.batch.BatchReportReaderRule;
-import org.sonar.server.computation.component.ProjectSettingsRepository;
-import org.sonar.server.user.index.UserDoc;
-import org.sonar.server.user.index.UserIndex;
-
-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;
-
-public class IssueComputationTest {
-
- private static final RuleKey RULE_KEY = RuleKey.of("squid", "R1");
- private static final String PROJECT_KEY = "PROJECT_KEY";
-
- @Rule
- public TemporaryFolder temp = new TemporaryFolder();
- @Rule
- public LogTester logTester = new LogTester();
- @Rule
- public BatchReportReaderRule reportReader = new BatchReportReaderRule();
-
- IssueComputation sut;
-
- // inputs
- RuleCache ruleCache = mock(RuleCache.class);
- SourceLinesCache lineCache = mock(SourceLinesCache.class);
- ScmAccountCache scmAccountCache = mock(ScmAccountCache.class);
- RuleDto rule = new RuleDto().setRepositoryKey(RULE_KEY.repository()).setRuleKey(RULE_KEY.rule());
- BatchReport.Issue.Builder inputIssue = BatchReport.Issue.newBuilder()
- .setUuid("ISSUE_A")
- .setRuleRepository(RULE_KEY.repository())
- .setRuleKey(RULE_KEY.rule())
- .setStatus(Issue.STATUS_OPEN);
- Settings projectSettings;
- ProjectSettingsRepository projectSettingsRepository = mock(ProjectSettingsRepository.class);
- UserIndex userIndex = mock(UserIndex.class);
-
- // output
- IssueCache outputIssues;
-
- @Before
- public void setUp() throws IOException {
- when(ruleCache.get(RULE_KEY)).thenReturn(rule);
- outputIssues = new IssueCache(temp.newFile(), System2.INSTANCE);
- projectSettings = new Settings();
- when(projectSettingsRepository.getProjectSettings(PROJECT_KEY)).thenReturn(projectSettings);
- sut = new IssueComputation(ruleCache, lineCache, scmAccountCache, outputIssues, userIndex, projectSettingsRepository, reportReader);
- }
-
- @After
- public void after() {
- sut.afterReportProcessing();
- }
-
- @Test
- public void store_issues_on_disk() {
- process();
-
- assertThat(Iterators.getOnlyElement(outputIssues.traverse()).key()).isEqualTo("ISSUE_A");
- }
-
- @Test
- public void copy_rule_tags_on_new_issues() {
- inputIssue.setIsNew(true);
- rule.setTags(ImmutableSet.of("bug", "performance"));
- rule.setSystemTags(ImmutableSet.of("blocker"));
-
- process();
-
- assertThat(Iterators.getOnlyElement(outputIssues.traverse()).tags()).containsOnly("blocker", "bug", "performance");
- }
-
- @Test
- public void do_not_copy_rule_tags_on_existing_issues() {
- inputIssue.setIsNew(false);
- rule.setTags(ImmutableSet.of("bug", "performance"));
- rule.setSystemTags(ImmutableSet.of("blocker"));
-
- process();
-
- assertThat(Iterators.getOnlyElement(outputIssues.traverse()).tags()).isEmpty();
- }
-
- @Test
- public void guess_author_of_new_issues() {
- inputIssue.setIsNew(true);
- inputIssue.setLine(3);
- when(lineCache.lineAuthor(3)).thenReturn("charlie");
-
- process();
-
- assertThat(Iterators.getOnlyElement(outputIssues.traverse()).authorLogin()).isEqualTo("charlie");
- }
-
- @Test
- public void do_not_fail_if_missing_author_for_new_issues() {
- inputIssue.setIsNew(true);
- inputIssue.setLine(3);
- when(lineCache.lineAuthor(3)).thenReturn(null);
-
- process();
-
- assertThat(Iterators.getOnlyElement(outputIssues.traverse()).authorLogin()).isNull();
- }
-
- @Test
- public void do_not_guess_author_of_existing_issues() {
- inputIssue.setIsNew(false);
- inputIssue.setLine(3);
- when(lineCache.lineAuthor(3)).thenReturn("charlie");
-
- process();
-
- assertThat(Iterators.getOnlyElement(outputIssues.traverse()).authorLogin()).isNull();
- }
-
- @Test
- public void auto_assign_new_issues() {
- inputIssue.setIsNew(true);
- inputIssue.setAuthorLogin("charlie");
- when(scmAccountCache.getNullable("charlie")).thenReturn("char.lie");
-
- process();
-
- assertThat(Iterators.getOnlyElement(outputIssues.traverse()).assignee()).isEqualTo("char.lie");
- }
-
- @Test
- public void do_not_auto_assign_existing_issues() {
- inputIssue.setIsNew(false);
- inputIssue.setAuthorLogin("charlie");
- when(scmAccountCache.getNullable("charlie")).thenReturn("char.lie");
-
- process();
-
- assertThat(Iterators.getOnlyElement(outputIssues.traverse()).assignee()).isNull();
- }
-
- @Test
- public void do_not_override_author_and_assignee_set_by_old_batch_plugins() {
- inputIssue.setIsNew(true);
-
- // these fields were provided during project analysis, for instance
- // by developer cockpit or issue-assign plugins
- inputIssue.setAuthorLogin("charlie");
- inputIssue.setAssignee("cabu");
-
- process();
-
- // keep the values, without trying to update them
- DefaultIssue cachedIssue = Iterators.getOnlyElement(outputIssues.traverse());
- assertThat(cachedIssue.assignee()).isEqualTo("cabu");
- assertThat(cachedIssue.authorLogin()).isEqualTo("charlie");
- verifyZeroInteractions(scmAccountCache);
- }
-
- @Test
- public void assign_default_assignee_when_available() {
- inputIssue.setIsNew(true);
- String wolinski = "wolinski";
- projectSettings.setProperty(CoreProperties.DEFAULT_ISSUE_ASSIGNEE, wolinski);
- when(userIndex.getNullableByLogin(wolinski)).thenReturn(new UserDoc());
-
- process();
-
- assertThat(Iterators.getOnlyElement(outputIssues.traverse()).assignee()).isEqualTo(wolinski);
- assertThat(logTester.logs()).doesNotContain(String.format("the %s property was set with an unknown login: %s", CoreProperties.DEFAULT_ISSUE_ASSIGNEE, wolinski));
- }
-
- @Test
- public void do_not_assign_default_assignee_when_not_found_in_index() {
- inputIssue.setIsNew(true);
- String wolinski = "wolinski";
- projectSettings.setProperty(CoreProperties.DEFAULT_ISSUE_ASSIGNEE, wolinski);
- when(userIndex.getNullableByLogin(wolinski)).thenReturn(null);
-
- process();
-
- assertThat(Iterators.getOnlyElement(outputIssues.traverse()).assignee()).isNull();
- assertThat(logTester.logs()).contains(String.format("the %s property was set with an unknown login: %s", CoreProperties.DEFAULT_ISSUE_ASSIGNEE, wolinski));
- }
-
- private void process() {
- sut.processComponentIssues(Arrays.asList(inputIssue.build()), "FILE_A", 1, PROJECT_KEY, "PROJECT_UUID");
- }
-}
+///*
+// * 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.ImmutableSet;
+//import com.google.common.collect.Iterators;
+//import java.io.IOException;
+//import java.util.Arrays;
+//import org.junit.After;
+//import org.junit.Before;
+//import org.junit.Rule;
+//import org.junit.Test;
+//import org.junit.rules.TemporaryFolder;
+//import org.sonar.api.CoreProperties;
+//import org.sonar.api.config.Settings;
+//import org.sonar.api.issue.Issue;
+//import org.sonar.core.issue.DefaultIssue;
+//import org.sonar.api.rule.RuleKey;
+//import org.sonar.api.utils.System2;
+//import org.sonar.api.utils.log.LogTester;
+//import org.sonar.batch.protocol.output.BatchReport;
+//import org.sonar.core.rule.RuleDto;
+//import org.sonar.server.computation.batch.BatchReportReaderRule;
+//import org.sonar.server.computation.component.ProjectSettingsRepository;
+//import org.sonar.server.user.index.UserDoc;
+//import org.sonar.server.user.index.UserIndex;
+//
+//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;
+//
+//public class IssueComputationTest {
+//
+// private static final RuleKey RULE_KEY = RuleKey.of("squid", "R1");
+// private static final String PROJECT_KEY = "PROJECT_KEY";
+//
+// @Rule
+// public TemporaryFolder temp = new TemporaryFolder();
+// @Rule
+// public LogTester logTester = new LogTester();
+// @Rule
+// public BatchReportReaderRule reportReader = new BatchReportReaderRule();
+//
+// IssueComputation sut;
+//
+// // inputs
+// RuleCache ruleCache = mock(RuleCache.class);
+// SourceAuthorsHolder lineCache = mock(SourceAuthorsHolder.class);
+// ScmAccountToUser scmAccountToUser = mock(ScmAccountToUser.class);
+// RuleDto rule = new RuleDto().setRepositoryKey(RULE_KEY.repository()).setRuleKey(RULE_KEY.rule());
+// BatchReport.Issue.Builder inputIssue = BatchReport.Issue.newBuilder()
+// .setUuid("ISSUE_A")
+// .setRuleRepository(RULE_KEY.repository())
+// .setRuleKey(RULE_KEY.rule())
+// .setStatus(Issue.STATUS_OPEN);
+// Settings projectSettings;
+// ProjectSettingsRepository projectSettingsRepository = mock(ProjectSettingsRepository.class);
+// UserIndex userIndex = mock(UserIndex.class);
+//
+// // output
+// DeprecatedIssueCache outputIssues;
+//
+// @Before
+// public void setUp() throws IOException {
+// when(ruleCache.get(RULE_KEY)).thenReturn(rule);
+// outputIssues = new DeprecatedIssueCache(temp.newFile(), System2.INSTANCE);
+// projectSettings = new Settings();
+// when(projectSettingsRepository.getProjectSettings(PROJECT_KEY)).thenReturn(projectSettings);
+// sut = new IssueComputation(ruleCache, lineCache, scmAccountToUser, outputIssues, userIndex, projectSettingsRepository, reportReader);
+// }
+//
+// @After
+// public void after() {
+// sut.afterReportProcessing();
+// }
+//
+// @Test
+// public void store_issues_on_disk() {
+// process();
+//
+// assertThat(Iterators.getOnlyElement(outputIssues.traverse()).key()).isEqualTo("ISSUE_A");
+// }
+//
+// @Test
+// public void copy_rule_tags_on_new_issues() {
+// inputIssue.setIsNew(true);
+// rule.setTags(ImmutableSet.of("bug", "performance"));
+// rule.setSystemTags(ImmutableSet.of("blocker"));
+//
+// process();
+//
+// assertThat(Iterators.getOnlyElement(outputIssues.traverse()).tags()).containsOnly("blocker", "bug", "performance");
+// }
+//
+// @Test
+// public void do_not_copy_rule_tags_on_existing_issues() {
+// inputIssue.setIsNew(false);
+// rule.setTags(ImmutableSet.of("bug", "performance"));
+// rule.setSystemTags(ImmutableSet.of("blocker"));
+//
+// process();
+//
+// assertThat(Iterators.getOnlyElement(outputIssues.traverse()).tags()).isEmpty();
+// }
+//
+// @Test
+// public void guess_author_of_new_issues() {
+// inputIssue.setIsNew(true);
+// inputIssue.setLine(3);
+// when(lineCache.lineAuthor(3)).thenReturn("charlie");
+//
+// process();
+//
+// assertThat(Iterators.getOnlyElement(outputIssues.traverse()).authorLogin()).isEqualTo("charlie");
+// }
+//
+// @Test
+// public void do_not_fail_if_missing_author_for_new_issues() {
+// inputIssue.setIsNew(true);
+// inputIssue.setLine(3);
+// when(lineCache.lineAuthor(3)).thenReturn(null);
+//
+// process();
+//
+// assertThat(Iterators.getOnlyElement(outputIssues.traverse()).authorLogin()).isNull();
+// }
+//
+// @Test
+// public void do_not_guess_author_of_existing_issues() {
+// inputIssue.setIsNew(false);
+// inputIssue.setLine(3);
+// when(lineCache.lineAuthor(3)).thenReturn("charlie");
+//
+// process();
+//
+// assertThat(Iterators.getOnlyElement(outputIssues.traverse()).authorLogin()).isNull();
+// }
+//
+// @Test
+// public void auto_assign_new_issues() {
+// inputIssue.setIsNew(true);
+// inputIssue.setAuthorLogin("charlie");
+// when(scmAccountToUser.getNullable("charlie")).thenReturn("char.lie");
+//
+// process();
+//
+// assertThat(Iterators.getOnlyElement(outputIssues.traverse()).assignee()).isEqualTo("char.lie");
+// }
+//
+// @Test
+// public void do_not_auto_assign_existing_issues() {
+// inputIssue.setIsNew(false);
+// inputIssue.setAuthorLogin("charlie");
+// when(scmAccountToUser.getNullable("charlie")).thenReturn("char.lie");
+//
+// process();
+//
+// assertThat(Iterators.getOnlyElement(outputIssues.traverse()).assignee()).isNull();
+// }
+//
+// @Test
+// public void do_not_override_author_and_assignee_set_by_old_batch_plugins() {
+// inputIssue.setIsNew(true);
+//
+// // these fields were provided during project analysis, for instance
+// // by developer cockpit or issue-assign plugins
+// inputIssue.setAuthorLogin("charlie");
+// inputIssue.setAssignee("cabu");
+//
+// process();
+//
+// // keep the values, without trying to update them
+// DefaultIssue cachedIssue = Iterators.getOnlyElement(outputIssues.traverse());
+// assertThat(cachedIssue.assignee()).isEqualTo("cabu");
+// assertThat(cachedIssue.authorLogin()).isEqualTo("charlie");
+// verifyZeroInteractions(scmAccountToUser);
+// }
+//
+// @Test
+// public void assign_default_assignee_when_available() {
+// inputIssue.setIsNew(true);
+// String wolinski = "wolinski";
+// projectSettings.setProperty(CoreProperties.DEFAULT_ISSUE_ASSIGNEE, wolinski);
+// when(userIndex.getNullableByLogin(wolinski)).thenReturn(new UserDoc());
+//
+// process();
+//
+// assertThat(Iterators.getOnlyElement(outputIssues.traverse()).assignee()).isEqualTo(wolinski);
+// assertThat(logTester.logs()).doesNotContain(String.format("the %s property was set with an unknown login: %s", CoreProperties.DEFAULT_ISSUE_ASSIGNEE, wolinski));
+// }
+//
+// @Test
+// public void do_not_assign_default_assignee_when_not_found_in_index() {
+// inputIssue.setIsNew(true);
+// String wolinski = "wolinski";
+// projectSettings.setProperty(CoreProperties.DEFAULT_ISSUE_ASSIGNEE, wolinski);
+// when(userIndex.getNullableByLogin(wolinski)).thenReturn(null);
+//
+// process();
+//
+// assertThat(Iterators.getOnlyElement(outputIssues.traverse()).assignee()).isNull();
+// assertThat(logTester.logs()).contains(String.format("the %s property was set with an unknown login: %s", CoreProperties.DEFAULT_ISSUE_ASSIGNEE, wolinski));
+// }
+//
+// private void process() {
+// sut.processComponentIssues(Arrays.asList(inputIssue.build()), "FILE_A", 1, PROJECT_KEY, "PROJECT_UUID");
+// }
+//}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueCounterTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueCounterTest.java
new file mode 100644
index 00000000000..67988ec1f5a
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueCounterTest.java
@@ -0,0 +1,266 @@
+/*
+ * 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 IssueCounterTest {
+
+ 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;
+ IssueCounter sut;
+
+ @Before
+ public void setUp() throws Exception {
+ initMetrics();
+ measureRepository = new MeasureRepositoryImpl(null, reportReader, metricRepository);
+
+ sut = new IssueCounter(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);
+ assertVariation(FILE1, NEW_BLOCKER_ISSUES_METRIC, period.getIndex(), 0);
+ assertVariation(FILE1, NEW_MAJOR_ISSUES_METRIC, period.getIndex(), 0);
+
+ assertVariation(PROJECT, NEW_ISSUES_METRIC, period.getIndex(), 1);
+ assertVariation(PROJECT, NEW_CRITICAL_ISSUES_METRIC, period.getIndex(), 1);
+ assertVariation(PROJECT, NEW_BLOCKER_ISSUES_METRIC, period.getIndex(), 0);
+ assertVariation(PROJECT, NEW_MAJOR_ISSUES_METRIC, period.getIndex(), 0);
+ }
+
+ 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/issue/NewDebtAggregatorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/NewDebtAggregatorTest.java
new file mode 100644
index 00000000000..cf864b8a8e0
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/NewDebtAggregatorTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.base.Optional;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.utils.Duration;
+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.component.DumbComponent;
+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.period.Period;
+import org.sonar.server.computation.period.PeriodsHolderRule;
+import org.sonar.server.db.DbClient;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.anyList;
+import static org.mockito.Matchers.same;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+import static org.sonar.api.CoreProperties.TIMEMACHINE_MODE_PREVIOUS_ANALYSIS;
+import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
+
+public class NewDebtAggregatorTest {
+
+ private static final Period PERIOD = new Period(1, TIMEMACHINE_MODE_PREVIOUS_ANALYSIS, null, 1_500_000_000L, 1000L);
+
+ Component file = DumbComponent.builder(Component.Type.FILE, 1).setUuid("FILE").build();
+ Component project = DumbComponent.builder(Component.Type.PROJECT, 2).setUuid("PROJECT").addChildren(file).build();
+
+ NewDebtCalculator calculator = mock(NewDebtCalculator.class);
+
+ @org.junit.Rule
+ public PeriodsHolderRule periodsHolder = new PeriodsHolderRule();
+
+ DbClient dbClient = mock(DbClient.class, Mockito.RETURNS_DEEP_STUBS);
+
+ @org.junit.Rule
+ public MetricRepositoryRule metricRepository = new MetricRepositoryRule().add(CoreMetrics.NEW_TECHNICAL_DEBT);
+
+ @org.junit.Rule
+ public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create();
+
+ NewDebtAggregator underTest = new NewDebtAggregator(calculator, periodsHolder, dbClient, metricRepository, measureRepository);
+
+ @Test
+ public void sum_new_debt_of_issues() {
+ periodsHolder.setPeriods(PERIOD);
+ DefaultIssue unresolved1 = new DefaultIssue().setDebt(Duration.create(10));
+ DefaultIssue unresolved2 = new DefaultIssue().setDebt(Duration.create(30));
+ DefaultIssue unresolvedWithoutDebt = new DefaultIssue().setDebt(null);
+ DefaultIssue resolved = new DefaultIssue().setDebt(Duration.create(50)).setResolution(RESOLUTION_FIXED);
+ when(calculator.calculate(same(unresolved1), anyList(), same(PERIOD))).thenReturn(4L);
+ when(calculator.calculate(same(unresolved2), anyList(), same(PERIOD))).thenReturn(3L);
+ verifyNoMoreInteractions(calculator);
+
+ underTest.beforeComponent(file, mock(Tracking.class));
+ underTest.onIssue(file, unresolved1);
+ underTest.onIssue(file, unresolved2);
+ underTest.onIssue(file, unresolvedWithoutDebt);
+ underTest.onIssue(file, resolved);
+ underTest.afterComponent(file);
+
+ Measure newDebtMeasure = newDebtMeasure(file).get();
+ assertThat(newDebtMeasure.getVariations().getVariation(PERIOD.getIndex())).isEqualTo(3 + 4);
+ assertThat(newDebtMeasure.getVariations().hasVariation(PERIOD.getIndex() + 1)).isFalse();
+ }
+
+ private Optional<Measure> newDebtMeasure(Component component) {
+ return measureRepository.getRawMeasure(component, metricRepository.getByKey(CoreMetrics.NEW_TECHNICAL_DEBT_KEY));
+ }
+
+ @Test
+ public void aggregate_new_debt_of_children() {
+
+ }
+
+ @Test
+ public void no_measures_if_no_periods() throws Exception {
+ periodsHolder.setPeriods();
+ DefaultIssue unresolved = new DefaultIssue().setDebt(Duration.create(10));
+ verifyZeroInteractions(calculator);
+
+ underTest.beforeComponent(file, mock(Tracking.class));
+ underTest.onIssue(file, unresolved);
+ underTest.afterComponent(file);
+
+ assertThat(newDebtMeasure(file).isPresent()).isFalse();
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/NewDebtCalculatorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/NewDebtCalculatorTest.java
new file mode 100644
index 00000000000..37add915d09
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/NewDebtCalculatorTest.java
@@ -0,0 +1,125 @@
+/*
+ * 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.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import javax.annotation.Nullable;
+import org.junit.Test;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.utils.Duration;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.FieldDiffs;
+import org.sonar.core.issue.db.IssueChangeDto;
+import org.sonar.server.computation.period.Period;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class NewDebtCalculatorTest {
+
+ private static final int HOURS_IN_DAY = 8;
+ private static final Duration ONE_DAY = Duration.create(HOURS_IN_DAY * 60 * 60L);
+ private static final Duration TWO_DAYS = Duration.create(2 * HOURS_IN_DAY * 60 * 60L);
+ private static final Duration FOUR_DAYS = Duration.create(4 * HOURS_IN_DAY * 60 * 60L);
+ private static final Duration FIVE_DAYS = Duration.create(5 * HOURS_IN_DAY * 60 * 60L);
+ private static final Duration TEN_DAYS = Duration.create(10 * HOURS_IN_DAY * 60 * 60L);
+ private static final long PERIOD_DATE = 150000000L;
+ private static final long SNAPSHOT_ID = 1000L;
+ private static final Period PERIOD = new Period(1, CoreProperties.TIMEMACHINE_MODE_PREVIOUS_VERSION, null, PERIOD_DATE, SNAPSHOT_ID);
+
+ DefaultIssue issue = new DefaultIssue();
+ NewDebtCalculator underTest = new NewDebtCalculator();
+
+ /**
+ * New debt is the value of the debt when issue is created during the period
+ */
+ @Test
+ public void total_debt_if_issue_created_during_period() {
+ issue.setDebt(TWO_DAYS).setCreationDate(new Date(PERIOD_DATE + 10000));
+
+ long newDebt = underTest.calculate(issue, Collections.<IssueChangeDto>emptyList(), PERIOD);
+
+ assertThat(newDebt).isEqualTo(TWO_DAYS.toMinutes());
+ }
+
+ @Test
+ public void new_debt_if_issue_created_before_period() throws Exception {
+ // creation: 1d
+ // before period: increased to 2d
+ // after period: increased to 5d, decreased to 4d then increased to 10d
+ // -> new debt is 10d - 2d = 8d
+ issue.setDebt(TEN_DAYS).setCreationDate(new Date(PERIOD_DATE - 10000));
+ List<IssueChangeDto> changelog = Arrays.asList(
+ newDebtChangelog(ONE_DAY.toMinutes(), TWO_DAYS.toMinutes(), PERIOD_DATE - 9000),
+ newDebtChangelog(TWO_DAYS.toMinutes(), FIVE_DAYS.toMinutes(), PERIOD_DATE + 10000),
+ newDebtChangelog(FIVE_DAYS.toMinutes(), FOUR_DAYS.toMinutes(), PERIOD_DATE + 20000),
+ newDebtChangelog(FOUR_DAYS.toMinutes(), TEN_DAYS.toMinutes(), PERIOD_DATE + 30000)
+ );
+
+ long newDebt = underTest.calculate(issue, changelog, PERIOD);
+
+ assertThat(newDebt).isEqualTo(TEN_DAYS.toMinutes() - TWO_DAYS.toMinutes());
+ }
+
+ @Test
+ public void new_debt_is_positive() throws Exception {
+ // creation: 1d
+ // before period: increased to 10d
+ // after period: decreased to 2d
+ // -> new debt is 2d - 10d = -8d -> 0d
+ issue.setDebt(TWO_DAYS).setCreationDate(new Date(PERIOD_DATE - 10000));
+ List<IssueChangeDto> changelog = Arrays.asList(
+ newDebtChangelog(ONE_DAY.toMinutes(), TEN_DAYS.toMinutes(), PERIOD_DATE - 9000),
+ newDebtChangelog(TEN_DAYS.toMinutes(), TWO_DAYS.toMinutes(), PERIOD_DATE + 30000)
+ );
+
+ long newDebt = underTest.calculate(issue, changelog, PERIOD);
+
+ assertThat(newDebt).isEqualTo(0L);
+ }
+
+ @Test
+ public void guess_initial_debt_when_first_change_is_after_period() throws Exception {
+ // creation: 1d
+ // after period: increased to 2d, then to 5d
+ // -> new debt is 5d - 1d = 4d
+ issue.setDebt(FIVE_DAYS).setCreationDate(new Date(PERIOD_DATE - 10000));
+ List<IssueChangeDto> changelog = Arrays.asList(
+ newDebtChangelog(ONE_DAY.toMinutes(), TWO_DAYS.toMinutes(), PERIOD_DATE + 20000),
+ newDebtChangelog(TWO_DAYS.toMinutes(), FIVE_DAYS.toMinutes(), PERIOD_DATE + 30000)
+ );
+
+ long newDebt = underTest.calculate(issue, changelog, PERIOD);
+
+ assertThat(newDebt).isEqualTo(FIVE_DAYS.toMinutes() - ONE_DAY.toMinutes());
+ }
+
+
+ private static IssueChangeDto newDebtChangelog(long previousValue, long value, @Nullable Long date) {
+ FieldDiffs diffs = new FieldDiffs().setDiff("technicalDebt", previousValue, value);
+ if (date != null) {
+ diffs.setCreationDate(new Date(date));
+ }
+ return new IssueChangeDto().setIssueChangeCreationDate(date).setChangeData(diffs.toString());
+ }
+
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/RuleCacheLoaderTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/RuleCacheLoaderTest.java
index d65db7e5307..0457c5dc0f2 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/RuleCacheLoaderTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/RuleCacheLoaderTest.java
@@ -19,26 +19,34 @@
*/
package org.sonar.server.computation.issue;
+import java.util.Collections;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
+import org.junit.experimental.categories.Category;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.utils.System2;
+import org.sonar.batch.protocol.output.BatchReport;
import org.sonar.core.persistence.DbTester;
+import org.sonar.server.computation.batch.BatchReportReaderRule;
import org.sonar.server.db.DbClient;
import org.sonar.server.rule.db.RuleDao;
+import org.sonar.test.DbTests;
-import java.util.Collections;
-
+import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
+@Category(DbTests.class)
public class RuleCacheLoaderTest {
@ClassRule
public static DbTester dbTester = new DbTester();
+ @org.junit.Rule
+ public BatchReportReaderRule reportReader = new BatchReportReaderRule();
+
@Before
public void setUp() {
dbTester.truncateTables();
@@ -46,18 +54,31 @@ public class RuleCacheLoaderTest {
@Test
public void load_by_key() {
+ BatchReport.Metadata metadata = BatchReport.Metadata.newBuilder()
+ .addAllActiveRuleKey(asList("java:JAV01")).build();
+ reportReader.setMetadata(metadata);
+
dbTester.prepareDbUnit(getClass(), "shared.xml");
DbClient dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new RuleDao(mock(System2.class)));
- RuleCacheLoader loader = new RuleCacheLoader(dbClient);
+ RuleCacheLoader loader = new RuleCacheLoader(dbClient, reportReader);
+
+ Rule javaRule = loader.load(RuleKey.of("java", "JAV01"));
+ assertThat(javaRule.getName()).isEqualTo("Java One");
+ assertThat(javaRule.isActivated()).isTrue();
- assertThat(loader.load(RuleKey.of("squid", "R001")).getName()).isEqualTo("Rule One");
- assertThat(loader.load(RuleKey.of("squid", "MISSING"))).isNull();
+ Rule jsRule = loader.load(RuleKey.of("js", "JS01"));
+ assertThat(jsRule.getName()).isEqualTo("JS One");
+ assertThat(jsRule.isActivated()).isFalse();
+
+ assertThat(loader.load(RuleKey.of("java", "MISSING"))).isNull();
}
@Test
public void load_by_keys_is_not_supported() {
+ reportReader.setMetadata(BatchReport.Metadata.newBuilder().build());
+
DbClient dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new RuleDao(mock(System2.class)));
- RuleCacheLoader loader = new RuleCacheLoader(dbClient);
+ RuleCacheLoader loader = new RuleCacheLoader(dbClient, reportReader);
try {
loader.loadAll(Collections.<RuleKey>emptyList());
fail();
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/RuleCacheTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/RuleRepositoryImplTest.java
index 57172b9b9dc..a7f5c5fba82 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/RuleCacheTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/RuleRepositoryImplTest.java
@@ -20,21 +20,27 @@
package org.sonar.server.computation.issue;
import org.junit.Test;
-import org.sonar.api.rule.RuleKey;
-import org.sonar.core.rule.RuleDto;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.mockito.internal.verification.VerificationModeFactory.times;
+import static org.sonar.server.rule.RuleTesting.XOO_X1;
-public class RuleCacheTest {
+public class RuleRepositoryImplTest {
+
+ RuleCacheLoader cacheLoader = mock(RuleCacheLoader.class);
+ RuleRepositoryImpl underTest = new RuleRepositoryImpl(cacheLoader);
@Test
- public void ruleName() {
- RuleCacheLoader loader = mock(RuleCacheLoader.class);
- when(loader.load(RuleKey.of("squid", "R002"))).thenReturn(new RuleDto().setName("Rule Two"));
- RuleCache cache = new RuleCache(loader);
- assertThat(cache.ruleName(RuleKey.of("squid", "R001"))).isNull();
- assertThat(cache.ruleName(RuleKey.of("squid", "R002"))).isEqualTo("Rule Two");
+ public void getByKey() throws Exception {
+ when(cacheLoader.load(XOO_X1)).thenReturn(new DumbRule(XOO_X1));
+
+ assertThat(underTest.getByKey(XOO_X1).getKey()).isEqualTo(XOO_X1);
+
+ // second call -> get from cache
+ assertThat(underTest.getByKey(XOO_X1).getKey()).isEqualTo(XOO_X1);
+ verify(cacheLoader, times(1)).load(XOO_X1);
}
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/RuleRepositoryRule.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/RuleRepositoryRule.java
new file mode 100644
index 00000000000..860ab256f05
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/RuleRepositoryRule.java
@@ -0,0 +1,59 @@
+/*
+ * 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.HashMap;
+import java.util.Map;
+import org.junit.rules.ExternalResource;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.server.exceptions.NotFoundException;
+
+import static java.util.Objects.requireNonNull;
+
+public class RuleRepositoryRule extends ExternalResource implements RuleRepository {
+
+ private final Map<RuleKey, Rule> rulesByKey = new HashMap<>();
+
+ @Override
+ protected void after() {
+ rulesByKey.clear();
+ }
+
+ @Override
+ public Rule getByKey(RuleKey key) {
+ Rule rule = rulesByKey.get(key);
+ if (rule == null) {
+ throw new NotFoundException();
+ }
+ return rule;
+ }
+
+ public DumbRule add(RuleKey key) {
+ DumbRule rule = new DumbRule(key);
+ rulesByKey.put(key, rule);
+ return rule;
+ }
+
+ public RuleRepositoryRule add(DumbRule rule) {
+ rulesByKey.put(rule.getKey(), rule);
+ return this;
+ }
+
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/RuleTagsCopierTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/RuleTagsCopierTest.java
new file mode 100644
index 00000000000..df0a3d4c9d9
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/RuleTagsCopierTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.Sets;
+import java.util.Collections;
+import org.junit.Test;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.server.computation.component.Component;
+
+import static java.util.Arrays.asList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.sonar.server.rule.RuleTesting.XOO_X1;
+
+public class RuleTagsCopierTest {
+
+ DumbRule rule = new DumbRule(XOO_X1);
+
+ @org.junit.Rule
+ public RuleRepositoryRule ruleRepository = new RuleRepositoryRule().add(rule);
+
+ DefaultIssue issue = new DefaultIssue().setRuleKey(rule.getKey());
+ RuleTagsCopier underTest = new RuleTagsCopier(ruleRepository);
+
+ @Test
+ public void copy_tags_if_new_rule() throws Exception {
+ rule.setTags(Sets.newHashSet("bug", "performance"));
+ issue.setNew(true);
+
+ underTest.onIssue(mock(Component.class), issue);
+
+ assertThat(issue.tags()).containsExactly("bug", "performance");
+ }
+
+ @Test
+ public void do_not_copy_tags_if_existing_rule() throws Exception {
+ rule.setTags(Sets.newHashSet("bug", "performance"));
+ issue.setNew(false).setTags(asList("misra"));
+
+ underTest.onIssue(mock(Component.class), issue);
+
+ assertThat(issue.tags()).containsExactly("misra");
+ }
+
+ @Test
+ public void do_not_copy_tags_if_existing_rule_without_tags() throws Exception {
+ rule.setTags(Sets.newHashSet("bug", "performance"));
+ issue.setNew(false).setTags(Collections.<String>emptyList());
+
+ underTest.onIssue(mock(Component.class), issue);
+
+ assertThat(issue.tags()).isEmpty();
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/ScmAccountCacheLoaderTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/ScmAccountToUserLoaderTest.java
index 6f0fd8ae6fc..40c58734445 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/ScmAccountCacheLoaderTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/ScmAccountToUserLoaderTest.java
@@ -19,27 +19,28 @@
*/
package org.sonar.server.computation.issue;
+import java.util.Collections;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
import org.sonar.api.config.Settings;
-import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.api.utils.log.LoggerLevel;
import org.sonar.server.es.EsTester;
import org.sonar.server.user.index.UserIndex;
import org.sonar.server.user.index.UserIndexDefinition;
-import java.util.Collections;
-
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-public class ScmAccountCacheLoaderTest {
+public class ScmAccountToUserLoaderTest {
@ClassRule
public static EsTester esTester = new EsTester().addDefinitions(new UserIndexDefinition(new Settings()));
+ @ClassRule
+ public static LogTester logTester = new LogTester();
+
@Before
public void setUp() {
esTester.truncateIndices();
@@ -49,7 +50,7 @@ public class ScmAccountCacheLoaderTest {
public void load_login_for_scm_account() throws Exception {
esTester.putDocuments("users", "user", getClass(), "charlie.json");
UserIndex index = new UserIndex(esTester.client());
- ScmAccountCacheLoader loader = new ScmAccountCacheLoader(index);
+ ScmAccountToUserLoader loader = new ScmAccountToUserLoader(index);
assertThat(loader.load("missing")).isNull();
assertThat(loader.load("jesuis@charlie.com")).isEqualTo("charlie");
@@ -59,17 +60,16 @@ public class ScmAccountCacheLoaderTest {
public void warn_if_multiple_users_share_same_scm_account() throws Exception {
esTester.putDocuments("users", "user", getClass(), "charlie.json", "charlie_conflict.json");
UserIndex index = new UserIndex(esTester.client());
- Logger log = mock(Logger.class);
- ScmAccountCacheLoader loader = new ScmAccountCacheLoader(index, log);
+ ScmAccountToUserLoader loader = new ScmAccountToUserLoader(index);
assertThat(loader.load("charlie")).isNull();
- verify(log).warn("Multiple users share the SCM account 'charlie': charlie, another.charlie");
+ assertThat(logTester.logs(LoggerLevel.WARN)).contains("Multiple users share the SCM account 'charlie': charlie, another.charlie");
}
@Test
public void load_by_multiple_scm_accounts_is_not_supported_yet() {
UserIndex index = new UserIndex(esTester.client());
- ScmAccountCacheLoader loader = new ScmAccountCacheLoader(index);
+ ScmAccountToUserLoader loader = new ScmAccountToUserLoader(index);
try {
loader.loadAll(Collections.<String>emptyList());
fail();
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/measure/BatchMeasureToMeasureTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/measure/BatchMeasureToMeasureTest.java
index fbf88dbdf0d..c00155eb02f 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/measure/BatchMeasureToMeasureTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/measure/BatchMeasureToMeasureTest.java
@@ -23,20 +23,14 @@ import com.google.common.base.Optional;
import com.tngtech.java.junit.dataprovider.DataProvider;
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
import com.tngtech.java.junit.dataprovider.UseDataProvider;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.sonar.api.rule.RuleKey;
import org.sonar.batch.protocol.output.BatchReport;
-import org.sonar.core.rule.RuleDto;
-import org.sonar.server.computation.issue.RuleCache;
import org.sonar.server.computation.metric.Metric;
import org.sonar.server.computation.metric.MetricImpl;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.guava.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
@RunWith(DataProviderRunner.class)
public class BatchMeasureToMeasureTest {
@@ -48,19 +42,9 @@ public class BatchMeasureToMeasureTest {
private static final Metric SOME_LEVEL_METRIC = new MetricImpl(42, "level", "name", Metric.MetricType.LEVEL);
private static final String SOME_DATA = "some_data man!";
- private static final String SOME_ALERT_TEXT = "some alert text_be_careFul!";
private static final BatchReport.Measure EMPTY_BATCH_MEASURE = BatchReport.Measure.newBuilder().build();
- private static final RuleKey SOME_RULE_KEY = RuleKey.of("A", "B");
- private static final int SOME_RULE_ID = 9513;
- private static final RuleDto SOME_RULE = new RuleDto().setRuleKey(SOME_RULE_KEY.toString()).setId(SOME_RULE_ID);
- private RuleCache ruleCache = mock(RuleCache.class);
- private BatchMeasureToMeasure underTest = new BatchMeasureToMeasure(ruleCache);
-
- @Before
- public void setUp() throws Exception {
- when(ruleCache.get(SOME_RULE_KEY)).thenReturn(SOME_RULE);
- }
+ private BatchMeasureToMeasure underTest = new BatchMeasureToMeasure();
@Test
public void toMeasure_returns_absent_for_null_argument() {
@@ -77,25 +61,6 @@ public class BatchMeasureToMeasureTest {
underTest.toMeasure(null, null);
}
- @Test(expected = IllegalArgumentException.class)
- public void toMeasure_throws_IAE_if_batch_Measure_has_both_ruleKey_and_characteristicId() {
- underTest.toMeasure(BatchReport.Measure.newBuilder().setRuleKey(SOME_RULE_KEY.toString()).setCharactericId(42).build(), SOME_STRING_METRIC);
- }
-
- @Test
- public void toMeasure_maps_characteristicId_if_present() {
- BatchReport.Measure batchMeasure = BatchReport.Measure.newBuilder().setCharactericId(42).build();
-
- assertThat(underTest.toMeasure(batchMeasure, SOME_STRING_METRIC).get().getCharacteristicId()).isEqualTo(42);
- }
-
- @Test
- public void toMeasure_maps_ruleKey_if_present() {
- BatchReport.Measure batchMeasure = BatchReport.Measure.newBuilder().setRuleKey(SOME_RULE_KEY.toString()).build();
-
- assertThat(underTest.toMeasure(batchMeasure, SOME_STRING_METRIC).get().getRuleId()).isEqualTo(SOME_RULE_ID);
- }
-
@Test
public void toMeasure_returns_no_value_if_dto_has_no_string_value_for_LEVEL_Metric() {
Optional<Measure> measure = underTest.toMeasure(EMPTY_BATCH_MEASURE, SOME_LEVEL_METRIC);
@@ -138,8 +103,6 @@ public class BatchMeasureToMeasureTest {
public void toMeasure_for_LEVEL_Metric_maps_QualityGateStatus() {
BatchReport.Measure batchMeasure = BatchReport.Measure.newBuilder()
.setStringValue(Measure.Level.OK.name())
- .setAlertStatus(Measure.Level.ERROR.name())
- .setAlertText(SOME_ALERT_TEXT)
.build();
Optional<Measure> measure = underTest.toMeasure(batchMeasure, SOME_LEVEL_METRIC);
@@ -147,8 +110,6 @@ public class BatchMeasureToMeasureTest {
assertThat(measure).isPresent();
assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.LEVEL);
assertThat(measure.get().getLevelValue()).isEqualTo(Measure.Level.OK);
- assertThat(measure.get().getQualityGateStatus().getStatus()).isEqualTo(Measure.Level.ERROR);
- assertThat(measure.get().getQualityGateStatus().getText()).isEqualTo(SOME_ALERT_TEXT);
}
@Test
@@ -182,7 +143,6 @@ public class BatchMeasureToMeasureTest {
BatchReport.Measure batchMeasure = BatchReport.Measure.newBuilder()
.setIntValue(10)
.setStringValue(SOME_DATA)
- .setAlertStatus(Measure.Level.OK.name()).setAlertText(SOME_ALERT_TEXT)
.build();
Optional<Measure> measure = underTest.toMeasure(batchMeasure, SOME_INT_METRIC);
@@ -191,8 +151,6 @@ public class BatchMeasureToMeasureTest {
assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.INT);
assertThat(measure.get().getIntValue()).isEqualTo(10);
assertThat(measure.get().getData()).isEqualTo(SOME_DATA);
- assertThat(measure.get().getQualityGateStatus().getStatus()).isEqualTo(Measure.Level.OK);
- assertThat(measure.get().getQualityGateStatus().getText()).isEqualTo(SOME_ALERT_TEXT);
}
@Test
@@ -217,7 +175,6 @@ public class BatchMeasureToMeasureTest {
BatchReport.Measure batchMeasure = BatchReport.Measure.newBuilder()
.setLongValue(10l)
.setStringValue(SOME_DATA)
- .setAlertStatus(Measure.Level.OK.name()).setAlertText(SOME_ALERT_TEXT)
.build();
Optional<Measure> measure = underTest.toMeasure(batchMeasure, SOME_LONG_METRIC);
@@ -226,8 +183,6 @@ public class BatchMeasureToMeasureTest {
assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.LONG);
assertThat(measure.get().getLongValue()).isEqualTo(10);
assertThat(measure.get().getData()).isEqualTo(SOME_DATA);
- assertThat(measure.get().getQualityGateStatus().getStatus()).isEqualTo(Measure.Level.OK);
- assertThat(measure.get().getQualityGateStatus().getText()).isEqualTo(SOME_ALERT_TEXT);
}
@Test
@@ -243,7 +198,6 @@ public class BatchMeasureToMeasureTest {
BatchReport.Measure batchMeasure = BatchReport.Measure.newBuilder()
.setDoubleValue(10.6395d)
.setStringValue(SOME_DATA)
- .setAlertStatus(Measure.Level.OK.name()).setAlertText(SOME_ALERT_TEXT)
.build();
Optional<Measure> measure = underTest.toMeasure(batchMeasure, SOME_DOUBLE_METRIC);
@@ -252,8 +206,6 @@ public class BatchMeasureToMeasureTest {
assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.DOUBLE);
assertThat(measure.get().getDoubleValue()).isEqualTo(10.6d);
assertThat(measure.get().getData()).isEqualTo(SOME_DATA);
- assertThat(measure.get().getQualityGateStatus().getStatus()).isEqualTo(Measure.Level.OK);
- assertThat(measure.get().getQualityGateStatus().getText()).isEqualTo(SOME_ALERT_TEXT);
}
@Test
@@ -281,7 +233,7 @@ public class BatchMeasureToMeasureTest {
@Test
public void toMeasure_maps_data_and_alert_properties_in_dto_for_Boolean_metric() {
BatchReport.Measure batchMeasure = BatchReport.Measure.newBuilder()
- .setBooleanValue(true).setStringValue(SOME_DATA).setAlertStatus(Measure.Level.OK.name()).setAlertText(SOME_ALERT_TEXT).build();
+ .setBooleanValue(true).setStringValue(SOME_DATA).build();
Optional<Measure> measure = underTest.toMeasure(batchMeasure, SOME_BOOLEAN_METRIC);
@@ -289,8 +241,6 @@ public class BatchMeasureToMeasureTest {
assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.BOOLEAN);
assertThat(measure.get().getBooleanValue()).isTrue();
assertThat(measure.get().getData()).isEqualTo(SOME_DATA);
- assertThat(measure.get().getQualityGateStatus().getStatus()).isEqualTo(Measure.Level.OK);
- assertThat(measure.get().getQualityGateStatus().getText()).isEqualTo(SOME_ALERT_TEXT);
}
@Test
@@ -305,7 +255,6 @@ public class BatchMeasureToMeasureTest {
public void toMeasure_maps_alert_properties_in_dto_for_String_Metric() {
BatchReport.Measure batchMeasure = BatchReport.Measure.newBuilder()
.setStringValue(SOME_DATA)
- .setAlertStatus(Measure.Level.OK.name()).setAlertText(SOME_ALERT_TEXT)
.build();
Optional<Measure> measure = underTest.toMeasure(batchMeasure, SOME_STRING_METRIC);
@@ -314,8 +263,6 @@ public class BatchMeasureToMeasureTest {
assertThat(measure.get().getValueType()).isEqualTo(Measure.ValueType.STRING);
assertThat(measure.get().getStringValue()).isEqualTo(SOME_DATA);
assertThat(measure.get().getData()).isEqualTo(SOME_DATA);
- assertThat(measure.get().getQualityGateStatus().getStatus()).isEqualTo(Measure.Level.OK);
- assertThat(measure.get().getQualityGateStatus().getText()).isEqualTo(SOME_ALERT_TEXT);
}
@DataProvider
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureRepositoryImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureRepositoryImplTest.java
index ad6f55545e4..67d0a10e685 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureRepositoryImplTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureRepositoryImplTest.java
@@ -49,7 +49,6 @@ import org.sonar.server.computation.batch.BatchReportReaderRule;
import org.sonar.server.computation.component.Component;
import org.sonar.server.computation.component.DumbComponent;
import org.sonar.server.computation.debt.Characteristic;
-import org.sonar.server.computation.issue.RuleCache;
import org.sonar.server.computation.metric.Metric;
import org.sonar.server.computation.metric.MetricImpl;
import org.sonar.server.computation.metric.MetricRepository;
@@ -89,16 +88,15 @@ public class MeasureRepositoryImplTest {
private static final Measure SOME_MEASURE = Measure.newMeasureBuilder().create("some value");
private static final String SOME_DATA = "some data";
private static final RuleDto SOME_RULE = RuleDto.createFor(RuleKey.of("A", "1")).setId(963);
- private static final Characteristic SOME_CHARACTERISTIC = new Characteristic(741, "key");
+ private static final Characteristic SOME_CHARACTERISTIC = new Characteristic(741, "key", null);
private DbClient dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new MeasureDao(), new SnapshotDao(), new MetricDao(), new ComponentDao());
private MetricRepository metricRepository = mock(MetricRepository.class);
- private RuleCache ruleCache = mock(RuleCache.class);
- private MeasureRepositoryImpl underTest = new MeasureRepositoryImpl(dbClient, reportReader, metricRepository, ruleCache);
+ private MeasureRepositoryImpl underTest = new MeasureRepositoryImpl(dbClient, reportReader, metricRepository);
private DbClient mockedDbClient = mock(DbClient.class);
private BatchReportReader mockBatchReportReader = mock(BatchReportReader.class);
- private MeasureRepositoryImpl underTestWithMock = new MeasureRepositoryImpl(mockedDbClient, mockBatchReportReader, metricRepository, ruleCache);
+ private MeasureRepositoryImpl underTestWithMock = new MeasureRepositoryImpl(mockedDbClient, mockBatchReportReader, metricRepository);
@CheckForNull
private DbSession dbSession;
@@ -227,7 +225,7 @@ public class MeasureRepositoryImplTest {
@Nullable
@Override
public Object[] apply(Measure input) {
- return new Measure[] { input };
+ return new Measure[] {input};
}
}).toArray(Object[].class);
}
@@ -254,8 +252,8 @@ public class MeasureRepositoryImplTest {
fail("An IllegalArgumentException should have been raised");
} catch (IllegalArgumentException e) {
assertThat(e).hasMessage(format(
- "Measure's ValueType (%s) is not consistent with the Measure's ValueType (%s)",
- measure.getValueType(), metricType.getValueType()));
+ "Measure's ValueType (%s) is not consistent with the Measure's ValueType (%s)",
+ measure.getValueType(), metricType.getValueType()));
}
}
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureRepositoryRule.java b/server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureRepositoryRule.java
index 381579a9e1e..28e2332d115 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureRepositoryRule.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureRepositoryRule.java
@@ -167,11 +167,19 @@ public class MeasureRepositoryRule extends ExternalResource implements MeasureRe
return Optional.fromNullable(rawMeasures.get(new InternalKey(component, metric, rule.getId(), null)));
}
+ public Optional<Measure> getRawRuleMeasure(Component component, Metric metric, int ruleId) {
+ return Optional.fromNullable(rawMeasures.get(new InternalKey(component, metric, ruleId, null)));
+ }
+
@Override
public Optional<Measure> getRawMeasure(Component component, Metric metric, Characteristic characteristic) {
return Optional.fromNullable(rawMeasures.get(new InternalKey(component, metric, null, characteristic.getId())));
}
+ public Optional<Measure> getRawCharacteristicMeasure(Component component, Metric metric, int characteristicId) {
+ return Optional.fromNullable(rawMeasures.get(new InternalKey(component, metric, null, characteristicId)));
+ }
+
@Override
public SetMultimap<String, Measure> getRawMeasures(Component component) {
ImmutableSetMultimap.Builder<String, Measure> builder = ImmutableSetMultimap.builder();
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/java/org/sonar/server/computation/step/FeedDebtModelStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/FeedDebtModelStepTest.java
index e57a43636a8..429b2c3282f 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/FeedDebtModelStepTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/FeedDebtModelStepTest.java
@@ -17,7 +17,6 @@
* 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.Collection;
@@ -76,12 +75,11 @@ public class FeedDebtModelStepTest extends BaseStepTest {
sut.execute();
- Collection<Characteristic> rootChars = debtModelHolder.findRootCharacteristics();
+ Collection<Characteristic> rootChars = debtModelHolder.getRootCharacteristics();
assertThat(rootChars).extracting("id").containsOnly(1);
assertThat(rootChars).extracting("key").containsOnly("PORTABILITY");
- Collection<Characteristic> subChars = debtModelHolder.findSubCharacteristicsByRootKey("PORTABILITY");
- assertThat(subChars).extracting("id").containsOnly(2, 3);
- assertThat(subChars).extracting("key").containsOnly("COMPILER_RELATED_PORTABILITY", "HARDWARE_RELATED_PORTABILITY");
+ Characteristic subChar = debtModelHolder.getCharacteristicById(1);
+ assertThat(subChar).isNotNull();
}
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/FillMeasuresWithVariationsStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/FillMeasuresWithVariationsStepTest.java
index 9d4d6504b99..786fdcce131 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/FillMeasuresWithVariationsStepTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/FillMeasuresWithVariationsStepTest.java
@@ -35,9 +35,7 @@ import org.sonar.core.measure.db.MeasureDto;
import org.sonar.core.metric.db.MetricDto;
import org.sonar.core.persistence.DbSession;
import org.sonar.core.persistence.DbTester;
-import org.sonar.core.rule.RuleDto;
import org.sonar.core.technicaldebt.db.CharacteristicDao;
-import org.sonar.core.technicaldebt.db.CharacteristicDto;
import org.sonar.server.component.ComponentTesting;
import org.sonar.server.component.db.ComponentDao;
import org.sonar.server.component.db.SnapshotDao;
@@ -45,9 +43,6 @@ 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.debt.Characteristic;
-import org.sonar.server.computation.issue.RuleCache;
-import org.sonar.server.computation.issue.RuleCacheLoader;
import org.sonar.server.computation.measure.Measure;
import org.sonar.server.computation.measure.MeasureRepository;
import org.sonar.server.computation.measure.MeasureRepositoryImpl;
@@ -60,7 +55,6 @@ import org.sonar.server.computation.period.PeriodsHolderRule;
import org.sonar.server.db.DbClient;
import org.sonar.server.measure.persistence.MeasureDao;
import org.sonar.server.metric.persistence.MetricDao;
-import org.sonar.server.rule.RuleTesting;
import org.sonar.server.rule.db.RuleDao;
import org.sonar.test.DbTests;
@@ -119,7 +113,7 @@ public class FillMeasuresWithVariationsStepTest {
metricRepository = new MetricRepositoryImpl(dbClient);
metricRepository.start();
- measureRepository = new MeasureRepositoryImpl(dbClient, reportReader, metricRepository, new RuleCache(new RuleCacheLoader(dbClient)));
+ measureRepository = new MeasureRepositoryImpl(dbClient, reportReader, metricRepository);
sut = new FillMeasuresWithVariationsStep(dbClient, treeRootHolder, periodsHolder, metricRepository, measureRepository);
}
@@ -258,66 +252,6 @@ public class FillMeasuresWithVariationsStepTest {
}
@Test
- public void set_variation_on_rule_measure() throws Exception {
- SnapshotDto period1ProjectSnapshot = createForProject(PROJECT_DTO);
- dbClient.snapshotDao().insert(session, period1ProjectSnapshot);
-
- RuleDto rule1 = RuleTesting.newXooX1();
- RuleDto rule2 = RuleTesting.newXooX2();
- dbClient.ruleDao().insert(session, rule1, rule2);
-
- dbClient.measureDao().insert(session, newMeasureDto(ISSUES_METRIC.getId(), PROJECT_DTO.getId(), period1ProjectSnapshot.getId(), 60d));
- dbClient.measureDao().insert(session, newMeasureDto(ISSUES_METRIC.getId(), PROJECT_DTO.getId(), period1ProjectSnapshot.getId(), 40d).setRuleId(rule1.getId()));
- dbClient.measureDao().insert(session, newMeasureDto(ISSUES_METRIC.getId(), PROJECT_DTO.getId(), period1ProjectSnapshot.getId(), 20d).setRuleId(rule2.getId()));
- session.commit();
-
- periodsHolder.setPeriods(newPeriod(1, period1ProjectSnapshot));
-
- treeRootHolder.setRoot(PROJECT);
-
- measureRepository.add(PROJECT, toMetric(ISSUES_METRIC), Measure.newMeasureBuilder().create(80, null));
- measureRepository.add(PROJECT, toMetric(ISSUES_METRIC), Measure.newMeasureBuilder().forRule(rule1.getId()).create(45, null));
- measureRepository.add(PROJECT, toMetric(ISSUES_METRIC), Measure.newMeasureBuilder().forRule(rule2.getId()).create(35, null));
-
- sut.execute();
-
- assertThat(measureRepository.getRawMeasure(PROJECT, toMetric(ISSUES_METRIC)).get().getVariations().getVariation1()).isEqualTo(20d);
- assertThat(measureRepository.getRawMeasure(PROJECT, toMetric(ISSUES_METRIC), rule1).get().getVariations().getVariation1()).isEqualTo(5d);
- assertThat(measureRepository.getRawMeasure(PROJECT, toMetric(ISSUES_METRIC), rule2).get().getVariations().getVariation1()).isEqualTo(15d);
- }
-
- @Test
- public void set_variation_on_characteristic_measure() throws Exception {
- SnapshotDto period1ProjectSnapshot = createForProject(PROJECT_DTO);
- dbClient.snapshotDao().insert(session, period1ProjectSnapshot);
-
- CharacteristicDto char1 = new CharacteristicDto().setKey("PORTABILITY");
- CharacteristicDto char2 = new CharacteristicDto().setKey("MAINTAINABILITY");
- dbClient.debtCharacteristicDao().insert(session, char1, char2);
-
- dbClient.measureDao().insert(session, newMeasureDto(ISSUES_METRIC.getId(), PROJECT_DTO.getId(), period1ProjectSnapshot.getId(), 60d));
- dbClient.measureDao().insert(session, newMeasureDto(ISSUES_METRIC.getId(), PROJECT_DTO.getId(), period1ProjectSnapshot.getId(), 40d).setCharacteristicId(char1.getId()));
- dbClient.measureDao().insert(session, newMeasureDto(ISSUES_METRIC.getId(), PROJECT_DTO.getId(), period1ProjectSnapshot.getId(), 20d).setCharacteristicId(char2.getId()));
- session.commit();
-
- periodsHolder.setPeriods(newPeriod(1, period1ProjectSnapshot));
-
- treeRootHolder.setRoot(PROJECT);
-
- measureRepository.add(PROJECT, toMetric(ISSUES_METRIC), Measure.newMeasureBuilder().create(80, null));
- measureRepository.add(PROJECT, toMetric(ISSUES_METRIC), Measure.newMeasureBuilder().forCharacteristic(char1.getId()).create(45, null));
- measureRepository.add(PROJECT, toMetric(ISSUES_METRIC), Measure.newMeasureBuilder().forCharacteristic(char2.getId()).create(35, null));
-
- sut.execute();
-
- assertThat(measureRepository.getRawMeasure(PROJECT, toMetric(ISSUES_METRIC)).get().getVariations().getVariation1()).isEqualTo(20d);
- assertThat(measureRepository.getRawMeasure(PROJECT, toMetric(ISSUES_METRIC), new Characteristic(char1.getId(), char1.getKey())).get().getVariations().getVariation1())
- .isEqualTo(5d);
- assertThat(measureRepository.getRawMeasure(PROJECT, toMetric(ISSUES_METRIC), new Characteristic(char2.getId(), char2.getKey())).get().getVariations().getVariation1())
- .isEqualTo(15d);
- }
-
- @Test
public void read_measure_from_batch() throws Exception {
// Project
SnapshotDto period1ProjectSnapshot = createForProject(PROJECT_DTO);
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/ParseReportStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/ParseReportStepTest.java
deleted file mode 100644
index c1344850083..00000000000
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/ParseReportStepTest.java
+++ /dev/null
@@ -1,122 +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.io.IOException;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import org.junit.ClassRule;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.sonar.batch.protocol.Constants;
-import org.sonar.batch.protocol.output.BatchReport;
-import org.sonar.core.persistence.DbTester;
-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.IssueComputation;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-public class ParseReportStepTest extends BaseStepTest {
-
- private static final String PROJECT_KEY = "PROJECT_KEY";
-
- private static final List<BatchReport.Issue> ISSUES_ON_DELETED_COMPONENT = Arrays.asList(BatchReport.Issue.newBuilder()
- .setUuid("DELETED_ISSUE_UUID")
- .build());
-
- @Rule
- public TemporaryFolder temp = new TemporaryFolder();
- @Rule
- public BatchReportReaderRule reportReader = new BatchReportReaderRule();
- @Rule
- public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
-
- @ClassRule
- public static DbTester dbTester = new DbTester();
-
- IssueComputation issueComputation = mock(IssueComputation.class);
- ParseReportStep sut = new ParseReportStep(issueComputation, reportReader, treeRootHolder);
-
- @Test
- public void extract_report_from_db_and_browse_components() throws Exception {
- DumbComponent root = DumbComponent.builder(Component.Type.PROJECT, 1).setUuid("PROJECT_UUID").setKey(PROJECT_KEY).addChildren(
- DumbComponent.builder(Component.Type.FILE, 2).setUuid("FILE1_UUID").setKey("PROJECT_KEY:file1").build(),
- DumbComponent.builder(Component.Type.FILE, 3).setUuid("FILE2_UUID").setKey("PROJECT_KEY:file2").build())
- .build();
-
- generateReport();
-
- treeRootHolder.setRoot(root);
-
- sut.execute();
-
- assertThat(reportReader.readMetadata().getRootComponentRef()).isEqualTo(1);
- assertThat(reportReader.readMetadata().getDeletedComponentsCount()).isEqualTo(1);
-
- // verify that all components are processed (currently only for issues)
- verify(issueComputation).processComponentIssues(Collections.<BatchReport.Issue>emptyList(), "PROJECT_UUID", 1, PROJECT_KEY, "PROJECT_UUID");
- verify(issueComputation).processComponentIssues(Collections.<BatchReport.Issue>emptyList(), "FILE1_UUID", 2, PROJECT_KEY, "PROJECT_UUID");
- verify(issueComputation).processComponentIssues(Collections.<BatchReport.Issue>emptyList(), "FILE2_UUID", 3, PROJECT_KEY, "PROJECT_UUID");
- verify(issueComputation).processComponentIssues(ISSUES_ON_DELETED_COMPONENT, "DELETED_UUID", null, PROJECT_KEY, "PROJECT_UUID");
- verify(issueComputation).afterReportProcessing();
- }
-
- private void generateReport() throws IOException {
- // project and 2 files
- reportReader.setMetadata(BatchReport.Metadata.newBuilder()
- .setRootComponentRef(1)
- .setDeletedComponentsCount(1)
- .build());
-
- reportReader.putComponent(BatchReport.Component.newBuilder()
- .setRef(1)
- .setType(Constants.ComponentType.PROJECT)
- .addChildRef(2)
- .addChildRef(3)
- .build());
- reportReader.putComponent(BatchReport.Component.newBuilder()
- .setRef(2)
- .setType(Constants.ComponentType.FILE)
- .build());
- reportReader.putComponent(BatchReport.Component.newBuilder()
- .setRef(3)
- .setType(Constants.ComponentType.FILE)
- .build());
-
- // deleted components
- BatchReport.Issues.Builder issuesBuilder = BatchReport.Issues.newBuilder();
- issuesBuilder.setComponentRef(1);
- issuesBuilder.setComponentUuid("DELETED_UUID");
- issuesBuilder.addAllIssue(ISSUES_ON_DELETED_COMPONENT);
- reportReader.putDeletedIssues(1, issuesBuilder.build());
- }
-
- @Override
- protected ComputationStep step() {
- return sut;
- }
-}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistIssuesStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistIssuesStepTest.java
index f50bc0e2cd9..fbb5aae1c1a 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistIssuesStepTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistIssuesStepTest.java
@@ -27,18 +27,20 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.sonar.api.issue.Issue;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.DefaultIssueComment;
-import org.sonar.api.issue.internal.FieldDiffs;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.Severity;
import org.sonar.api.utils.System2;
+import org.sonar.batch.protocol.output.BatchReport;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.DefaultIssueComment;
+import org.sonar.core.issue.FieldDiffs;
import org.sonar.core.issue.db.UpdateConflictResolver;
import org.sonar.core.persistence.DbSession;
import org.sonar.core.persistence.DbTester;
+import org.sonar.server.computation.batch.BatchReportReaderRule;
import org.sonar.server.computation.issue.IssueCache;
-import org.sonar.server.computation.issue.RuleCache;
import org.sonar.server.computation.issue.RuleCacheLoader;
+import org.sonar.server.computation.issue.RuleRepositoryImpl;
import org.sonar.server.db.DbClient;
import org.sonar.server.issue.db.IssueDao;
import org.sonar.server.rule.db.RuleDao;
@@ -48,12 +50,17 @@ import static org.mockito.Mockito.when;
public class PersistIssuesStepTest extends BaseStepTest {
+ public static final long NOW = 1400000000000L;
+
@Rule
public TemporaryFolder temp = new TemporaryFolder();
@ClassRule
public static DbTester dbTester = new DbTester();
+ @Rule
+ public BatchReportReaderRule reportReader = new BatchReportReaderRule();
+
DbSession session;
DbClient dbClient;
@@ -74,11 +81,12 @@ public class PersistIssuesStepTest extends BaseStepTest {
dbTester.truncateTables();
session = dbTester.myBatis().openSession(false);
dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new IssueDao(dbTester.myBatis()), new RuleDao(system2));
-
issueCache = new IssueCache(temp.newFile(), System2.INSTANCE);
system2 = mock(System2.class);
- when(system2.now()).thenReturn(1400000000000L);
- step = new PersistIssuesStep(dbClient, system2, new UpdateConflictResolver(), new RuleCache(new RuleCacheLoader(dbClient)), issueCache);
+ when(system2.now()).thenReturn(NOW);
+ reportReader.setMetadata(BatchReport.Metadata.getDefaultInstance());
+
+ step = new PersistIssuesStep(dbClient, system2, new UpdateConflictResolver(), new RuleRepositoryImpl(new RuleCacheLoader(dbClient, reportReader)), issueCache);
}
@After
@@ -117,6 +125,7 @@ public class PersistIssuesStepTest extends BaseStepTest {
.setSeverity(Severity.BLOCKER)
.setStatus(Issue.STATUS_CLOSED)
.setResolution(Issue.RESOLUTION_FIXED)
+ .setSelectedAt(NOW)
.setNew(false)
.setChanged(true)
).close();
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistMeasuresStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistMeasuresStepTest.java
index 296dda1d018..627545565ae 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistMeasuresStepTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistMeasuresStepTest.java
@@ -29,41 +29,31 @@ import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
-import org.sonar.api.measures.Metric;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.utils.System2;
import org.sonar.api.utils.internal.Uuids;
import org.sonar.batch.protocol.Constants.MeasureValueType;
import org.sonar.batch.protocol.output.BatchReport;
import org.sonar.core.component.ComponentDto;
+import org.sonar.core.metric.db.MetricDto;
import org.sonar.core.persistence.DbSession;
import org.sonar.core.persistence.DbTester;
-import org.sonar.core.rule.RuleDto;
import org.sonar.server.component.db.ComponentDao;
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.DbIdsRepository;
import org.sonar.server.computation.component.DumbComponent;
-import org.sonar.server.computation.issue.RuleCache;
-import org.sonar.server.computation.issue.RuleCacheLoader;
import org.sonar.server.computation.measure.MeasureRepository;
import org.sonar.server.computation.measure.MeasureRepositoryImpl;
-import org.sonar.server.computation.metric.MetricRepositoryRule;
+import org.sonar.server.computation.metric.MetricRepositoryImpl;
import org.sonar.server.db.DbClient;
import org.sonar.server.measure.persistence.MeasureDao;
import org.sonar.server.metric.persistence.MetricDao;
-import org.sonar.server.rule.RuleTesting;
import org.sonar.server.rule.db.RuleDao;
import org.sonar.test.DbTests;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonar.api.measures.CoreMetrics.DUPLICATIONS_DATA;
-import static org.sonar.api.measures.CoreMetrics.DUPLICATIONS_DATA_KEY;
-import static org.sonar.api.measures.CoreMetrics.FILE_COMPLEXITY_DISTRIBUTION;
-import static org.sonar.api.measures.CoreMetrics.FILE_COMPLEXITY_DISTRIBUTION_KEY;
-import static org.sonar.api.measures.CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION;
-import static org.sonar.api.measures.CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION_KEY;
@Category(DbTests.class)
public class PersistMeasuresStepTest extends BaseStepTest {
@@ -72,31 +62,23 @@ public class PersistMeasuresStepTest extends BaseStepTest {
private static final String STRING_METRIC_KEY = "string-metric-key";
private static final String DOUBLE_METRIC_KEY = "double-metric-key";
private static final String OPTIMIZED_METRIC_KEY = "optimized-metric-key";
-
- private static final Metric STRING_METRIC = new Metric.Builder(STRING_METRIC_KEY, "String metric", Metric.ValueType.STRING).create();
- private static final Metric DOUBLE_METRIC = new Metric.Builder(DOUBLE_METRIC_KEY, "Double metric", Metric.ValueType.FLOAT).create();
-
private static final RuleKey RULE_KEY = RuleKey.of("repo", "rule-key");
-
private static final int PROJECT_REF = 1;
private static final int FILE_REF = 2;
@ClassRule
public static DbTester dbTester = new DbTester();
-
@Rule
public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
-
@Rule
public BatchReportReaderRule reportReader = new BatchReportReaderRule();
- @Rule
- public MetricRepositoryRule metricRepository = new MetricRepositoryRule();
-
DbClient dbClient;
DbSession session;
DbIdsRepository dbIdsRepository = new DbIdsRepository();
- RuleDto rule;
+ MetricDto stringMetric;
+ MetricDto doubleMetric;
+ MetricDto optimizedMetric;
ComponentDto projectDto;
ComponentDto fileDto;
@@ -109,12 +91,18 @@ public class PersistMeasuresStepTest extends BaseStepTest {
dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new MeasureDao(), new ComponentDao(), new MetricDao(), new RuleDao(System2.INSTANCE));
session = dbClient.openSession(false);
- rule = RuleTesting.newDto(RULE_KEY);
- dbClient.ruleDao().insert(session, rule);
+ stringMetric = new MetricDto().setValueType("STRING").setShortName("String metric").setKey(STRING_METRIC_KEY).setEnabled(true);
+ dbClient.metricDao().insert(session, stringMetric);
+ doubleMetric = new MetricDto().setValueType("FLOAT").setShortName("Double metric").setKey(DOUBLE_METRIC_KEY).setEnabled(true);
+ dbClient.metricDao().insert(session, doubleMetric);
+ optimizedMetric = new MetricDto().setValueType("BOOL").setShortName("Optimized metric").setKey(OPTIMIZED_METRIC_KEY).setEnabled(true).setOptimizedBestValue(true)
+ .setBestValue(1d);
+ dbClient.metricDao().insert(session, optimizedMetric);
session.commit();
- RuleCache ruleCache = new RuleCache(new RuleCacheLoader(dbClient));
- MeasureRepository measureRepository = new MeasureRepositoryImpl(dbClient, reportReader, metricRepository, ruleCache);
+ MetricRepositoryImpl metricRepository = new MetricRepositoryImpl(dbClient);
+ metricRepository.start();
+ MeasureRepository measureRepository = new MeasureRepositoryImpl(dbClient, reportReader, metricRepository);
session.commit();
sut = new PersistMeasuresStep(dbClient, metricRepository, dbIdsRepository, treeRootHolder, measureRepository);
@@ -139,9 +127,6 @@ public class PersistMeasuresStepTest extends BaseStepTest {
@Test
public void insert_measures_from_report() throws Exception {
- metricRepository.add(1, STRING_METRIC);
- metricRepository.add(2, DOUBLE_METRIC);
-
reportReader.putMeasures(PROJECT_REF, Arrays.asList(
BatchReport.Measure.newBuilder()
.setValueType(MeasureValueType.STRING)
@@ -151,11 +136,8 @@ public class PersistMeasuresStepTest extends BaseStepTest {
.setVariationValue3(3.3d)
.setVariationValue4(4.4d)
.setVariationValue5(5.5d)
- .setAlertStatus("WARN")
- .setAlertText("Open issues > 0")
.setDescription("measure-description")
.setMetricKey(STRING_METRIC_KEY)
- .setCharactericId(123456)
.build()));
reportReader.putMeasures(FILE_REF, Arrays.asList(
@@ -167,24 +149,21 @@ public class PersistMeasuresStepTest extends BaseStepTest {
.setVariationValue3(3.3d)
.setVariationValue4(4.4d)
.setVariationValue5(5.5d)
- .setAlertStatus("ERROR")
- .setAlertText("Blocker issues variation > 0")
.setDescription("measure-description")
.setMetricKey(DOUBLE_METRIC_KEY)
- .setRuleKey(RULE_KEY.toString())
.build()));
sut.execute();
session.commit();
- assertThat(dbTester.countRowsOfTable("project_measures")).isEqualTo(2);
+ assertThat(dbTester.countRowsOfTable("project_measures")).isEqualTo(FILE_REF);
List<Map<String, Object>> dtos = retrieveDtos();
Map<String, Object> dto = dtos.get(0);
assertThat(dto.get("snapshotId")).isEqualTo(3L);
assertThat(dto.get("componentId")).isEqualTo(projectDto.getId());
- assertThat(dto.get("metricId")).isEqualTo(1L);
+ assertThat(dto.get("metricId")).isEqualTo(stringMetric.getId().longValue());
assertThat(dto.get("ruleId")).isNull();
assertThat(dto.get("textValue")).isEqualTo("measure-data");
assertThat(dto.get("severity")).isNull();
@@ -192,8 +171,7 @@ public class PersistMeasuresStepTest extends BaseStepTest {
dto = dtos.get(PROJECT_REF);
assertThat(dto.get("snapshotId")).isEqualTo(4L);
assertThat(dto.get("componentId")).isEqualTo(fileDto.getId());
- assertThat(dto.get("metricId")).isEqualTo(2L);
- assertThat(dto.get("ruleId")).isEqualTo(rule.getId().longValue());
+ assertThat(dto.get("metricId")).isEqualTo(doubleMetric.getId().longValue());
assertThat(dto.get("characteristicId")).isNull();
assertThat(dto.get("value")).isEqualTo(123.1d);
assertThat(dto.get("severity")).isNull();
@@ -207,8 +185,6 @@ public class PersistMeasuresStepTest extends BaseStepTest {
@Test
public void bestValue_measure_of_bestValueOptimized_metrics_are_not_persisted() {
- metricRepository.add(1, new Metric.Builder(OPTIMIZED_METRIC_KEY, "Optimized metric", Metric.ValueType.BOOL).setOptimizedBestValue(true).setBestValue(1d).create());
-
reportReader.putMeasures(FILE_REF, Arrays.asList(
BatchReport.Measure.newBuilder()
.setValueType(MeasureValueType.BOOLEAN)
@@ -224,9 +200,6 @@ public class PersistMeasuresStepTest extends BaseStepTest {
@Test
public void empty_values_are_not_persisted() {
- metricRepository.add(1, STRING_METRIC);
- metricRepository.add(2, DOUBLE_METRIC);
-
reportReader.putMeasures(FILE_REF, Arrays.asList(
BatchReport.Measure.newBuilder()
.setValueType(MeasureValueType.STRING)
@@ -244,86 +217,6 @@ public class PersistMeasuresStepTest extends BaseStepTest {
assertThat(retrieveDtos()).isEmpty();
}
- @Test(expected = IllegalStateException.class)
- public void fail_with_ISE_when_trying_to_insert_forbidden_measures() throws Exception {
- metricRepository.add(1, DUPLICATIONS_DATA);
-
- reportReader.putMeasures(FILE_REF, Arrays.asList(
- BatchReport.Measure.newBuilder()
- .setValueType(MeasureValueType.STRING)
- .setStringValue("{duplications}")
- .setMetricKey(DUPLICATIONS_DATA_KEY)
- .build()));
-
- sut.execute();
- }
-
- @Test
- public void do_not_insert_file_complexity_distribution_metric_on_files() throws Exception {
- metricRepository.add(1, FILE_COMPLEXITY_DISTRIBUTION);
-
- reportReader.putMeasures(PROJECT_REF, Arrays.asList(
- BatchReport.Measure.newBuilder()
- .setValueType(MeasureValueType.STRING)
- .setStringValue("0=1;2=10")
- .setMetricKey(FILE_COMPLEXITY_DISTRIBUTION_KEY)
- .build()));
-
- // Should not be persisted
- reportReader.putMeasures(FILE_REF, Arrays.asList(
- BatchReport.Measure.newBuilder()
- .setValueType(MeasureValueType.STRING)
- .setStringValue("0=1;2=10")
- .setMetricKey(FILE_COMPLEXITY_DISTRIBUTION_KEY)
- .build()));
-
- sut.execute();
-
- session.commit();
-
- assertThat(dbTester.countRowsOfTable("project_measures")).isEqualTo(1);
-
- List<Map<String, Object>> dtos = retrieveDtos();
-
- Map<String, Object> dto = dtos.get(0);
- assertThat(dto.get("snapshotId")).isEqualTo(3L);
- assertThat(dto.get("componentId")).isEqualTo(projectDto.getId());
- assertThat(dto.get("textValue")).isEqualTo("0=1;2=10");
- }
-
- @Test
- public void do_not_insert_function_complexity_distribution_metric_on_files() throws Exception {
- metricRepository.add(1, FUNCTION_COMPLEXITY_DISTRIBUTION);
-
- reportReader.putMeasures(PROJECT_REF, Arrays.asList(
- BatchReport.Measure.newBuilder()
- .setValueType(MeasureValueType.STRING)
- .setStringValue("0=1;2=10")
- .setMetricKey(FUNCTION_COMPLEXITY_DISTRIBUTION_KEY)
- .build()));
-
- // Should not be persisted
- reportReader.putMeasures(FILE_REF, Arrays.asList(
- BatchReport.Measure.newBuilder()
- .setValueType(MeasureValueType.STRING)
- .setStringValue("0=1;2=10")
- .setMetricKey(FUNCTION_COMPLEXITY_DISTRIBUTION_KEY)
- .build()));
-
- sut.execute();
-
- session.commit();
-
- assertThat(dbTester.countRowsOfTable("project_measures")).isEqualTo(1);
-
- List<Map<String, Object>> dtos = retrieveDtos();
-
- Map<String, Object> dto = dtos.get(0);
- assertThat(dto.get("snapshotId")).isEqualTo(3L);
- assertThat(dto.get("componentId")).isEqualTo(projectDto.getId());
- assertThat(dto.get("textValue")).isEqualTo("0=1;2=10");
- }
-
private ComponentDto addComponent(String key) {
ComponentDto componentDto = new ComponentDto().setKey(key).setUuid(Uuids.create());
dbClient.componentDao().insert(session, componentDto);
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/SendIssueNotificationsStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/SendIssueNotificationsStepTest.java
index cbca0035cb0..b846287b270 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/SendIssueNotificationsStepTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/SendIssueNotificationsStepTest.java
@@ -25,18 +25,18 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.Mockito;
-import org.sonar.api.issue.internal.DefaultIssue;
import org.sonar.api.notifications.Notification;
import org.sonar.api.rule.Severity;
import org.sonar.api.utils.System2;
import org.sonar.batch.protocol.Constants;
import org.sonar.batch.protocol.output.BatchReport;
+import org.sonar.core.issue.DefaultIssue;
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.IssueCache;
-import org.sonar.server.computation.issue.RuleCache;
+import org.sonar.server.computation.issue.RuleRepository;
import org.sonar.server.issue.notification.IssueChangeNotification;
import org.sonar.server.issue.notification.NewIssuesNotification;
import org.sonar.server.issue.notification.NewIssuesNotificationFactory;
@@ -71,7 +71,7 @@ public class SendIssueNotificationsStepTest extends BaseStepTest {
public void setUp() throws Exception {
issueCache = new IssueCache(temp.newFile(), System2.INSTANCE);
NewIssuesNotificationFactory newIssuesNotificationFactory = mock(NewIssuesNotificationFactory.class, Mockito.RETURNS_DEEP_STUBS);
- sut = new SendIssueNotificationsStep(issueCache, mock(RuleCache.class), treeRootHolder, notifService, reportReader, newIssuesNotificationFactory);
+ sut = new SendIssueNotificationsStep(issueCache, mock(RuleRepository.class), treeRootHolder, notifService, reportReader, newIssuesNotificationFactory);
treeRootHolder.setRoot(DumbComponent.builder(Component.Type.PROJECT, 1).setUuid(PROJECT_UUID).setKey(PROJECT_KEY).build());
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ActionServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ActionServiceTest.java
index 29f8b0d496d..1738484d0c8 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/ActionServiceTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ActionServiceTest.java
@@ -28,8 +28,8 @@ import org.sonar.api.issue.Issue;
import org.sonar.api.issue.action.Actions;
import org.sonar.api.issue.action.Function;
import org.sonar.api.issue.condition.Condition;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.IssueChangeContext;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.IssueChangeContext;
import org.sonar.core.component.ComponentDto;
import org.sonar.core.issue.IssueUpdater;
import org.sonar.core.issue.db.IssueDto;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/AddTagsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/AddTagsActionTest.java
index d525662371c..326a7fbb2e9 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/AddTagsActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/AddTagsActionTest.java
@@ -26,8 +26,8 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.Matchers;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.IssueChangeContext;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.IssueChangeContext;
import org.sonar.core.issue.IssueUpdater;
import java.util.Collection;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/AssignActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/AssignActionTest.java
index cec5baa96ce..cd5bba96b08 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/AssignActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/AssignActionTest.java
@@ -25,8 +25,8 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.issue.Issue;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.IssueChangeContext;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.IssueChangeContext;
import org.sonar.api.user.User;
import org.sonar.api.user.UserFinder;
import org.sonar.core.issue.IssueUpdater;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/CommentActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/CommentActionTest.java
index 9453a822e98..09ad3c81e84 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/CommentActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/CommentActionTest.java
@@ -25,8 +25,8 @@ import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.sonar.api.issue.Issue;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.IssueChangeContext;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.IssueChangeContext;
import org.sonar.core.issue.IssueUpdater;
import org.sonar.server.tester.AnonymousMockUserSession;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java
index 93d9c9d3696..392c7182df1 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java
@@ -33,8 +33,8 @@ import org.mockito.ArgumentCaptor;
import org.sonar.api.issue.ActionPlan;
import org.sonar.api.issue.Issue;
import org.sonar.api.issue.action.Action;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.FieldDiffs;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.FieldDiffs;
import org.sonar.api.user.User;
import org.sonar.api.web.UserRole;
import org.sonar.core.issue.DefaultActionPlan;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueChangelogFormatterTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueChangelogFormatterTest.java
index fbdec123925..d678470bcbe 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueChangelogFormatterTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueChangelogFormatterTest.java
@@ -28,7 +28,7 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.sonar.api.i18n.I18n;
-import org.sonar.api.issue.internal.FieldDiffs;
+import org.sonar.core.issue.FieldDiffs;
import org.sonar.api.utils.Duration;
import org.sonar.api.utils.Durations;
import org.sonar.server.tester.UserSessionRule;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueChangelogServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueChangelogServiceTest.java
index 7723f024513..f06bea4e57b 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueChangelogServiceTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueChangelogServiceTest.java
@@ -27,8 +27,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.FieldDiffs;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.FieldDiffs;
import org.sonar.api.user.User;
import org.sonar.api.user.UserFinder;
import org.sonar.core.issue.db.IssueChangeDao;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueCommentServiceMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueCommentServiceMediumTest.java
index a343a039412..d0bdd68ea9d 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueCommentServiceMediumTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueCommentServiceMediumTest.java
@@ -27,7 +27,7 @@ import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.issue.Issue;
-import org.sonar.api.issue.internal.DefaultIssueComment;
+import org.sonar.core.issue.DefaultIssueComment;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.RuleStatus;
import org.sonar.api.security.DefaultGroups;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueCommentServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueCommentServiceTest.java
index c4921fddb2a..51d64252b1f 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueCommentServiceTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueCommentServiceTest.java
@@ -28,9 +28,9 @@ import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.DefaultIssueComment;
-import org.sonar.api.issue.internal.IssueChangeContext;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.DefaultIssueComment;
+import org.sonar.core.issue.IssueChangeContext;
import org.sonar.core.issue.IssueUpdater;
import org.sonar.core.issue.db.IssueChangeDao;
import org.sonar.core.issue.db.IssueChangeDto;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/PlanActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/PlanActionTest.java
index 9da70d4d606..f47fe9ceb9d 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/PlanActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/PlanActionTest.java
@@ -26,8 +26,8 @@ import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.issue.ActionPlan;
import org.sonar.api.issue.Issue;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.IssueChangeContext;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.IssueChangeContext;
import org.sonar.core.issue.DefaultActionPlan;
import org.sonar.core.issue.IssueUpdater;
import org.sonar.server.issue.actionplan.ActionPlanService;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/RemoveTagsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/RemoveTagsActionTest.java
index 7280f972a34..1c966c06cb6 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/RemoveTagsActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/RemoveTagsActionTest.java
@@ -26,8 +26,8 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.Matchers;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.IssueChangeContext;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.IssueChangeContext;
import org.sonar.core.issue.IssueUpdater;
import java.util.Collection;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ServerIssueStorageTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ServerIssueStorageTest.java
index a6ea52cbaf3..e295b3c965f 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/ServerIssueStorageTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ServerIssueStorageTest.java
@@ -23,9 +23,9 @@ package org.sonar.server.issue;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.DefaultIssueComment;
-import org.sonar.api.issue.internal.IssueChangeContext;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.DefaultIssueComment;
+import org.sonar.core.issue.IssueChangeContext;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rules.Rule;
import org.sonar.api.rules.RuleFinder;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/SetSeverityActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/SetSeverityActionTest.java
index 093e41fa9c1..07de711cfc3 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/SetSeverityActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/SetSeverityActionTest.java
@@ -26,8 +26,8 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.issue.Issue;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.IssueChangeContext;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.IssueChangeContext;
import org.sonar.api.web.UserRole;
import org.sonar.core.issue.IssueUpdater;
import org.sonar.server.tester.UserSessionRule;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/TransitionActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/TransitionActionTest.java
index 86945151ade..99c17905fb7 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/TransitionActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/TransitionActionTest.java
@@ -26,8 +26,8 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.issue.Issue;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.IssueChangeContext;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.IssueChangeContext;
import org.sonar.core.issue.workflow.IssueWorkflow;
import org.sonar.core.issue.workflow.Transition;
import org.sonar.server.tester.UserSessionRule;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/actionplan/ActionPlanServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/actionplan/ActionPlanServiceTest.java
index 4d17c6e4667..500f85a8348 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/actionplan/ActionPlanServiceTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/actionplan/ActionPlanServiceTest.java
@@ -28,8 +28,8 @@ import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.sonar.api.issue.ActionPlan;
import org.sonar.api.issue.Issue;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.IssueChangeContext;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.IssueChangeContext;
import org.sonar.api.web.UserRole;
import org.sonar.core.issue.ActionPlanStats;
import org.sonar.core.issue.DefaultActionPlan;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/notification/IssueChangeNotificationTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/notification/IssueChangeNotificationTest.java
index acd55086472..49a57b9db6d 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/notification/IssueChangeNotificationTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/notification/IssueChangeNotificationTest.java
@@ -20,8 +20,8 @@
package org.sonar.server.issue.notification;
import org.junit.Test;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.FieldDiffs;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.FieldDiffs;
import org.sonar.core.component.ComponentDto;
import static org.assertj.core.api.Assertions.assertThat;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationTest.java
index 63c35a2bdeb..61e2e63bf0b 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationTest.java
@@ -24,7 +24,7 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import org.junit.Test;
import org.mockito.Mockito;
-import org.sonar.api.issue.internal.DefaultIssue;
+import org.sonar.core.issue.DefaultIssue;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.Severity;
import org.sonar.api.utils.DateUtils;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/notification/NewIssuesStatisticsTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/notification/NewIssuesStatisticsTest.java
index 215297d17ed..08f6851849b 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/notification/NewIssuesStatisticsTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/notification/NewIssuesStatisticsTest.java
@@ -22,7 +22,7 @@ package org.sonar.server.issue.notification;
import com.google.common.collect.Lists;
import org.junit.Test;
-import org.sonar.api.issue.internal.DefaultIssue;
+import org.sonar.core.issue.DefaultIssue;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.Severity;
import org.sonar.api.utils.Duration;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueActionsWriterTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueActionsWriterTest.java
index 34aab044a41..d80a5470154 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueActionsWriterTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueActionsWriterTest.java
@@ -29,7 +29,7 @@ import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.sonar.api.issue.Issue;
import org.sonar.api.issue.action.Action;
-import org.sonar.api.issue.internal.DefaultIssue;
+import org.sonar.core.issue.DefaultIssue;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.utils.text.JsonWriter;
import org.sonar.api.web.UserRole;
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/ShowActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/ShowActionTest.java
index 61e7a768110..5d1268abc73 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/ShowActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/ShowActionTest.java
@@ -31,9 +31,9 @@ import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.sonar.api.i18n.I18n;
import org.sonar.api.issue.Issue;
-import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.issue.internal.DefaultIssueComment;
-import org.sonar.api.issue.internal.FieldDiffs;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.DefaultIssueComment;
+import org.sonar.core.issue.FieldDiffs;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.server.debt.internal.DefaultDebtCharacteristic;
import org.sonar.api.user.User;
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/computation/issue/RuleCacheLoaderTest/shared.xml b/server/sonar-server/src/test/resources/org/sonar/server/computation/issue/RuleCacheLoaderTest/shared.xml
index 97ee8157a48..075ab07d14f 100644
--- a/server/sonar-server/src/test/resources/org/sonar/server/computation/issue/RuleCacheLoaderTest/shared.xml
+++ b/server/sonar-server/src/test/resources/org/sonar/server/computation/issue/RuleCacheLoaderTest/shared.xml
@@ -1,11 +1,11 @@
<dataset>
- <rules id="1" name="Rule One" plugin_name="squid" plugin_rule_key="R001"
+ <rules id="1" name="JS One" plugin_name="js" plugin_rule_key="JS01"
plugin_config_key="[null]" description="[null]" priority="4"
status="READY"
is_template="[false]" template_id="[null]"
tags="[null]" system_tags="[null]"/>
- <rules id="2" name="Rule Two" plugin_name="squid" plugin_rule_key="R002"
+ <rules id="2" name="Java One" plugin_name="java" plugin_rule_key="JAV01"
plugin_config_key="[null]" description="[null]" priority="4"
status="READY"
is_template="[false]" template_id="[null]"
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/server/sonar-server/src/test/resources/org/sonar/server/computation/step/PersistIssuesStepTest/close_issue-result.xml b/server/sonar-server/src/test/resources/org/sonar/server/computation/step/PersistIssuesStepTest/close_issue-result.xml
index e15b08c3e25..a4c029b4606 100644
--- a/server/sonar-server/src/test/resources/org/sonar/server/computation/step/PersistIssuesStepTest/close_issue-result.xml
+++ b/server/sonar-server/src/test/resources/org/sonar/server/computation/step/PersistIssuesStepTest/close_issue-result.xml
@@ -24,7 +24,7 @@
issue_creation_date="[null]"
issue_update_date="[null]"
issue_close_date="[null]"
- created_at="1100000000000"
+ created_at="1300000000000"
updated_at="1400000000000"
/>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/computation/step/PersistIssuesStepTest/insert_new_issue.xml b/server/sonar-server/src/test/resources/org/sonar/server/computation/step/PersistIssuesStepTest/insert_new_issue.xml
index 0d56aa5ec07..9f3a7f46ea4 100644
--- a/server/sonar-server/src/test/resources/org/sonar/server/computation/step/PersistIssuesStepTest/insert_new_issue.xml
+++ b/server/sonar-server/src/test/resources/org/sonar/server/computation/step/PersistIssuesStepTest/insert_new_issue.xml
@@ -4,7 +4,7 @@
plugin_config_key="[null]" priority="0" is_template="[true]" language="xoo" template_id="[null]"
note_data="[null]" note_user_login="[null]" note_created_at="[null]" note_updated_at="[null]"
characteristic_id="100" default_characteristic_id="101"
- remediation_function="LINEAR" default_remediation_function="LINEAR_OFFSET"
+ remediation_function="LINEAR_OFFSET" default_remediation_function="LINEAR_OFFSET"
remediation_coeff="1h" default_remediation_coeff="5d"
remediation_offset="5min" default_remediation_offset="10h"
effort_to_fix_description="[null]" description_format="MARKDOWN"
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/computation/step/PersistIssuesStepTest/shared.xml b/server/sonar-server/src/test/resources/org/sonar/server/computation/step/PersistIssuesStepTest/shared.xml
index 23f7ab20677..3cee2f8e087 100644
--- a/server/sonar-server/src/test/resources/org/sonar/server/computation/step/PersistIssuesStepTest/shared.xml
+++ b/server/sonar-server/src/test/resources/org/sonar/server/computation/step/PersistIssuesStepTest/shared.xml
@@ -36,8 +36,8 @@
issue_creation_date="[null]"
issue_update_date="[null]"
issue_close_date="[null]"
- created_at="1100000000000"
- updated_at="1100000000000"
+ created_at="1300000000000"
+ updated_at="1300000000000"
/>
</dataset>