aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainerImpl.java10
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/debt/DebtModelHolder.java3
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/debt/DebtModelHolderImpl.java17
-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.java28
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/DebtAggregator.java51
-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.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueCache.java1
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueCounter.java22
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueLifecycle.java22
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueVisitor.java5
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/NewDebtAggregator.java18
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/NewDebtCalculator.java24
-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.java (renamed from sonar-core/src/main/java/org/sonar/core/computation/package-info.java)10
-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.java13
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/ScmAccountToUserLoader.java34
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/issue/TrackerRawInputFactory.java32
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/period/PeriodsHolderImpl.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/step/ComputationSteps.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/step/IntegrateIssuesStep.java24
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistIssuesStep.java17
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/step/SendIssueNotificationsStep.java11
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/IssueQueryService.java8
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/debt/DebtModelHolderImplTest.java209
-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.java124
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueCounterTest.java (renamed from server/sonar-server/src/test/java/org/sonar/server/computation/issue/CountIssuesListenerTest.java)10
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/issue/NewDebtAggregatorTest.java443
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/issue/NewDebtCalculatorTest.java233
-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.java13
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/issue/SourceAuthorsHolderTest.java137
-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/FeedDebtModelStepTest.java153
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistIssuesStepTest.java12
-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/resources/org/sonar/server/computation/issue/RuleCacheLoaderTest/shared.xml4
-rw-r--r--server/sonar-server/src/test/resources/org/sonar/server/computation/step/PersistIssuesStepTest/insert_new_issue.xml2
-rw-r--r--sonar-batch-protocol/src/main/gen-java/org/sonar/batch/protocol/output/BatchReport.java193
-rw-r--r--sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/BatchReportReader.java1
-rw-r--r--sonar-batch-protocol/src/main/protobuf/batch_report.proto5
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java29
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/phases/PhaseExecutor.java2
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/report/IssuesPublisher.java5
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureValueCoder.java1
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java109
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/report/IssuesPublisherTest.java2
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeDao.java4
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeMapper.java2
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/db/UpdateConflictResolver.java2
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/tracking/BlockHashSequence.java5
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/tracking/BlockRecognizer.java20
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/tracking/Tracker.java65
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/tracking/Tracking.java3
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/workflow/IssueWorkflow.java4
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/workflow/SetClosed.java3
-rw-r--r--sonar-core/src/main/java/org/sonar/core/measure/db/MeasureDto.java26
-rw-r--r--sonar-core/src/main/java/org/sonar/core/rule/RuleKeyFunctions.java49
-rw-r--r--sonar-core/src/main/resources/org/sonar/core/issue/db/IssueChangeMapper.xml4
-rw-r--r--sonar-core/src/test/java/org/sonar/core/issue/DefaultIssueTest.java219
-rw-r--r--sonar-core/src/test/java/org/sonar/core/issue/FieldDiffsTest.java149
-rw-r--r--sonar-core/src/test/java/org/sonar/core/issue/db/IssueChangeDaoTest.java8
-rw-r--r--sonar-core/src/test/java/org/sonar/core/issue/tracking/TrackerTest.java76
-rw-r--r--sonar-core/src/test/java/org/sonar/core/issue/workflow/IssueWorkflowTest.java5
-rw-r--r--sonar-core/src/test/java/org/sonar/core/issue/workflow/SetClosedTest.java20
-rw-r--r--sonar-core/src/test/java/org/sonar/core/rule/RuleKeyFunctionsTest.java46
-rw-r--r--sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/selectChangelogOfNonClosedIssuesByComponent.xml (renamed from sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/selectChangelogOfUnresolvedIssuesByComponent.xml)52
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/Rule.java12
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultRule.java9
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/NewRule.java15
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/issue/condition/HasIssuePropertyCondition.java2
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java26
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasuresFilters.java2
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleKey.java2
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/server/debt/DebtRemediationFunction.java18
-rw-r--r--sonar-plugin-api/src/test/java/org/sonar/api/batch/rule/internal/RulesBuilderTest.java5
-rw-r--r--sonar-plugin-api/src/test/java/org/sonar/api/issue/action/ActionTest.java168
-rw-r--r--sonar-plugin-api/src/test/java/org/sonar/api/issue/condition/HasIssuePropertyConditionTest.java113
-rw-r--r--sonar-plugin-api/src/test/java/org/sonar/api/issue/condition/HasResolutionTest.java68
-rw-r--r--sonar-plugin-api/src/test/java/org/sonar/api/issue/condition/HasStatusTest.java78
-rw-r--r--sonar-plugin-api/src/test/java/org/sonar/api/issue/condition/IsUnResolvedTest.java62
-rw-r--r--sonar-plugin-api/src/test/java/org/sonar/api/issue/condition/NotConditionTest.java72
-rw-r--r--sonar-plugin-api/src/test/java/org/sonar/api/issue/internal/DefaultIssueTest.java238
-rw-r--r--sonar-plugin-api/src/test/java/org/sonar/api/issue/internal/FieldDiffsTest.java168
96 files changed, 2624 insertions, 2354 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainerImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainerImpl.java
index 7bbd85c8748..210d9d9842e 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainerImpl.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainerImpl.java
@@ -45,6 +45,7 @@ import org.sonar.server.computation.debt.DebtModelHolderImpl;
import org.sonar.server.computation.event.EventRepositoryImpl;
import org.sonar.server.computation.formula.CoreFormulaRepositoryImpl;
import org.sonar.server.computation.issue.BaseIssuesLoader;
+import org.sonar.server.computation.issue.DebtCalculator;
import org.sonar.server.computation.issue.IssueCounter;
import org.sonar.server.computation.issue.DebtAggregator;
import org.sonar.server.computation.issue.DefaultAssignee;
@@ -54,8 +55,8 @@ 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.RuleCache;
import org.sonar.server.computation.issue.RuleCacheLoader;
+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;
@@ -168,16 +169,17 @@ public class ComputeEngineContainerImpl extends ComponentContainer implements Co
NewCoverageMetricKeysModule.class,
// issues
+ RuleCacheLoader.class,
+ RuleRepositoryImpl.class,
ScmAccountToUserLoader.class,
ScmAccountToUser.class,
- RuleCache.class,
- RuleCacheLoader.class,
IssueCache.class,
DefaultAssignee.class,
IssueVisitors.class,
IssueLifecycle.class,
- // order is important, NewDebtAggregator is based on DebtAggregator (new debt requires debt)
+ // order is important: DebtAggregator then NewDebtAggregator (new debt requires debt)
+ DebtCalculator.class,
DebtAggregator.class,
NewDebtCalculator.class,
NewDebtAggregator.class,
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 f10dbfe5af9..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,6 +20,8 @@
package org.sonar.server.computation.debt;
+import java.util.List;
+
public interface DebtModelHolder {
/**
@@ -30,4 +32,5 @@ public interface DebtModelHolder {
*/
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 206ed5f3a8b..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,7 +20,9 @@
package org.sonar.server.computation.debt;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import static com.google.common.base.Preconditions.checkArgument;
@@ -29,23 +31,26 @@ import static java.util.Objects.requireNonNull;
public class DebtModelHolderImpl implements MutableDebtModelHolder {
+ 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");
+ rootCharacteristics.add(rootCharacteristic);
characteristicById.put(rootCharacteristic.getId(), rootCharacteristic);
for (Characteristic characteristic : subCharacteristics) {
characteristicById.put(characteristic.getId(), characteristic);
}
+ return this;
}
@Override
public Characteristic getCharacteristicById(int id) {
- checkCharacteristicsAreInitialized();
+ checkInitialized();
Characteristic characteristic = characteristicById.get(id);
if (characteristic == null) {
throw new IllegalStateException("Debt characteristic with id [" + id + "] does not exist");
@@ -53,7 +58,13 @@ public class DebtModelHolderImpl implements MutableDebtModelHolder {
return characteristic;
}
- private void checkCharacteristicsAreInitialized() {
+ @Override
+ public List<Characteristic> getRootCharacteristics() {
+ checkInitialized();
+ return rootCharacteristics;
+ }
+
+ 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
index 216fe6db9b9..8ebfb64479e 100644
--- 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
@@ -19,29 +19,26 @@
*/
package org.sonar.server.computation.issue;
-import com.google.common.base.Function;
import com.google.common.collect.ImmutableMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import javax.annotation.Nonnull;
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.api.utils.log.Loggers;
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.core.rule.RuleDto;
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.
@@ -52,14 +49,14 @@ public class BaseIssuesLoader {
private final Set<RuleKey> activeRuleKeys;
private final TreeRootHolder treeRootHolder;
private final DbClient dbClient;
- private final RuleCache ruleCache;
+ private final RuleRepository ruleRepository;
public BaseIssuesLoader(BatchReportReader reportReader, TreeRootHolder treeRootHolder,
- DbClient dbClient, RuleCache ruleCache) {
- this.activeRuleKeys = from(reportReader.readMetadata().getActiveRuleKeyList()).transform(ToRuleKey.INSTANCE).toSet();
+ DbClient dbClient, RuleRepository ruleRepository) {
+ this.activeRuleKeys = from(reportReader.readMetadata().getActiveRuleKeyList()).transform(stringToRuleKey()).toSet();
this.treeRootHolder = treeRootHolder;
this.dbClient = dbClient;
- this.ruleCache = ruleCache;
+ this.ruleRepository = ruleRepository;
}
public List<DefaultIssue> loadForComponentUuid(String componentUuid) {
@@ -73,15 +70,13 @@ public class BaseIssuesLoader {
DefaultIssue issue = ((IssueDto) resultContext.getResultObject()).toDefaultIssue();
// TODO this field should be set outside this class
- RuleDto rule = ruleCache.getNullable(issue.ruleKey());
- if (rule == null || rule.getStatus() == RuleStatus.REMOVED || !isActive(issue.ruleKey())) {
+ 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());
- Loggers.get(getClass()).info("Loaded from db: " + issue);
result.add(issue);
}
});
@@ -98,7 +93,7 @@ public class BaseIssuesLoader {
/**
* Uuids of all the components that have open issues on this project.
*/
- public Set<String> loadComponentUuids() {
+ public Set<String> loadUuidsOfComponentsWithOpenIssues() {
DbSession session = dbClient.openSession(false);
try {
return dbClient.issueDao().selectComponentUuidsOfOpenIssuesForProjectUuid(session, treeRootHolder.getRoot().getUuid());
@@ -106,13 +101,4 @@ public class BaseIssuesLoader {
MyBatis.closeQuietly(session);
}
}
-
- private enum ToRuleKey implements Function<String, RuleKey> {
- INSTANCE;
- @Nonnull
- @Override
- public RuleKey apply(@Nonnull String input) {
- return RuleKey.parse(input);
- }
- }
}
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
index 8bce13283e3..c8c410a6503 100644
--- 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
@@ -27,7 +27,6 @@ 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.core.rule.RuleDto;
import org.sonar.server.computation.component.Component;
import org.sonar.server.computation.debt.Characteristic;
import org.sonar.server.computation.debt.DebtModelHolder;
@@ -40,7 +39,7 @@ import static com.google.common.collect.Maps.newHashMap;
public class DebtAggregator extends IssueVisitor {
- private final RuleCache ruleCache;
+ private final RuleRepository ruleRepository;
private final DebtModelHolder debtModelHolder;
private final MetricRepository metricRepository;
private final MeasureRepository measureRepository;
@@ -48,9 +47,9 @@ public class DebtAggregator extends IssueVisitor {
private final Map<Integer, Debt> debtsByComponentRef = new HashMap<>();
private Debt currentDebt;
- public DebtAggregator(RuleCache ruleCache, DebtModelHolder debtModelHolder,
+ public DebtAggregator(RuleRepository ruleRepository, DebtModelHolder debtModelHolder,
MetricRepository metricRepository, MeasureRepository measureRepository) {
- this.ruleCache = ruleCache;
+ this.ruleRepository = ruleRepository;
this.debtModelHolder = debtModelHolder;
this.metricRepository = metricRepository;
this.measureRepository = measureRepository;
@@ -58,7 +57,8 @@ public class DebtAggregator extends IssueVisitor {
@Override
public void beforeComponent(Component component, Tracking tracking) {
- this.currentDebt = new Debt();
+ currentDebt = new Debt();
+ debtsByComponentRef.put(component.getRef(), currentDebt);
// aggregate children counters
for (Component child : component.getChildren()) {
@@ -79,26 +79,35 @@ public class DebtAggregator extends IssueVisitor {
@Override
public void afterComponent(Component component) {
- if (this.currentDebt.minutes > 0L) {
- Metric metric = metricRepository.getByKey(CoreMetrics.TECHNICAL_DEBT_KEY);
+ Metric metric = metricRepository.getByKey(CoreMetrics.TECHNICAL_DEBT_KEY);
- // total value
- measureRepository.add(component, metric, Measure.newMeasureBuilder().create(this.currentDebt.minutes));
+ // 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();
- measureRepository.add(component, metric, Measure.newMeasureBuilder().forRule(ruleId).create(ruleDebt));
- }
+ // 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));
+ }
- // distribution by characteristic
- for (Map.Entry<Integer, Long> entry : currentDebt.minutesByCharacteristicId.entrySet()) {
- int characteristicId = entry.getKey();
- long characteristicDebt = entry.getValue();
- 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;
}
@@ -112,7 +121,7 @@ public class DebtAggregator extends IssueVisitor {
if (issueMinutes != null && issueMinutes != 0L) {
this.minutes += issueMinutes;
- RuleDto rule = ruleCache.get(issue.ruleKey());
+ Rule rule = ruleRepository.getByKey(issue.ruleKey());
this.minutesByRuleId.add(rule.getId(), issueMinutes);
Integer subCharacteristicId = rule.getSubCharacteristicId();
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
index 466575e7b2a..7cdd0a5da73 100644
--- 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
@@ -65,7 +65,7 @@ public class DefaultAssignee {
private boolean isValidLogin(String s) {
UserDoc user = userIndex.getNullableByLogin(s);
if (user == null) {
- LOG.info("the {} property was set with an unknown login: {}", CoreProperties.DEFAULT_ISSUE_ASSIGNEE, s);
+ 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/IssueCache.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueCache.java
index af1dc395b59..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
@@ -29,7 +29,6 @@ import org.sonar.server.util.cache.DiskCache;
/**
* 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/IssueCounter.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueCounter.java
index 2e49500d8d2..8c27aefac35 100644
--- 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
@@ -75,7 +75,7 @@ import static org.sonar.api.rule.Severity.MINOR;
*/
public class IssueCounter extends IssueVisitor {
- private final static Map<String, String> SEVERITY_TO_METRIC_KEY = ImmutableMap.of(
+ private static final Map<String, String> SEVERITY_TO_METRIC_KEY = ImmutableMap.of(
BLOCKER, BLOCKER_VIOLATIONS_KEY,
CRITICAL, CRITICAL_VIOLATIONS_KEY,
MAJOR, MAJOR_VIOLATIONS_KEY,
@@ -83,7 +83,7 @@ public class IssueCounter extends IssueVisitor {
INFO, INFO_VIOLATIONS_KEY
);
- private final static Map<String, String> SEVERITY_TO_NEW_METRIC_KEY = ImmutableMap.of(
+ 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,
@@ -99,7 +99,7 @@ public class IssueCounter extends IssueVisitor {
private Counters currentCounters;
public IssueCounter(PeriodsHolder periodsHolder,
- MetricRepository metricRepository, MeasureRepository measureRepository) {
+ MetricRepository metricRepository, MeasureRepository measureRepository) {
this.periodsHolder = periodsHolder;
this.metricRepository = metricRepository;
this.measureRepository = measureRepository;
@@ -173,20 +173,14 @@ public class IssueCounter extends IssueVisitor {
String severity = entry.getKey();
String metricKey = entry.getValue();
Double[] variations = new Double[PeriodsHolder.MAX_NUMBER_OF_PERIODS];
- boolean set = false;
for (Period period : periodsHolder.getPeriods()) {
Multiset<String> bag = currentCounters.counterForPeriod(period.getIndex()).severityBag;
- if (bag.contains(severity)) {
- variations[period.getIndex() - 1] = new Double(bag.count(severity));
- set = true;
- }
- }
- if (set) {
- Metric metric = metricRepository.getByKey(metricKey);
- measureRepository.add(component, metric, Measure.newMeasureBuilder()
- .setVariations(new MeasureVariations(variations))
- .createNoValue());
+ 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());
}
}
}
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
index 10bd8af2edf..31856ec5b6f 100644
--- 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
@@ -20,6 +20,7 @@
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;
@@ -28,15 +29,25 @@ 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) {
+ 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()));
}
@@ -45,6 +56,7 @@ public class IssueLifecycle {
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) {
@@ -59,6 +71,8 @@ public class IssueLifecycle {
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());
@@ -66,7 +80,7 @@ public class IssueLifecycle {
updater.setPastSeverity(raw, base.severity(), changeContext);
}
- // TODO attributes + changelog
+ // TODO attributes
// fields coming from raw
updater.setPastLine(raw, base.getLine());
@@ -76,6 +90,10 @@ public class IssueLifecycle {
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
index 7385ff05326..6b3768dc26b 100644
--- 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
@@ -34,8 +34,9 @@ public abstract class IssueVisitor {
}
/**
- * This method is called 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.
+ * 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) {
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
index dd212257ab9..d26b324ff14 100644
--- 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
@@ -64,27 +64,27 @@ public class NewDebtAggregator extends IssueVisitor {
public void beforeComponent(Component component, Tracking tracking) {
currentSum = new DebtSum();
sumsByComponentRef.put(component.getRef(), currentSum);
- List<IssueChangeDto> changes = dbClient.issueChangeDao().selectChangelogOfUnresolvedIssuesByComponent(component.getUuid());
+ 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.debtInMinutes() != null && !periodsHolder.getPeriods().isEmpty()) {
+ 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);
}
}
- for (Component child : component.getChildren()) {
- DebtSum childSum = sumsByComponentRef.remove(child.getRef());
- if (childSum != null) {
- currentSum.add(childSum);
- }
- }
}
@Override
@@ -99,7 +99,7 @@ public class NewDebtAggregator extends IssueVisitor {
}
private static class DebtSum {
- private Double[] sums = new Double[PeriodsHolder.MAX_NUMBER_OF_PERIODS];
+ private final Double[] sums = new Double[PeriodsHolder.MAX_NUMBER_OF_PERIODS];
private boolean isEmpty = true;
void add(int periodIndex, long newDebt) {
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
index 6d7c85941c3..ed7502e7b22 100644
--- 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
@@ -22,8 +22,10 @@ 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;
@@ -46,16 +48,22 @@ import static com.google.common.collect.FluentIterable.from;
*/
public class NewDebtCalculator {
- public long calculate(DefaultIssue issue, List<IssueChangeDto> debtChangelog, Period period) {
+ 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, List<IssueChangeDto> debtChangelog, long periodDate) {
+ private long calculateFromChangelog(DefaultIssue issue, Collection<IssueChangeDto> debtChangelog, long periodDate) {
List<FieldDiffs> debtDiffs = from(debtChangelog).transform(ToFieldDiffs.INSTANCE).filter(HasDebtChange.INSTANCE).toSortedList(CHANGE_ORDERING);
- long newDebt = issue.debtInMinutes().longValue();
+ 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();
@@ -80,7 +88,7 @@ public class NewDebtCalculator {
@CheckForNull
private static long subtract(long newDebt, @Nullable Long with) {
if (with != null) {
- return Math.min(0L, newDebt - with);
+ return Math.max(0L, newDebt - with);
}
return newDebt;
}
@@ -108,13 +116,7 @@ public class NewDebtCalculator {
INSTANCE;
@Override
public FieldDiffs apply(@Nonnull IssueChangeDto dto) {
- FieldDiffs diffs = FieldDiffs.parse(dto.getChangeData());
- diffs.setIssueKey(dto.getIssueKey());
- Long date = dto.getIssueChangeCreationDate();
- if (date != null) {
- diffs.setCreationDate(new Date(date));
- }
- return diffs;
+ return dto.toFieldDiffs();
}
}
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/sonar-core/src/main/java/org/sonar/core/computation/package-info.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/RuleRepository.java
index 218db23b3fb..3b86d2c24e5 100644
--- a/sonar-core/src/main/java/org/sonar/core/computation/package-info.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/RuleRepository.java
@@ -17,8 +17,12 @@
* 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;
-@ParametersAreNonnullByDefault
-package org.sonar.core.computation;
+import org.sonar.api.rule.RuleKey;
-import javax.annotation.ParametersAreNonnullByDefault;
+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
index 75530ff601d..c44f4710b9c 100644
--- 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
@@ -19,28 +19,25 @@
*/
package org.sonar.server.computation.issue;
-import java.util.Set;
import org.sonar.core.issue.DefaultIssue;
-import org.sonar.core.rule.RuleDto;
import org.sonar.server.computation.component.Component;
import static com.google.common.collect.Sets.union;
public class RuleTagsCopier extends IssueVisitor {
- private final RuleCache ruleCache;
+ private final RuleRepository ruleRepository;
- public RuleTagsCopier(RuleCache ruleCache) {
- this.ruleCache = ruleCache;
+ 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
- RuleDto rule = ruleCache.get(issue.ruleKey());
- Set<String> ruleTags = union(rule.getTags(), rule.getSystemTags());
- issue.setTags(union(issue.tags(), ruleTags));
+ 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/ScmAccountToUserLoader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/ScmAccountToUserLoader.java
index c1035975b09..6eb42c29ba8 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/ScmAccountToUserLoader.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 ScmAccountToUserLoader implements CacheLoader<String, String> {
- private final Logger log;
+ private static final Logger log = Loggers.get(ScmAccountToUserLoader.class);
private final UserIndex index;
public ScmAccountToUserLoader(UserIndex index) {
- this(index, Loggers.get(ScmAccountToUserLoader.class));
- }
-
- @VisibleForTesting
- ScmAccountToUserLoader(UserIndex index, Logger log) {
- this.log = log;
this.index = index;
}
@@ -60,12 +54,7 @@ public class ScmAccountToUserLoader 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 ScmAccountToUserLoader 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/TrackerRawInputFactory.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/TrackerRawInputFactory.java
index e61e9a578ba..6f7cc4640cf 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/TrackerRawInputFactory.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/TrackerRawInputFactory.java
@@ -26,8 +26,7 @@ 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.Duration;
-import org.sonar.api.utils.log.Loggers;
+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;
@@ -41,12 +40,10 @@ public class TrackerRawInputFactory {
private final TreeRootHolder treeRootHolder;
private final BatchReportReader reportReader;
- private final RuleCache ruleCache;
- public TrackerRawInputFactory(TreeRootHolder treeRootHolder, BatchReportReader reportReader, RuleCache ruleCache) {
+ public TrackerRawInputFactory(TreeRootHolder treeRootHolder, BatchReportReader reportReader) {
this.treeRootHolder = treeRootHolder;
this.reportReader = reportReader;
- this.ruleCache = ruleCache;
}
public Input<DefaultIssue> create(Component component) {
@@ -80,10 +77,7 @@ public class TrackerRawInputFactory {
LineHashSequence lineHashSeq = getLineHashSequence();
for (BatchReport.Issue reportIssue : reportIssues) {
DefaultIssue issue = toIssue(lineHashSeq, reportIssue);
- if (isValid(issue, lineHashSeq)) {
- Loggers.get(getClass()).info("Loaded from report: " + issue);
- issues.add(issue);
- }
+ issues.add(issue);
}
}
return issues;
@@ -114,25 +108,11 @@ public class TrackerRawInputFactory {
if (reportIssue.hasEffortToFix()) {
issue.setEffortToFix(reportIssue.getEffortToFix());
}
- if (reportIssue.hasDebtInMinutes()) {
- issue.setDebt(Duration.create(reportIssue.getDebtInMinutes()));
- }
issue.setTags(Sets.newHashSet(reportIssue.getTagList()));
- // TODO issue attributes
+ if (reportIssue.hasAttributes()) {
+ issue.setAttributes(KeyValueFormat.parse(reportIssue.getAttributes()));
+ }
return issue;
}
}
-
- private boolean isValid(DefaultIssue issue, LineHashSequence lineHashSeq) {
- // TODO log debug when invalid ? Or throw exception ?
-
- if (ruleCache.getNullable(issue.ruleKey()) == null) {
- return false;
- }
- if (issue.getLine() != null && !lineHashSeq.hasLine(issue.getLine())) {
- // FIXME
- //return false;
- }
- return true;
- }
}
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 f671559674f..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
@@ -51,7 +51,7 @@ public class PeriodsHolderImpl implements PeriodsHolder {
public void setPeriods(Iterable<Period> periods) {
requireNonNull(periods, "Periods cannot be null");
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");
+ checkState(this.periods == null, "Periods have already been initialized");
Period[] newPeriods = new Period[MAX_NUMBER_OF_PERIODS];
for (Period period : from(periods).filter(CheckNotNull.INSTANCE)) {
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 bc5d9633880..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
@@ -52,10 +52,10 @@ public class ComputationSteps {
// data computation
IntegrateIssuesStep.class,
- ComputeIssueMeasuresStep.class,
CustomMeasuresCopyStep.class,
ComputeFormulaMeasuresStep.class,
- CustomMeasuresCopyStep.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/IntegrateIssuesStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/IntegrateIssuesStep.java
index f9c08506ed5..c1c234d32e2 100644
--- 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
@@ -61,7 +61,7 @@ public class IntegrateIssuesStep implements ComputationStep {
@Override
public void execute() {
// all the components that had issues before this analysis
- final Set<String> unprocessedComponentUuids = Sets.newHashSet(baseIssuesLoader.loadComponentUuids());
+ final Set<String> unprocessedComponentUuids = Sets.newHashSet(baseIssuesLoader.loadUuidsOfComponentsWithOpenIssues());
new DepthTraversalTypeAwareVisitor(Component.Type.FILE, POST_ORDER) {
@Override
@@ -76,8 +76,6 @@ public class IntegrateIssuesStep implements ComputationStep {
private void processIssues(Component component) {
Tracking<DefaultIssue, DefaultIssue> tracking = tracker.track(component);
- Loggers.get(getClass()).info("----- tracking ------");
- Loggers.get(getClass()).info("" + tracking);
DiskCache<DefaultIssue>.DiskAppender cacheAppender = issueCache.newAppender();
try {
issueVisitors.beforeComponent(component, tracking);
@@ -92,44 +90,35 @@ public class IntegrateIssuesStep implements ComputationStep {
private void fillNewOpenIssues(Component component, Tracking<DefaultIssue, DefaultIssue> tracking, DiskCache<DefaultIssue>.DiskAppender cacheAppender) {
Set<DefaultIssue> issues = tracking.getUnmatchedRaws();
- Loggers.get(getClass()).info("----- fillNewOpenIssues on " + component.getKey());
for (DefaultIssue issue : issues) {
issueLifecycle.initNewOpenIssue(issue);
- Loggers.get(getClass()).info("new " + issue);
process(component, issue, cacheAppender);
}
- Loggers.get(getClass()).info("----- /fillNewOpenIssues on " + component.getKey());
}
private void fillExistingOpenIssues(Component component, Tracking<DefaultIssue, DefaultIssue> tracking, DiskCache<DefaultIssue>.DiskAppender cacheAppender) {
- Loggers.get(getClass()).info("----- fillExistingOpenIssues on " + component.getKey());
for (Map.Entry<DefaultIssue, DefaultIssue> entry : tracking.getMatchedRaws().entrySet()) {
DefaultIssue raw = entry.getKey();
DefaultIssue base = entry.getValue();
issueLifecycle.mergeExistingOpenIssue(raw, base);
- Loggers.get(getClass()).info("merged " + raw);
process(component, raw, cacheAppender);
}
for (Map.Entry<Integer, DefaultIssue> entry : tracking.getOpenManualIssuesByLine().entries()) {
- int line = entry.getKey();
+ Integer line = entry.getKey();
DefaultIssue manualIssue = entry.getValue();
- manualIssue.setLine(line == 0 ? null : line);
- Loggers.get(getClass()).info("kept manual " + manualIssue);
+ issueLifecycle.moveOpenManualIssue(manualIssue, line);
process(component, manualIssue, cacheAppender);
}
- Loggers.get(getClass()).info("----- /fillExistingOpenIssues on " + component.getKey());
}
private void closeUnmatchedBaseIssues(Component component, Tracking<DefaultIssue, DefaultIssue> tracking, DiskCache<DefaultIssue>.DiskAppender cacheAppender) {
- Loggers.get(getClass()).info("----- closeUnmatchedBaseIssues on " + component.getKey());
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);
- Loggers.get(getClass()).info("closing " + issue);
// TODO manual issues -> was updater.setResolution(newIssue, Issue.RESOLUTION_REMOVED, changeContext);. Is it a problem ?
process(component, issue, cacheAppender);
}
- Loggers.get(getClass()).info("----- /closeUnmatchedBaseIssues on " + component.getKey());
}
private void process(Component component, DefaultIssue issue, DiskCache<DefaultIssue>.DiskAppender cacheAppender) {
@@ -145,10 +134,9 @@ public class IntegrateIssuesStep implements ComputationStep {
List<DefaultIssue> issues = baseIssuesLoader.loadForComponentUuid(deletedComponentUuid);
for (DefaultIssue issue : issues) {
issue.setBeingClosed(true);
- // FIXME should be renamed "setToRemovedStatus"
- issue.setOnDisabledRule(true);
+ // TODO should be renamed
+ issue.setOnDisabledRule(false);
issueLifecycle.doAutomaticTransition(issue);
- // TODO execute visitors ? Component is currently missing.
cacheAppender.append(issue);
}
}
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 a5ac16a7477..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
@@ -21,7 +21,6 @@ package org.sonar.server.computation.step;
import org.sonar.api.issue.IssueComment;
import org.sonar.api.utils.System2;
-import org.sonar.api.utils.log.Loggers;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.core.issue.DefaultIssueComment;
import org.sonar.core.issue.FieldDiffs;
@@ -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,25 +65,19 @@ public class PersistIssuesStep implements ComputationStep {
DefaultIssue issue = issues.next();
boolean saved = false;
if (issue.isNew()) {
- Integer ruleId = ruleCache.get(issue.ruleKey()).getId();
+ Integer ruleId = ruleRepository.getByKey(issue.ruleKey()).getId();
IssueDto dto = IssueDto.toDtoForComputationInsert(issue, ruleId, system2.now());
- Loggers.get(getClass()).info("---- INSERT " + dto);
mapper.insert(dto);
saved = true;
} else if (issue.isChanged()) {
IssueDto dto = IssueDto.toDtoForUpdate(issue, system2.now());
- Loggers.get(getClass()).info("---- UPDATE " + dto);
int updateCount = mapper.updateIfBeforeSelectedDate(dto);
if (updateCount == 0) {
- Loggers.get(getClass()).info("---- CONFLICT: " + updateCount);
// 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;
- } else {
- Loggers.get(getClass()).info("---- UNCHANGED " + issue);
}
if (saved) {
insertChanges(changeMapper, issue);
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 2c8aa110430..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
@@ -28,7 +28,7 @@ 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/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/test/java/org/sonar/server/computation/debt/DebtModelHolderImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/debt/DebtModelHolderImplTest.java
index a9172d9842e..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,129 +17,86 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-///*
-// * 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.debt;
-//
-//import java.util.Arrays;
-//import java.util.Collections;
-//import org.junit.Rule;
-//import org.junit.Test;
-//import org.junit.rules.ExpectedException;
-//
-//import static java.util.Collections.singletonList;
-//
-//public class DebtModelHolderImplTest {
-//
-// @Rule
-// public ExpectedException thrown = ExpectedException.none();
-//
-// 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", null);
-// private static final Characteristic READABILITY = new Characteristic(5, "READABILITY", null);
-//
-// DebtModelHolderImpl sut = new DebtModelHolderImpl();
-//
-// @Test
-// public void add_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);
-// }
-//
-// @Test
-// public void add_characteristics_fail_with_NPE_if_root_characteristic_is_null() throws Exception {
-// thrown.expect(NullPointerException.class);
-// thrown.expectMessage("rootCharacteristic cannot be null");
-//
-// sut.addCharacteristics(null, singletonList(COMPILER_RELATED_PORTABILITY));
-// }
-//
-// @Test
-// public void add_characteristics_fail_with_NPE_if_sub_characteristics_are_null() throws Exception {
-// thrown.expect(NullPointerException.class);
-// thrown.expectMessage("subCharacteristics cannot be null");
-//
-// sut.addCharacteristics(PORTABILITY, null);
-// }
-//
-// @Test
-// public void add_characteristics_fail_with_IAE_if_sub_characteristics_are_empty() throws Exception {
-// thrown.expect(IllegalArgumentException.class);
-// thrown.expectMessage("subCharacteristics cannot be empty");
-//
-// sut.addCharacteristics(PORTABILITY, Collections.<Characteristic>emptyList());
-// }
-//
-// @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);
-// }
-//
-// @Test
-// public void get_root_characteristics_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();
-// }
-//
-// @Test
-// public void get_sub_characteristics_by_root_key_throws_a_ISE_when_not_initialized() throws Exception {
-// thrown.expect(IllegalStateException.class);
-// thrown.expectMessage("Characteristics have not been initialized yet");
-//
-// sut.findSubCharacteristicsByRootKey("PORTABILITY");
-// }
-//}
+package org.sonar.server.computation.debt;
+
+import java.util.Arrays;
+import java.util.Collections;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static java.util.Collections.singletonList;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class DebtModelHolderImplTest {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ 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", null);
+ private static final Characteristic READABILITY = new Characteristic(5, "READABILITY", null);
+
+ DebtModelHolderImpl sut = new DebtModelHolderImpl();
+
+ @Test
+ 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.getRootCharacteristics()).hasSize(2);
+ assertThat(sut.getCharacteristicById(PORTABILITY.getId()).getKey()).isEqualTo("PORTABILITY");
+ assertThat(sut.getCharacteristicById(COMPILER_RELATED_PORTABILITY.getId()).getKey()).isEqualTo("COMPILER_RELATED_PORTABILITY");
+ }
+
+ @Test
+ public void add_characteristics_fail_with_NPE_if_root_characteristic_is_null() throws Exception {
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("rootCharacteristic cannot be null");
+
+ sut.addCharacteristics(null, singletonList(COMPILER_RELATED_PORTABILITY));
+ }
+
+ @Test
+ public void add_characteristics_fail_with_NPE_if_sub_characteristics_are_null() throws Exception {
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("subCharacteristics cannot be null");
+
+ sut.addCharacteristics(PORTABILITY, null);
+ }
+
+ @Test
+ public void add_characteristics_fail_with_IAE_if_sub_characteristics_are_empty() throws Exception {
+ thrown.expect(IllegalArgumentException.class);
+ thrown.expectMessage("subCharacteristics cannot be empty");
+
+ sut.addCharacteristics(PORTABILITY, Collections.<Characteristic>emptyList());
+ }
+
+ @Test
+ public void get_root_characteristics() throws Exception {
+ sut.addCharacteristics(PORTABILITY, Arrays.asList(COMPILER_RELATED_PORTABILITY, READABILITY));
+ sut.addCharacteristics(MAINTAINABILITY, singletonList(READABILITY));
+
+ assertThat(sut.getRootCharacteristics()).hasSize(2);
+ }
+
+ @Test
+ public void getCharacteristicById_throws_ISE_when_not_initialized() throws Exception {
+ thrown.expect(IllegalStateException.class);
+ thrown.expectMessage("Characteristics have not been initialized yet");
+
+ sut.getCharacteristicById(1);
+ }
+
+ @Test
+ public void getRootCharacteristics_throws_ISE_when_not_initialized() throws Exception {
+ thrown.expect(IllegalStateException.class);
+ thrown.expectMessage("Characteristics have not been initialized yet");
+
+ 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/IssueAssignerTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueAssignerTest.java
new file mode 100644
index 00000000000..d5f8ab5a2e8
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueAssignerTest.java
@@ -0,0 +1,124 @@
+/*
+ * 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 org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+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 static org.mockito.Mockito.mock;
+
+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();
+
+ IssueAssigner underTest;
+
+ @Before
+ public void setUp() throws Exception {
+ esTester.truncateIndices();
+ underTest = new IssueAssigner(new SourceLineIndex(esTester.client()), reportReader, scmAccountToUser, defaultAssignee);
+ }
+
+ @Test
+ public void line_author_from_report() {
+ reportReader.putChangesets(BatchReport.Changesets.newBuilder()
+ .setComponentRef(123_456_789)
+ .addChangeset(newChangeset("charb", "123-456-789", 123_456_789L))
+ .addChangeset(newChangeset("wolinski", "987-654-321", 987_654_321L))
+ .addChangesetIndexByLine(0)
+ .addChangesetIndexByLine(0)
+ .addChangesetIndexByLine(1)
+ .build());
+
+ 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
+// 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()
+ .setAuthor(author)
+ .setRevision(revision)
+ .setDate(date);
+ }
+
+ private SourceLineDoc newSourceLine(String author, String revision, long date, int lineNumber) {
+ return new SourceLineDoc()
+ .setScmAuthor(author)
+ .setScmRevision(revision)
+ .setScmDate(new Date(date))
+ .setLine(lineNumber)
+ .setProjectUuid("PROJECT_UUID")
+ .setFileUuid("DEFAULT_UUID");
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/CountIssuesListenerTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueCounterTest.java
index 5a7fa451ba4..67988ec1f5a 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/CountIssuesListenerTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueCounterTest.java
@@ -71,7 +71,7 @@ import static org.sonar.api.rule.Severity.MAJOR;
import static org.sonar.server.computation.component.DumbComponent.builder;
import static org.sonar.server.computation.metric.Metric.MetricType.INT;
-public class CountIssuesListenerTest {
+public class IssueCounterTest {
static final Component FILE1 = builder(Component.Type.FILE, 1).build();
static final Component FILE2 = builder(Component.Type.FILE, 2).build();
@@ -213,13 +213,13 @@ public class CountIssuesListenerTest {
assertVariation(FILE1, NEW_ISSUES_METRIC, period.getIndex(), 1);
assertVariation(FILE1, NEW_CRITICAL_ISSUES_METRIC, period.getIndex(), 1);
- assertThat(measureRepository.getRawMeasure(FILE1, NEW_BLOCKER_ISSUES_METRIC).isPresent()).isFalse();
- assertThat(measureRepository.getRawMeasure(FILE1, NEW_MAJOR_ISSUES_METRIC).isPresent()).isFalse();
+ assertVariation(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);
- assertThat(measureRepository.getRawMeasure(PROJECT, NEW_BLOCKER_ISSUES_METRIC).isPresent()).isFalse();
- assertThat(measureRepository.getRawMeasure(PROJECT, NEW_MAJOR_ISSUES_METRIC).isPresent()).isFalse();
+ 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) {
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
index 17c4dc053d3..cf864b8a8e0 100644
--- 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
@@ -17,351 +17,98 @@
* 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 org.apache.commons.lang.ObjectUtils;
-//import org.apache.commons.lang.time.DateUtils;
-//import org.junit.Before;
-//import org.junit.Test;
-//import org.mockito.ArgumentMatcher;
-//import org.mockito.Mock;
-//import org.sonar.api.CoreProperties;
-//import org.sonar.api.batch.DecoratorContext;
-//import org.sonar.api.component.ResourcePerspectives;
-//import org.sonar.api.config.Settings;
-//import org.sonar.api.issue.Issuable;
-//import org.sonar.api.issue.Issue;
-//import org.sonar.api.measures.CoreMetrics;
-//import org.sonar.api.measures.Measure;
-//import org.sonar.api.measures.Metric;
-//import org.sonar.api.resources.Resource;
-//import org.sonar.api.utils.Duration;
-//import org.sonar.core.issue.DefaultIssue;
-//import org.sonar.core.issue.FieldDiffs;
-//
-//import static com.google.common.collect.Lists.newArrayList;
-//import static org.assertj.core.api.Assertions.assertThat;
-//import static org.mockito.Matchers.argThat;
-//import static org.mockito.Mockito.mock;
-//import static org.mockito.Mockito.never;
-//import static org.mockito.Mockito.verify;
-//import static org.mockito.Mockito.when;
-//
-//public class NewDebtAggregatorTest {
-// NewDebtDecorator decorator;
-//
-// @Mock
-// TimeMachineConfiguration timeMachineConfiguration;
-//
-// @Mock
-// Resource resource;
-//
-// @Mock
-// Issuable issuable;
-//
-// @Mock
-// DecoratorContext context;
-//
-// Date rightNow;
-// Date elevenDaysAgo;
-// Date tenDaysAgo;
-// Date nineDaysAgo;
-// Date fiveDaysAgo;
-// Date fourDaysAgo;
-//
-// static final int HOURS_IN_DAY = 8;
-//
-// static final Long ONE_DAY_IN_MINUTES = 1L * HOURS_IN_DAY * 60;
-// static final Long TWO_DAYS_IN_MINUTES = 2L * HOURS_IN_DAY * 60;
-// static final Long FIVE_DAYS_IN_MINUTES = 5L * HOURS_IN_DAY * 60;
-//
-// @Before
-// public void setup() {
-// Settings settings = new Settings();
-// settings.setProperty(CoreProperties.HOURS_IN_DAY, HOURS_IN_DAY);
-//
-// ResourcePerspectives perspectives = mock(ResourcePerspectives.class);
-// when(perspectives.as(Issuable.class, resource)).thenReturn(issuable);
-//
-// rightNow = new Date();
-// elevenDaysAgo = DateUtils.addDays(rightNow, -11);
-// tenDaysAgo = DateUtils.addDays(rightNow, -10);
-// nineDaysAgo = DateUtils.addDays(rightNow, -9);
-// fiveDaysAgo = DateUtils.addDays(rightNow, -5);
-// fourDaysAgo = DateUtils.addDays(rightNow, -4);
-//
-// when(timeMachineConfiguration.periods()).thenReturn(newArrayList(new Period(1, fiveDaysAgo), new Period(2, tenDaysAgo)));
-//
-// decorator = new NewDebtDecorator(perspectives, timeMachineConfiguration, new IssueChangelogDebtCalculator());
-// }
-//
-// @Test
-// public void generates_metrics() {
-// assertThat(decorator.generatesMetrics()).hasSize(1);
-// }
-//
-// @Test
-// public void execute_on_project() {
-// assertThat(decorator.shouldExecuteOnProject(null)).isTrue();
-// }
-//
-// @Test
-// public void save_on_one_issue_with_one_new_changelog() {
-// Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES)).setChanges(
-// newArrayList(
-// // changelog created at is null because it has just been created on the current analysis
-// new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(null)
-// )
-// );
-// when(issuable.issues()).thenReturn(newArrayList(issue));
-//
-// decorator.decorate(resource, context);
-//
-// // remember : period1 is 5daysAgo, period2 is 10daysAgo
-// verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 1.0 * ONE_DAY_IN_MINUTES, 1.0 * ONE_DAY_IN_MINUTES)));
-// }
-//
-// @Test
-// public void save_on_one_issue_with_changelog() {
-// Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges(
-// newArrayList(
-// new FieldDiffs().setDiff("technicalDebt", TWO_DAYS_IN_MINUTES, FIVE_DAYS_IN_MINUTES).setCreationDate(null),
-// new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(fourDaysAgo)
-// )
-// );
-// when(issuable.issues()).thenReturn(newArrayList(issue));
-//
-// decorator.decorate(resource, context);
-//
-// // remember : period1 is 5daysAgo, period2 is 10daysAgo
-// verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 4.0 * ONE_DAY_IN_MINUTES, 4.0 * ONE_DAY_IN_MINUTES)));
-// }
-//
-// @Test
-// public void save_on_one_issue_with_changelog_only_in_the_past() {
-// Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(ONE_DAY_IN_MINUTES)).setChanges(
-// newArrayList(
-// // Change before all periods
-// new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(elevenDaysAgo)
-// )
-// );
-// when(issuable.issues()).thenReturn(newArrayList(issue));
-//
-// decorator.decorate(resource, context);
-//
-// // remember : period1 is 5daysAgo, period2 is 10daysAgo
-// verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 0.0)));
-// }
-//
-// @Test
-// public void save_on_one_issue_with_changelog_having_null_value() {
-// Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges(
-// newArrayList(
-// new FieldDiffs().setDiff("technicalDebt", null, FIVE_DAYS_IN_MINUTES).setCreationDate(null),
-// new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, null).setCreationDate(fourDaysAgo),
-// new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo)
-// )
-// );
-// when(issuable.issues()).thenReturn(newArrayList(issue));
-//
-// decorator.decorate(resource, context);
-//
-// // remember : period1 is 5daysAgo, period2 is 10daysAgo
-// verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 4.0 * ONE_DAY_IN_MINUTES, 5.0 * ONE_DAY_IN_MINUTES)));
-// }
-//
-// @Test
-// public void save_on_one_issue_with_changelog_and_periods_have_no_dates() {
-// when(timeMachineConfiguration.periods()).thenReturn(newArrayList(new Period(1, null), new Period(2, null)));
-//
-// Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges(
-// newArrayList(
-// new FieldDiffs().setDiff("technicalDebt", null, FIVE_DAYS_IN_MINUTES).setCreationDate(null),
-// new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, null).setCreationDate(fourDaysAgo),
-// new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo)
-// )
-// );
-// when(issuable.issues()).thenReturn(newArrayList(issue));
-//
-// decorator.decorate(resource, context);
-//
-// // remember : period1 is 5daysAgo, period2 is 10daysAgo
-// verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 5.0 * ONE_DAY_IN_MINUTES, 5.0 * ONE_DAY_IN_MINUTES)));
-// }
-//
-// @Test
-// public void save_on_one_issue_with_changelog_having_not_only_technical_debt_changes() {
-// Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges(
-// newArrayList(
-// new FieldDiffs()
-// .setDiff("actionPlan", "1.0", "1.1").setCreationDate(fourDaysAgo)
-// .setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(fourDaysAgo)
-// )
-// );
-// when(issuable.issues()).thenReturn(newArrayList(issue));
-//
-// decorator.decorate(resource, context);
-//
-// // remember : period1 is 5daysAgo, period2 is 10daysAgo
-// verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 4.0 * ONE_DAY_IN_MINUTES, 4.0 * ONE_DAY_IN_MINUTES)));
-// }
-//
-// @Test
-// public void save_on_issues_with_changelog() {
-// Issue issue1 = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges(
-// newArrayList(
-// new FieldDiffs().setDiff("technicalDebt", TWO_DAYS_IN_MINUTES, FIVE_DAYS_IN_MINUTES).setCreationDate(rightNow),
-// new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(fourDaysAgo),
-// new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo)
-// )
-// );
-// Issue issue2 = new DefaultIssue().setKey("B").setCreationDate(tenDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES)).setChanges(
-// newArrayList(
-// new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(rightNow),
-// new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo)
-// )
-// );
-// when(issuable.issues()).thenReturn(newArrayList(issue1, issue2));
-//
-// decorator.decorate(resource, context);
-//
-// // remember : period1 is 5daysAgo, period2 is 10daysAgo
-// verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 5.0 * ONE_DAY_IN_MINUTES, 7.0 * ONE_DAY_IN_MINUTES)));
-// }
-//
-// @Test
-// public void save_on_one_issue_without_changelog() {
-// when(issuable.issues()).thenReturn(newArrayList(
-// (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)))
-// );
-//
-// decorator.decorate(resource, context);
-//
-// // remember : period1 is 5daysAgo, period2 is 10daysAgo
-// verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 5.0 * ONE_DAY_IN_MINUTES)));
-// }
-//
-// @Test
-// public void save_on_one_issue_without_technical_debt_and_without_changelog() {
-// when(issuable.issues()).thenReturn(newArrayList(
-// (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(null))
-// );
-//
-// decorator.decorate(resource, context);
-//
-// // remember : period1 is 5daysAgo, period2 is 10daysAgo
-// verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 0.0)));
-// }
-//
-// @Test
-// public void save_on_one_issue_without_changelog_and_periods_have_no_dates() {
-// when(timeMachineConfiguration.periods()).thenReturn(newArrayList(new Period(1, null), new Period(2, null)));
-//
-// when(issuable.issues()).thenReturn(newArrayList(
-// (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)))
-// );
-//
-// decorator.decorate(resource, context);
-//
-// // remember : period1 is null, period2 is null
-// verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 5.0 * ONE_DAY_IN_MINUTES, 5.0 * ONE_DAY_IN_MINUTES)));
-// }
-//
-// @Test
-// public void save_on_issues_without_changelog() {
-// when(issuable.issues()).thenReturn(newArrayList(
-// (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)),
-// new DefaultIssue().setKey("B").setCreationDate(fiveDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES))
-// ));
-//
-// decorator.decorate(resource, context);
-//
-// // remember : period1 is 5daysAgo, period2 is 10daysAgo
-// verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 7.0 * ONE_DAY_IN_MINUTES)));
-// }
-//
-// @Test
-// public void save_on_issues_with_changelog_and_issues_without_changelog() {
-// // issue1 and issue2 have changelog
-// Issue issue1 = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges(
-// newArrayList(
-// new FieldDiffs().setDiff("technicalDebt", TWO_DAYS_IN_MINUTES, FIVE_DAYS_IN_MINUTES).setCreationDate(rightNow),
-// new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(fourDaysAgo),
-// new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo)
-// )
-// );
-// Issue issue2 = new DefaultIssue().setKey("B").setCreationDate(tenDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES)).setChanges(
-// newArrayList(
-// new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(rightNow),
-// new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo)
-// )
-// );
-//
-// // issue3 and issue4 have no changelog
-// Issue issue3 = new DefaultIssue().setKey("C").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES));
-// Issue issue4 = new DefaultIssue().setKey("D").setCreationDate(fiveDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES));
-// when(issuable.issues()).thenReturn(newArrayList(issue1, issue2, issue3, issue4));
-//
-// decorator.decorate(resource, context);
-//
-// // remember : period1 is 5daysAgo, period2 is 10daysAgo
-// verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 5.0 * ONE_DAY_IN_MINUTES, 14.0 * ONE_DAY_IN_MINUTES)));
-// }
-//
-// @Test
-// public void not_save_if_measure_already_computed() {
-// when(context.getMeasure(CoreMetrics.NEW_TECHNICAL_DEBT)).thenReturn(new Measure());
-// when(issuable.issues()).thenReturn(newArrayList(
-// (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)),
-// new DefaultIssue().setKey("B").setCreationDate(fiveDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES))
-// ));
-//
-// decorator.decorate(resource, context);
-//
-// verify(context, never()).saveMeasure(argThat(new IsMeasure(CoreMetrics.NEW_TECHNICAL_DEBT)));
-// }
-//
-// /**
-// * SONAR-5059
-// */
-// @Test
-// public void not_return_negative_debt() {
-// Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(ONE_DAY_IN_MINUTES)).setChanges(
-// newArrayList(
-// // changelog created at is null because it has just been created on the current analysis
-// new FieldDiffs().setDiff("technicalDebt", TWO_DAYS_IN_MINUTES, ONE_DAY_IN_MINUTES).setCreationDate(null)
-// )
-// );
-// when(issuable.issues()).thenReturn(newArrayList(issue));
-//
-// decorator.decorate(resource, context);
-//
-// // remember : period1 is 5daysAgo, period2 is 10daysAgo
-// verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 0.0)));
-// }
-//
-//
-// class IsVariationMeasure extends ArgumentMatcher<Measure> {
-// Metric metric = null;
-// Double var1 = null;
-// Double var2 = null;
-//
-// public IsVariationMeasure(Metric metric, Double var1, Double var2) {
-// this.metric = metric;
-// this.var1 = var1;
-// this.var2 = var2;
-// }
-//
-// public boolean matches(Object o) {
-// if (!(o instanceof Measure)) {
-// return false;
-// }
-// Measure m = (Measure) o;
-// return ObjectUtils.equals(metric, m.getMetric()) &&
-// ObjectUtils.equals(var1, m.getVariation1()) &&
-// ObjectUtils.equals(var2, m.getVariation2());
-// }
-// }
-//
-//}
+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
index bfbcb7a87ce..37add915d09 100644
--- 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
@@ -17,130 +17,109 @@
* 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 org.apache.commons.lang.time.DateUtils;
-//import org.junit.Before;
-//import org.junit.Test;
-//import org.sonar.api.issue.Issue;
-//import org.sonar.api.utils.Duration;
-//import org.sonar.core.issue.DefaultIssue;
-//import org.sonar.core.issue.FieldDiffs;
-//
-//import static com.google.common.collect.Lists.newArrayList;
-//import static org.assertj.core.api.Assertions.assertThat;
-//
-//public class NewDebtCalculatorTest {
-// private static final int HOURS_IN_DAY = 8;
-//
-// IssueChangelogDebtCalculator issueChangelogDebtCalculator;
-//
-// Date rightNow = new Date();
-// Date elevenDaysAgo = DateUtils.addDays(rightNow, -11);
-// Date tenDaysAgo = DateUtils.addDays(rightNow, -10);
-// Date nineDaysAgo = DateUtils.addDays(rightNow, -9);
-// Date fiveDaysAgo = DateUtils.addDays(rightNow, -5);
-// Date fourDaysAgo = DateUtils.addDays(rightNow, -4);
-//
-// long oneDay = 1 * HOURS_IN_DAY * 60 * 60L;
-// long twoDays = 2 * HOURS_IN_DAY * 60 * 60L;
-// long fiveDays = 5 * HOURS_IN_DAY * 60 * 60L;
-//
-// Duration oneDayDebt = Duration.create(oneDay);
-// Duration twoDaysDebt = Duration.create(twoDays);
-// Duration fiveDaysDebt = Duration.create(fiveDays);
-//
-// @Before
-// public void setUp() {
-// issueChangelogDebtCalculator = new IssueChangelogDebtCalculator();
-// }
-//
-// @Test
-// public void calculate_new_technical_debt_with_one_diff_in_changelog() {
-// Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(twoDaysDebt).setChanges(
-// newArrayList(
-// // changelog created at is null because it has just been created on the current analysis
-// new FieldDiffs().setDiff("technicalDebt", oneDay, twoDays).setCreationDate(null)
-// )
-// );
-//
-// assertThat(issueChangelogDebtCalculator.calculateNewTechnicalDebt(issue, rightNow)).isEqualTo(oneDay);
-// assertThat(issueChangelogDebtCalculator.calculateNewTechnicalDebt(issue, fiveDaysAgo)).isEqualTo(oneDay);
-//
-// assertThat(issueChangelogDebtCalculator.calculateNewTechnicalDebt(issue, elevenDaysAgo)).isEqualTo(twoDays);
-// }
-//
-// @Test
-// public void calculate_new_technical_debt_with_many_diffs_in_changelog() {
-// Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(fiveDaysDebt).setChanges(
-// newArrayList(
-// new FieldDiffs().setDiff("technicalDebt", twoDays, fiveDays).setCreationDate(null),
-// new FieldDiffs().setDiff("technicalDebt", oneDay, twoDays).setCreationDate(fourDaysAgo)
-// )
-// );
-//
-// assertThat(issueChangelogDebtCalculator.calculateNewTechnicalDebt(issue, rightNow)).isEqualTo(3 * oneDay);
-// assertThat(issueChangelogDebtCalculator.calculateNewTechnicalDebt(issue, fiveDaysAgo)).isEqualTo(4 * oneDay);
-// assertThat(issueChangelogDebtCalculator.calculateNewTechnicalDebt(issue, elevenDaysAgo)).isEqualTo(5 * oneDay);
-// }
-//
-// @Test
-// public void changelog_can_be_in_wrong_order() {
-// Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(fiveDaysDebt).setChanges(
-// newArrayList(
-// // 3rd
-// new FieldDiffs().setDiff("technicalDebt", null, oneDay).setCreationDate(nineDaysAgo),
-// // 1st
-// new FieldDiffs().setDiff("technicalDebt", twoDays, fiveDays).setCreationDate(rightNow),
-// // 2nd
-// new FieldDiffs().setDiff("technicalDebt", oneDay, twoDays).setCreationDate(fourDaysAgo)
-// )
-// );
-//
-// assertThat(issueChangelogDebtCalculator.calculateNewTechnicalDebt(issue, fiveDaysAgo)).isEqualTo(4 * oneDay);
-// assertThat(issueChangelogDebtCalculator.calculateNewTechnicalDebt(issue, elevenDaysAgo)).isEqualTo(5 * oneDay);
-// }
-//
-// @Test
-// public void calculate_new_technical_debt_with_null_date() {
-// Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(twoDaysDebt).setChanges(
-// newArrayList(
-// new FieldDiffs().setDiff("technicalDebt", oneDay, twoDays).setCreationDate(null)
-// )
-// );
-//
-// assertThat(issueChangelogDebtCalculator.calculateNewTechnicalDebt(issue, null)).isEqualTo(2 * oneDay);
-// }
-//
-// @Test
-// public void calculate_new_technical_debt_when_new_debt_is_null() {
-// Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(null).setChanges(
-// newArrayList(
-// new FieldDiffs().setDiff("technicalDebt", oneDay, null).setCreationDate(null),
-// new FieldDiffs().setDiff("technicalDebt", null, oneDay).setCreationDate(nineDaysAgo)
-// )
-// );
-//
-// assertThat(issueChangelogDebtCalculator.calculateNewTechnicalDebt(issue, rightNow)).isNull();
-// }
-//
-// @Test
-// public void calculate_new_technical_debt_on_issue_without_technical_debt_and_without_changelog() {
-// Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo);
-//
-// assertThat(issueChangelogDebtCalculator.calculateNewTechnicalDebt(issue, rightNow)).isNull();
-// }
-//
-// @Test
-// public void not_return_negative_debt() {
-// Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(oneDayDebt).setChanges(
-// newArrayList(
-// new FieldDiffs().setDiff("technicalDebt", twoDays, oneDay).setCreationDate(null)
-// )
-// );
-//
-// assertThat(issueChangelogDebtCalculator.calculateNewTechnicalDebt(issue, rightNow)).isNull();
-// }
-//
-//}
+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/ScmAccountToUserLoaderTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/ScmAccountToUserLoaderTest.java
index a25df3a68af..40c58734445 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/ScmAccountToUserLoaderTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/ScmAccountToUserLoaderTest.java
@@ -24,21 +24,23 @@ 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 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 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();
@@ -58,11 +60,10 @@ public class ScmAccountToUserLoaderTest {
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);
- ScmAccountToUserLoader loader = new ScmAccountToUserLoader(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
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/SourceAuthorsHolderTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/SourceAuthorsHolderTest.java
deleted file mode 100644
index ccfcddd96d9..00000000000
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/SourceAuthorsHolderTest.java
+++ /dev/null
@@ -1,137 +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.
- */
-///*
-// * 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 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.server.computation.batch.BatchReportReaderRule;
-//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;
-//
-//@Category(DbTests.class)
-//public class SourceAuthorsHolderTest {
-//
-// @ClassRule
-// public static EsTester esTester = new EsTester().addDefinitions(new SourceLineIndexDefinition(new Settings()));
-// @Rule
-// public BatchReportReaderRule reportReader = new BatchReportReaderRule();
-//
-// SourceAuthorsHolder sut;
-//
-// @Before
-// public void setUp() throws Exception {
-// esTester.truncateIndices();
-// sut = new SourceAuthorsHolder(new SourceLineIndex(esTester.client()), reportReader);
-// }
-//
-// @Test
-// public void line_author_from_report() {
-// reportReader.putChangesets(BatchReport.Changesets.newBuilder()
-// .setComponentRef(123_456_789)
-// .addChangeset(newChangeset("charb", "123-456-789", 123_456_789L))
-// .addChangeset(newChangeset("wolinski", "987-654-321", 987_654_321L))
-// .addChangesetIndexByLine(0)
-// .addChangesetIndexByLine(0)
-// .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");
-// }
-//
-// @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()
-// .setAuthor(author)
-// .setRevision(revision)
-// .setDate(date);
-// }
-//
-// private SourceLineDoc newSourceLine(String author, String revision, long date, int lineNumber) {
-// return new SourceLineDoc()
-// .setScmAuthor(author)
-// .setScmRevision(revision)
-// .setScmDate(new Date(date))
-// .setLine(lineNumber)
-// .setProjectUuid("PROJECT_UUID")
-// .setFileUuid("DEFAULT_UUID");
-// }
-//}
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/FeedDebtModelStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/FeedDebtModelStepTest.java
index edf10791eb2..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,90 +17,69 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-///*
-// * 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.Collection;
-//import org.junit.After;
-//import org.junit.Before;
-//import org.junit.ClassRule;
-//import org.junit.Test;
-//import org.junit.experimental.categories.Category;
-//import org.sonar.core.persistence.DbSession;
-//import org.sonar.core.persistence.DbTester;
-//import org.sonar.core.technicaldebt.db.CharacteristicDao;
-//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.db.DbClient;
-//import org.sonar.test.DbTests;
-//
-//import static org.assertj.core.api.Assertions.assertThat;
-//
-//@Category(DbTests.class)
-//public class FeedDebtModelStepTest extends BaseStepTest {
-//
-// @ClassRule
-// public static final DbTester dbTester = new DbTester();
-//
-// DbClient dbClient;
-//
-// DbSession dbSession;
-//
-// MutableDebtModelHolder debtModelHolder = new DebtModelHolderImpl();
-//
-// FeedDebtModelStep sut;
-//
-// @Before
-// public void setUp() throws Exception {
-// dbTester.truncateTables();
-// dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new CharacteristicDao(dbTester.myBatis()));
-// dbSession = dbClient.openSession(false);
-//
-// sut = new FeedDebtModelStep(dbClient, debtModelHolder);
-// }
-//
-// @After
-// public void tearDown() throws Exception {
-// dbSession.close();
-// }
-//
-// @Override
-// protected ComputationStep step() {
-// return sut;
-// }
-//
-// @Test
-// public void feed_characteristics() throws Exception {
-// dbTester.prepareDbUnit(getClass(), "shared.xml");
-//
-// sut.execute();
-//
-// Collection<Characteristic> rootChars = debtModelHolder.findRootCharacteristics();
-// 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");
-// }
-//}
+package org.sonar.server.computation.step;
+
+import java.util.Collection;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.persistence.DbTester;
+import org.sonar.core.technicaldebt.db.CharacteristicDao;
+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.db.DbClient;
+import org.sonar.test.DbTests;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@Category(DbTests.class)
+public class FeedDebtModelStepTest extends BaseStepTest {
+
+ @ClassRule
+ public static final DbTester dbTester = new DbTester();
+
+ DbClient dbClient;
+
+ DbSession dbSession;
+
+ MutableDebtModelHolder debtModelHolder = new DebtModelHolderImpl();
+
+ FeedDebtModelStep sut;
+
+ @Before
+ public void setUp() throws Exception {
+ dbTester.truncateTables();
+ dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new CharacteristicDao(dbTester.myBatis()));
+ dbSession = dbClient.openSession(false);
+
+ sut = new FeedDebtModelStep(dbClient, debtModelHolder);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ dbSession.close();
+ }
+
+ @Override
+ protected ComputationStep step() {
+ return sut;
+ }
+
+ @Test
+ public void feed_characteristics() throws Exception {
+ dbTester.prepareDbUnit(getClass(), "shared.xml");
+
+ sut.execute();
+
+ Collection<Characteristic> rootChars = debtModelHolder.getRootCharacteristics();
+ assertThat(rootChars).extracting("id").containsOnly(1);
+ assertThat(rootChars).extracting("key").containsOnly("PORTABILITY");
+
+ Characteristic subChar = debtModelHolder.getCharacteristicById(1);
+ assertThat(subChar).isNotNull();
+ }
+}
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 ff4b22ba991..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
@@ -30,15 +30,17 @@ import org.sonar.api.issue.Issue;
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;
@@ -56,6 +58,9 @@ public class PersistIssuesStepTest extends BaseStepTest {
@ClassRule
public static DbTester dbTester = new DbTester();
+ @Rule
+ public BatchReportReaderRule reportReader = new BatchReportReaderRule();
+
DbSession session;
DbClient dbClient;
@@ -76,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(NOW);
- step = new PersistIssuesStep(dbClient, system2, new UpdateConflictResolver(), new RuleCache(new RuleCacheLoader(dbClient)), issueCache);
+ reportReader.setMetadata(BatchReport.Metadata.getDefaultInstance());
+
+ step = new PersistIssuesStep(dbClient, system2, new UpdateConflictResolver(), new RuleRepositoryImpl(new RuleCacheLoader(dbClient, reportReader)), issueCache);
}
@After
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 b99f981492a..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,42 +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 java.util.Arrays.asList;
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 {
@@ -73,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;
@@ -110,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);
@@ -140,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)
@@ -172,14 +156,14 @@ public class PersistMeasuresStepTest extends BaseStepTest {
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();
@@ -187,15 +171,9 @@ public class PersistMeasuresStepTest extends BaseStepTest {
dto = dtos.get(PROJECT_REF);
assertThat(dto.get("snapshotId")).isEqualTo(4L);
assertThat(dto.get("componentId")).isEqualTo(fileDto.getId());
-<<<<<<< HEAD
- 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("metricId")).isEqualTo(doubleMetric.getId().longValue());
- assertThat(dto.get("value")).isEqualTo(123.123d);
->>>>>>> SONAR-6588 integrate issues to Compute Engine
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 e79b559ed79..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.core.issue.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/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/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/sonar-batch-protocol/src/main/gen-java/org/sonar/batch/protocol/output/BatchReport.java b/sonar-batch-protocol/src/main/gen-java/org/sonar/batch/protocol/output/BatchReport.java
index 21d7b857ca9..93549a0163f 100644
--- a/sonar-batch-protocol/src/main/gen-java/org/sonar/batch/protocol/output/BatchReport.java
+++ b/sonar-batch-protocol/src/main/gen-java/org/sonar/batch/protocol/output/BatchReport.java
@@ -5764,6 +5764,10 @@ public final class BatchReport {
}
/**
* Protobuf type {@code Measures}
+ *
+ * <pre>
+ * TODO to be removed. It prevents streaming
+ * </pre>
*/
public static final class Measures extends
com.google.protobuf.GeneratedMessage implements
@@ -6040,6 +6044,10 @@ public final class BatchReport {
}
/**
* Protobuf type {@code Measures}
+ *
+ * <pre>
+ * TODO to be removed. It prevents streaming
+ * </pre>
*/
public static final class Builder extends
com.google.protobuf.GeneratedMessage.Builder<Builder> implements
@@ -6587,23 +6595,6 @@ public final class BatchReport {
*/
com.google.protobuf.ByteString
getAttributesBytes();
-
- /**
- * <code>optional int64 debt_in_minutes = 9;</code>
- *
- * <pre>
- * TODO should it be moved to compute engine?
- * </pre>
- */
- boolean hasDebtInMinutes();
- /**
- * <code>optional int64 debt_in_minutes = 9;</code>
- *
- * <pre>
- * TODO should it be moved to compute engine?
- * </pre>
- */
- long getDebtInMinutes();
}
/**
* Protobuf type {@code Issue}
@@ -6711,11 +6702,6 @@ public final class BatchReport {
attributes_ = bs;
break;
}
- case 72: {
- bitField0_ |= 0x00000080;
- debtInMinutes_ = input.readInt64();
- break;
- }
}
}
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
@@ -7001,29 +6987,6 @@ public final class BatchReport {
}
}
- public static final int DEBT_IN_MINUTES_FIELD_NUMBER = 9;
- private long debtInMinutes_;
- /**
- * <code>optional int64 debt_in_minutes = 9;</code>
- *
- * <pre>
- * TODO should it be moved to compute engine?
- * </pre>
- */
- public boolean hasDebtInMinutes() {
- return ((bitField0_ & 0x00000080) == 0x00000080);
- }
- /**
- * <code>optional int64 debt_in_minutes = 9;</code>
- *
- * <pre>
- * TODO should it be moved to compute engine?
- * </pre>
- */
- public long getDebtInMinutes() {
- return debtInMinutes_;
- }
-
private void initFields() {
ruleRepository_ = "";
ruleKey_ = "";
@@ -7033,7 +6996,6 @@ public final class BatchReport {
tag_ = com.google.protobuf.LazyStringArrayList.EMPTY;
effortToFix_ = 0D;
attributes_ = "";
- debtInMinutes_ = 0L;
}
private byte memoizedIsInitialized = -1;
public final boolean isInitialized() {
@@ -7072,9 +7034,6 @@ public final class BatchReport {
if (((bitField0_ & 0x00000040) == 0x00000040)) {
output.writeBytes(8, getAttributesBytes());
}
- if (((bitField0_ & 0x00000080) == 0x00000080)) {
- output.writeInt64(9, debtInMinutes_);
- }
getUnknownFields().writeTo(output);
}
@@ -7121,10 +7080,6 @@ public final class BatchReport {
size += com.google.protobuf.CodedOutputStream
.computeBytesSize(8, getAttributesBytes());
}
- if (((bitField0_ & 0x00000080) == 0x00000080)) {
- size += com.google.protobuf.CodedOutputStream
- .computeInt64Size(9, debtInMinutes_);
- }
size += getUnknownFields().getSerializedSize();
memoizedSerializedSize = size;
return size;
@@ -7258,8 +7213,6 @@ public final class BatchReport {
bitField0_ = (bitField0_ & ~0x00000040);
attributes_ = "";
bitField0_ = (bitField0_ & ~0x00000080);
- debtInMinutes_ = 0L;
- bitField0_ = (bitField0_ & ~0x00000100);
return this;
}
@@ -7321,10 +7274,6 @@ public final class BatchReport {
to_bitField0_ |= 0x00000040;
}
result.attributes_ = attributes_;
- if (((from_bitField0_ & 0x00000100) == 0x00000100)) {
- to_bitField0_ |= 0x00000080;
- }
- result.debtInMinutes_ = debtInMinutes_;
result.bitField0_ = to_bitField0_;
onBuilt();
return result;
@@ -7380,9 +7329,6 @@ public final class BatchReport {
attributes_ = other.attributes_;
onChanged();
}
- if (other.hasDebtInMinutes()) {
- setDebtInMinutes(other.getDebtInMinutes());
- }
this.mergeUnknownFields(other.getUnknownFields());
return this;
}
@@ -7906,54 +7852,6 @@ public final class BatchReport {
return this;
}
- private long debtInMinutes_ ;
- /**
- * <code>optional int64 debt_in_minutes = 9;</code>
- *
- * <pre>
- * TODO should it be moved to compute engine?
- * </pre>
- */
- public boolean hasDebtInMinutes() {
- return ((bitField0_ & 0x00000100) == 0x00000100);
- }
- /**
- * <code>optional int64 debt_in_minutes = 9;</code>
- *
- * <pre>
- * TODO should it be moved to compute engine?
- * </pre>
- */
- public long getDebtInMinutes() {
- return debtInMinutes_;
- }
- /**
- * <code>optional int64 debt_in_minutes = 9;</code>
- *
- * <pre>
- * TODO should it be moved to compute engine?
- * </pre>
- */
- public Builder setDebtInMinutes(long value) {
- bitField0_ |= 0x00000100;
- debtInMinutes_ = value;
- onChanged();
- return this;
- }
- /**
- * <code>optional int64 debt_in_minutes = 9;</code>
- *
- * <pre>
- * TODO should it be moved to compute engine?
- * </pre>
- */
- public Builder clearDebtInMinutes() {
- bitField0_ = (bitField0_ & ~0x00000100);
- debtInMinutes_ = 0L;
- onChanged();
- return this;
- }
-
// @@protoc_insertion_point(builder_scope:Issue)
}
@@ -8004,6 +7902,10 @@ public final class BatchReport {
}
/**
* Protobuf type {@code Issues}
+ *
+ * <pre>
+ * TODO to be removed. It prevents streaming
+ * </pre>
*/
public static final class Issues extends
com.google.protobuf.GeneratedMessage implements
@@ -8280,6 +8182,10 @@ public final class BatchReport {
}
/**
* Protobuf type {@code Issues}
+ *
+ * <pre>
+ * TODO to be removed. It prevents streaming
+ * </pre>
*/
public static final class Builder extends
com.google.protobuf.GeneratedMessage.Builder<Builder> implements
@@ -19463,42 +19369,41 @@ public final class BatchReport {
"alue_4\030\021 \001(\001\022\031\n\021variation_value_5\030\022 \001(\001\022",
"\021\n\tperson_id\030\024 \001(\005\"<\n\010Measures\022\025\n\rcompon" +
"ent_ref\030\001 \001(\005\022\031\n\007measure\030\002 \003(\0132\010.Measure" +
- "\"\273\001\n\005Issue\022\027\n\017rule_repository\030\001 \001(\t\022\020\n\010r" +
+ "\"\242\001\n\005Issue\022\027\n\017rule_repository\030\001 \001(\t\022\020\n\010r" +
"ule_key\030\002 \001(\t\022\014\n\004line\030\003 \001(\005\022\013\n\003msg\030\004 \001(\t" +
"\022\033\n\010severity\030\005 \001(\0162\t.Severity\022\013\n\003tag\030\006 \003" +
"(\t\022\025\n\reffort_to_fix\030\007 \001(\001\022\022\n\nattributes\030" +
- "\010 \001(\t\022\027\n\017debt_in_minutes\030\t \001(\003\"6\n\006Issues" +
- "\022\025\n\rcomponent_ref\030\001 \001(\005\022\025\n\005issue\030\002 \003(\0132\006" +
- ".Issue\"\254\001\n\nChangesets\022\025\n\rcomponent_ref\030\001" +
- " \001(\005\022(\n\tchangeset\030\002 \003(\0132\025.Changesets.Cha",
- "ngeset\022 \n\024changesetIndexByLine\030\003 \003(\005B\002\020\001" +
- "\032;\n\tChangeset\022\020\n\010revision\030\001 \001(\t\022\016\n\006autho" +
- "r\030\002 \001(\t\022\014\n\004date\030\003 \001(\003\"R\n\tDuplicate\022\026\n\016ot" +
- "her_file_ref\030\001 \001(\005\022\025\n\005range\030\002 \001(\0132\006.Rang" +
- "e\022\026\n\016other_file_key\030\003 \001(\t\"M\n\013Duplication" +
- "\022\037\n\017origin_position\030\001 \001(\0132\006.Range\022\035\n\tdup" +
- "licate\030\002 \003(\0132\n.Duplicate\"H\n\014Duplications" +
- "\022\025\n\rcomponent_ref\030\001 \001(\005\022!\n\013duplication\030\002" +
- " \003(\0132\014.Duplication\"W\n\005Range\022\022\n\nstart_lin" +
- "e\030\001 \001(\005\022\020\n\010end_line\030\002 \001(\005\022\024\n\014start_offse",
- "t\030\003 \001(\005\022\022\n\nend_offset\030\004 \001(\005\"~\n\007Symbols\022\020" +
- "\n\010file_ref\030\001 \001(\005\022\037\n\006symbol\030\002 \003(\0132\017.Symbo" +
- "ls.Symbol\032@\n\006Symbol\022\033\n\013declaration\030\001 \001(\013" +
- "2\006.Range\022\031\n\treference\030\002 \003(\0132\006.Range\"\260\001\n\010" +
- "Coverage\022\014\n\004line\030\001 \001(\005\022\022\n\nconditions\030\002 \001" +
- "(\005\022\017\n\007ut_hits\030\003 \001(\010\022\017\n\007it_hits\030\004 \001(\010\022\035\n\025" +
- "ut_covered_conditions\030\005 \001(\005\022\035\n\025it_covere" +
- "d_conditions\030\006 \001(\005\022\"\n\032overall_covered_co" +
- "nditions\030\007 \001(\005\"L\n\022SyntaxHighlighting\022\025\n\005" +
- "range\030\001 \001(\0132\006.Range\022\037\n\004type\030\002 \001(\0162\021.High",
- "lightingType\"j\n\004Test\022\014\n\004name\030\001 \001(\t\022\033\n\006st" +
- "atus\030\002 \001(\0162\013.TestStatus\022\026\n\016duration_in_m" +
- "s\030\003 \001(\003\022\022\n\nstacktrace\030\004 \001(\t\022\013\n\003msg\030\005 \001(\t" +
- "\"\221\001\n\016CoverageDetail\022\021\n\ttest_name\030\001 \001(\t\0221" +
- "\n\014covered_file\030\002 \003(\0132\033.CoverageDetail.Co" +
- "veredFile\0329\n\013CoveredFile\022\020\n\010file_ref\030\001 \001" +
- "(\005\022\030\n\014covered_line\030\002 \003(\005B\002\020\001B#\n\037org.sona" +
- "r.batch.protocol.outputH\001"
+ "\010 \001(\t\"6\n\006Issues\022\025\n\rcomponent_ref\030\001 \001(\005\022\025" +
+ "\n\005issue\030\002 \003(\0132\006.Issue\"\254\001\n\nChangesets\022\025\n\r" +
+ "component_ref\030\001 \001(\005\022(\n\tchangeset\030\002 \003(\0132\025" +
+ ".Changesets.Changeset\022 \n\024changesetIndexB",
+ "yLine\030\003 \003(\005B\002\020\001\032;\n\tChangeset\022\020\n\010revision" +
+ "\030\001 \001(\t\022\016\n\006author\030\002 \001(\t\022\014\n\004date\030\003 \001(\003\"R\n\t" +
+ "Duplicate\022\026\n\016other_file_ref\030\001 \001(\005\022\025\n\005ran" +
+ "ge\030\002 \001(\0132\006.Range\022\026\n\016other_file_key\030\003 \001(\t" +
+ "\"M\n\013Duplication\022\037\n\017origin_position\030\001 \001(\013" +
+ "2\006.Range\022\035\n\tduplicate\030\002 \003(\0132\n.Duplicate\"" +
+ "H\n\014Duplications\022\025\n\rcomponent_ref\030\001 \001(\005\022!" +
+ "\n\013duplication\030\002 \003(\0132\014.Duplication\"W\n\005Ran" +
+ "ge\022\022\n\nstart_line\030\001 \001(\005\022\020\n\010end_line\030\002 \001(\005" +
+ "\022\024\n\014start_offset\030\003 \001(\005\022\022\n\nend_offset\030\004 \001",
+ "(\005\"~\n\007Symbols\022\020\n\010file_ref\030\001 \001(\005\022\037\n\006symbo" +
+ "l\030\002 \003(\0132\017.Symbols.Symbol\032@\n\006Symbol\022\033\n\013de" +
+ "claration\030\001 \001(\0132\006.Range\022\031\n\treference\030\002 \003" +
+ "(\0132\006.Range\"\260\001\n\010Coverage\022\014\n\004line\030\001 \001(\005\022\022\n" +
+ "\nconditions\030\002 \001(\005\022\017\n\007ut_hits\030\003 \001(\010\022\017\n\007it" +
+ "_hits\030\004 \001(\010\022\035\n\025ut_covered_conditions\030\005 \001" +
+ "(\005\022\035\n\025it_covered_conditions\030\006 \001(\005\022\"\n\032ove" +
+ "rall_covered_conditions\030\007 \001(\005\"L\n\022SyntaxH" +
+ "ighlighting\022\025\n\005range\030\001 \001(\0132\006.Range\022\037\n\004ty" +
+ "pe\030\002 \001(\0162\021.HighlightingType\"j\n\004Test\022\014\n\004n",
+ "ame\030\001 \001(\t\022\033\n\006status\030\002 \001(\0162\013.TestStatus\022\026" +
+ "\n\016duration_in_ms\030\003 \001(\003\022\022\n\nstacktrace\030\004 \001" +
+ "(\t\022\013\n\003msg\030\005 \001(\t\"\221\001\n\016CoverageDetail\022\021\n\tte" +
+ "st_name\030\001 \001(\t\0221\n\014covered_file\030\002 \003(\0132\033.Co" +
+ "verageDetail.CoveredFile\0329\n\013CoveredFile\022" +
+ "\020\n\010file_ref\030\001 \001(\005\022\030\n\014covered_line\030\002 \003(\005B" +
+ "\002\020\001B#\n\037org.sonar.batch.protocol.outputH\001"
};
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
new com.google.protobuf.Descriptors.FileDescriptor. InternalDescriptorAssigner() {
@@ -19548,7 +19453,7 @@ public final class BatchReport {
internal_static_Issue_fieldAccessorTable = new
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
internal_static_Issue_descriptor,
- new java.lang.String[] { "RuleRepository", "RuleKey", "Line", "Msg", "Severity", "Tag", "EffortToFix", "Attributes", "DebtInMinutes", });
+ new java.lang.String[] { "RuleRepository", "RuleKey", "Line", "Msg", "Severity", "Tag", "EffortToFix", "Attributes", });
internal_static_Issues_descriptor =
getDescriptor().getMessageTypes().get(6);
internal_static_Issues_fieldAccessorTable = new
diff --git a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/BatchReportReader.java b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/BatchReportReader.java
index fe310bafadf..0c13820f339 100644
--- a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/BatchReportReader.java
+++ b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/output/BatchReportReader.java
@@ -24,7 +24,6 @@ import java.util.Collections;
import java.util.List;
import javax.annotation.CheckForNull;
import org.sonar.batch.protocol.ProtobufUtil;
-import org.sonar.batch.protocol.output.BatchReport.Issues;
public class BatchReportReader {
diff --git a/sonar-batch-protocol/src/main/protobuf/batch_report.proto b/sonar-batch-protocol/src/main/protobuf/batch_report.proto
index 361c0419fff..08c5340d366 100644
--- a/sonar-batch-protocol/src/main/protobuf/batch_report.proto
+++ b/sonar-batch-protocol/src/main/protobuf/batch_report.proto
@@ -99,6 +99,7 @@ message Measure {
optional int32 person_id = 20;
}
+/* TODO to be removed. It prevents streaming */
message Measures {
optional int32 component_ref = 1;
repeated Measure measure = 2;
@@ -113,11 +114,9 @@ message Issue {
repeated string tag = 6;
optional double effort_to_fix = 7;
optional string attributes = 8;
-
- // TODO should it be moved to compute engine?
- optional int64 debt_in_minutes = 9;
}
+/* TODO to be removed. It prevents streaming */
message Issues {
optional int32 component_ref = 1;
repeated Issue issue = 2;
diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java b/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java
index 2ff7190907b..73c7604860c 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/issue/ModuleIssues.java
@@ -19,10 +19,8 @@
*/
package org.sonar.batch.issue;
-import com.google.common.base.Objects;
import com.google.common.base.Strings;
import javax.annotation.Nullable;
-import org.sonar.api.batch.debt.DebtRemediationFunction;
import org.sonar.api.batch.rule.ActiveRule;
import org.sonar.api.batch.rule.ActiveRules;
import org.sonar.api.batch.rule.Rule;
@@ -31,7 +29,6 @@ import org.sonar.api.batch.rule.internal.DefaultActiveRule;
import org.sonar.api.resources.Project;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rules.Violation;
-import org.sonar.api.utils.Duration;
import org.sonar.api.utils.MessageException;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.core.issue.DefaultIssueBuilder;
@@ -118,31 +115,5 @@ public class ModuleIssues {
if (issue.severity() == null) {
issue.setSeverity(activeRule.severity());
}
- if (rule != null) {
- DebtRemediationFunction function = rule.debtRemediationFunction();
- if (function != null) {
- issue.setDebt(calculateDebt(function, issue.effortToFix(), rule.key()));
- }
- }
- }
-
- private Duration calculateDebt(DebtRemediationFunction function, @Nullable Double effortToFix, RuleKey ruleKey) {
- if (DebtRemediationFunction.Type.CONSTANT_ISSUE.equals(function.type()) && effortToFix != null) {
- throw new IllegalArgumentException("Rule '" + ruleKey + "' can not use 'Constant/issue' remediation function " +
- "because this rule does not have a fixed remediation cost.");
- }
- Duration result = Duration.create(0);
- Duration factor = function.coefficient();
- Duration offset = function.offset();
-
- if (factor != null) {
- int effortToFixValue = Objects.firstNonNull(effortToFix, 1).intValue();
- result = factor.multiply(effortToFixValue);
- }
- if (offset != null) {
- result = result.add(offset);
- }
- return result;
}
-
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/phases/PhaseExecutor.java b/sonar-batch/src/main/java/org/sonar/batch/phases/PhaseExecutor.java
index 3a616e62465..e41acc6760d 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/phases/PhaseExecutor.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/phases/PhaseExecutor.java
@@ -108,7 +108,7 @@ public final class PhaseExecutor {
postJobsExecutor.execute(sensorContext);
}
cleanMemory();
- eventBus.fireEvent(new ProjectAnalysisEvent(module, false));
+ eventBus.fireEvent(new ProjectAnalysisEvent(module, false));
}
private void publishReportJob() {
diff --git a/sonar-batch/src/main/java/org/sonar/batch/report/IssuesPublisher.java b/sonar-batch/src/main/java/org/sonar/batch/report/IssuesPublisher.java
index 9c56d2404af..b3e3692d937 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/report/IssuesPublisher.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/report/IssuesPublisher.java
@@ -79,11 +79,6 @@ public class IssuesPublisher implements ReportPublisherStep {
if (effortToFix != null) {
builder.setEffortToFix(effortToFix);
}
- Long debtInMinutes = issue.debtInMinutes();
- if (debtInMinutes != null) {
- builder.setDebtInMinutes(debtInMinutes);
- }
-
return builder.build();
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureValueCoder.java b/sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureValueCoder.java
index e0fb8bb54a9..b9c8f06511b 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureValueCoder.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureValueCoder.java
@@ -27,7 +27,6 @@ import org.sonar.api.batch.measure.MetricFinder;
import org.sonar.api.measures.Measure;
import org.sonar.api.measures.Metric;
import org.sonar.api.measures.PersistenceMode;
-import org.sonar.api.technicaldebt.batch.Requirement;
class MeasureValueCoder implements ValueCoder {
diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java
index 6f9961c681a..356b9f9e436 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/issue/ModuleIssuesTest.java
@@ -19,6 +19,8 @@
*/
package org.sonar.batch.issue;
+import java.util.Calendar;
+import java.util.Date;
import org.apache.commons.lang.time.DateUtils;
import org.junit.Before;
import org.junit.Test;
@@ -26,10 +28,8 @@ import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
-import org.sonar.api.batch.debt.DebtRemediationFunction;
import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
import org.sonar.api.batch.rule.internal.RulesBuilder;
-import org.sonar.core.issue.DefaultIssue;
import org.sonar.api.resources.File;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
@@ -37,11 +37,8 @@ import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.Severity;
import org.sonar.api.rules.RulePriority;
import org.sonar.api.rules.Violation;
-import org.sonar.api.utils.Duration;
import org.sonar.api.utils.MessageException;
-
-import java.util.Calendar;
-import java.util.Date;
+import org.sonar.core.issue.DefaultIssue;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
@@ -249,106 +246,6 @@ public class ModuleIssuesTest {
verifyZeroInteractions(cache);
}
- @Test
- public void set_debt_with_linear_function() {
- ruleBuilder.add(SQUID_RULE_KEY)
- .setName(SQUID_RULE_NAME)
- .setDebtRemediationFunction(DebtRemediationFunction.createLinear(Duration.create(10L)));
- activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate();
- initModuleIssues();
-
- Date analysisDate = new Date();
- when(project.getAnalysisDate()).thenReturn(analysisDate);
-
- DefaultIssue issue = new DefaultIssue()
- .setKey("ABCDE")
- .setRuleKey(SQUID_RULE_KEY)
- .setSeverity(Severity.CRITICAL)
- .setEffortToFix(2d);
-
- when(filters.accept(issue)).thenReturn(true);
- moduleIssues.initAndAddIssue(issue);
-
- ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class);
- verify(cache).put(argument.capture());
- assertThat(argument.getValue().debt()).isEqualTo(Duration.create(20L));
- }
-
- @Test
- public void set_debt_with_linear_with_offset_function() {
- ruleBuilder.add(SQUID_RULE_KEY)
- .setName(SQUID_RULE_NAME)
- .setDebtRemediationFunction(DebtRemediationFunction.createLinearWithOffset(Duration.create(10L), Duration.create(25L)));
- activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate();
- initModuleIssues();
-
- Date analysisDate = new Date();
- when(project.getAnalysisDate()).thenReturn(analysisDate);
-
- DefaultIssue issue = new DefaultIssue()
- .setKey("ABCDE")
- .setRuleKey(SQUID_RULE_KEY)
- .setSeverity(Severity.CRITICAL)
- .setEffortToFix(2d);
-
- when(filters.accept(issue)).thenReturn(true);
- moduleIssues.initAndAddIssue(issue);
-
- ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class);
- verify(cache).put(argument.capture());
- assertThat(argument.getValue().debt()).isEqualTo(Duration.create(45L));
- }
-
- @Test
- public void set_debt_with_constant_issue_function() {
- ruleBuilder.add(SQUID_RULE_KEY)
- .setName(SQUID_RULE_NAME)
- .setDebtRemediationFunction(DebtRemediationFunction.createConstantPerIssue(Duration.create(10L)));
- activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate();
- initModuleIssues();
-
- Date analysisDate = new Date();
- when(project.getAnalysisDate()).thenReturn(analysisDate);
-
- DefaultIssue issue = new DefaultIssue()
- .setKey("ABCDE")
- .setRuleKey(SQUID_RULE_KEY)
- .setSeverity(Severity.CRITICAL)
- .setEffortToFix(null);
-
- when(filters.accept(issue)).thenReturn(true);
- moduleIssues.initAndAddIssue(issue);
-
- ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class);
- verify(cache).put(argument.capture());
- assertThat(argument.getValue().debt()).isEqualTo(Duration.create(10L));
- }
-
- @Test
- public void fail_to_set_debt_with_constant_issue_function_when_effort_to_fix_is_set() {
- ruleBuilder.add(SQUID_RULE_KEY)
- .setName(SQUID_RULE_NAME)
- .setDebtRemediationFunction(DebtRemediationFunction.createConstantPerIssue(Duration.create(25L)));
- activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate();
- initModuleIssues();
-
- DefaultIssue issue = new DefaultIssue()
- .setKey("ABCDE")
- .setRuleKey(SQUID_RULE_KEY)
- .setSeverity(Severity.CRITICAL)
- .setEffortToFix(2d);
-
- when(filters.accept(issue)).thenReturn(true);
-
- try {
- moduleIssues.initAndAddIssue(issue);
- fail();
- } catch (Exception e) {
- assertThat(e).isInstanceOf(IllegalArgumentException.class)
- .hasMessage("Rule 'squid:AvoidCycle' can not use 'Constant/issue' remediation function because this rule does not have a fixed remediation cost.");
- }
- }
-
/**
* Every rules and active rules has to be added in builders before creating ModuleIssues
*/
diff --git a/sonar-batch/src/test/java/org/sonar/batch/report/IssuesPublisherTest.java b/sonar-batch/src/test/java/org/sonar/batch/report/IssuesPublisherTest.java
index bdcce1bd74f..a27ce7d2447 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/report/IssuesPublisherTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/report/IssuesPublisherTest.java
@@ -31,7 +31,6 @@ import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.database.model.Snapshot;
import org.sonar.api.resources.Project;
import org.sonar.api.rule.RuleKey;
-import org.sonar.api.utils.Duration;
import org.sonar.batch.index.BatchComponentCache;
import org.sonar.batch.issue.IssueCache;
import org.sonar.batch.protocol.output.BatchReportReader;
@@ -81,7 +80,6 @@ public class IssuesPublisherTest {
issue2.setLine(2);
issue2.setMessage("msg");
issue2.setEffortToFix(2d);
- issue2.setDebt(Duration.create(2));
issue2.setResolution("FIXED");
issue2.setStatus("RESOLVED");
issue2.setChecksum("checksum");
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeDao.java b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeDao.java
index 3e7c21e8d23..23018566dc4 100644
--- a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeDao.java
+++ b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeDao.java
@@ -65,11 +65,11 @@ public class IssueChangeDao implements DaoComponent {
}
}
- public List<IssueChangeDto> selectChangelogOfUnresolvedIssuesByComponent(String componentUuid) {
+ public List<IssueChangeDto> selectChangelogOfNonClosedIssuesByComponent(String componentUuid) {
DbSession session = mybatis.openSession(false);
try {
IssueChangeMapper mapper = session.getMapper(IssueChangeMapper.class);
- return mapper.selectChangelogOfUnresolvedIssuesByComponent(componentUuid, IssueChangeDto.TYPE_FIELD_CHANGE);
+ return mapper.selectChangelogOfNonClosedIssuesByComponent(componentUuid, IssueChangeDto.TYPE_FIELD_CHANGE);
} finally {
MyBatis.closeQuietly(session);
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeMapper.java b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeMapper.java
index 732ddc5c75b..61d6b8e01ab 100644
--- a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeMapper.java
+++ b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueChangeMapper.java
@@ -41,5 +41,5 @@ public interface IssueChangeMapper {
List<IssueChangeDto> selectByIssuesAndType(@Param("issueKeys") List<String> issueKeys,
@Param("changeType") String changeType);
- List<IssueChangeDto> selectChangelogOfUnresolvedIssuesByComponent(@Param("componentUuid") String componentUuid, @Param("changeType") String changeType);
+ List<IssueChangeDto> selectChangelogOfNonClosedIssuesByComponent(@Param("componentUuid") String componentUuid, @Param("changeType") String changeType);
}
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/UpdateConflictResolver.java b/sonar-core/src/main/java/org/sonar/core/issue/db/UpdateConflictResolver.java
index 7c6b48a1603..ccc84139154 100644
--- a/sonar-core/src/main/java/org/sonar/core/issue/db/UpdateConflictResolver.java
+++ b/sonar-core/src/main/java/org/sonar/core/issue/db/UpdateConflictResolver.java
@@ -35,7 +35,7 @@ public class UpdateConflictResolver {
private static final Logger LOG = Loggers.get(UpdateConflictResolver.class);
public void resolve(DefaultIssue issue, IssueMapper mapper) {
- LOG.debug("Resolve conflict on issue " + issue.key());
+ LOG.debug("Resolve conflict on issue {}", issue.key());
IssueDto dbIssue = mapper.selectByKey(issue.key());
if (dbIssue != null) {
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/tracking/BlockHashSequence.java b/sonar-core/src/main/java/org/sonar/core/issue/tracking/BlockHashSequence.java
index 7a4d38671ca..9a9a179378f 100644
--- a/sonar-core/src/main/java/org/sonar/core/issue/tracking/BlockHashSequence.java
+++ b/sonar-core/src/main/java/org/sonar/core/issue/tracking/BlockHashSequence.java
@@ -20,6 +20,7 @@
package org.sonar.core.issue.tracking;
import java.util.List;
+import javax.annotation.Nullable;
public class BlockHashSequence {
@@ -57,6 +58,10 @@ public class BlockHashSequence {
return blockHashes[line - 1];
}
+ public boolean hasLine(@Nullable Integer line) {
+ return (line != null) && (line > 0) && (line <= blockHashes.length);
+ }
+
private static class BlockHashFactory {
private static final int PRIME_BASE = 31;
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/tracking/BlockRecognizer.java b/sonar-core/src/main/java/org/sonar/core/issue/tracking/BlockRecognizer.java
index d4361819fbb..55138a34995 100644
--- a/sonar-core/src/main/java/org/sonar/core/issue/tracking/BlockRecognizer.java
+++ b/sonar-core/src/main/java/org/sonar/core/issue/tracking/BlockRecognizer.java
@@ -36,22 +36,22 @@ class BlockRecognizer<RAW extends Trackable, BASE extends Trackable> {
* Only the issues associated to a line can be matched here.
*/
void match(Input<RAW> rawInput, Input<BASE> baseInput, Tracking<RAW, BASE> tracking) {
- BlockHashSequence baseHashSequence = baseInput.getBlockHashSequence();
BlockHashSequence rawHashSequence = rawInput.getBlockHashSequence();
+ BlockHashSequence baseHashSequence = baseInput.getBlockHashSequence();
- Multimap<Integer, RAW> rawsByLine = groupByLine(tracking.getUnmatchedRaws());
- Multimap<Integer, BASE> basesByLine = groupByLine(tracking.getUnmatchedBases());
- Map<Integer, HashOccurrence> map = new HashMap<>();
+ Multimap<Integer, RAW> rawsByLine = groupByLine(tracking.getUnmatchedRaws(), rawHashSequence);
+ Multimap<Integer, BASE> basesByLine = groupByLine(tracking.getUnmatchedBases(), baseHashSequence);
+ Map<Integer, HashOccurrence> occurrencesByHash = new HashMap<>();
for (Integer line : basesByLine.keySet()) {
int hash = baseHashSequence.getBlockHashForLine(line);
- HashOccurrence hashOccurrence = map.get(hash);
+ HashOccurrence hashOccurrence = occurrencesByHash.get(hash);
if (hashOccurrence == null) {
// first occurrence in base
hashOccurrence = new HashOccurrence();
hashOccurrence.baseLine = line;
hashOccurrence.baseCount = 1;
- map.put(hash, hashOccurrence);
+ occurrencesByHash.put(hash, hashOccurrence);
} else {
hashOccurrence.baseCount++;
}
@@ -59,14 +59,14 @@ class BlockRecognizer<RAW extends Trackable, BASE extends Trackable> {
for (Integer line : rawsByLine.keySet()) {
int hash = rawHashSequence.getBlockHashForLine(line);
- HashOccurrence hashOccurrence = map.get(hash);
+ HashOccurrence hashOccurrence = occurrencesByHash.get(hash);
if (hashOccurrence != null) {
hashOccurrence.rawLine = line;
hashOccurrence.rawCount++;
}
}
- for (HashOccurrence hashOccurrence : map.values()) {
+ for (HashOccurrence hashOccurrence : occurrencesByHash.values()) {
if (hashOccurrence.baseCount == 1 && hashOccurrence.rawCount == 1) {
// Guaranteed that baseLine has been moved to rawLine, so we can map all issues on baseLine to all issues on rawLine
map(rawsByLine.get(hashOccurrence.rawLine), basesByLine.get(hashOccurrence.baseLine), tracking);
@@ -131,11 +131,11 @@ class BlockRecognizer<RAW extends Trackable, BASE extends Trackable> {
}
}
- private static <T extends Trackable> Multimap<Integer, T> groupByLine(Collection<T> trackables) {
+ private static <T extends Trackable> Multimap<Integer, T> groupByLine(Collection<T> trackables, BlockHashSequence hashSequence) {
Multimap<Integer, T> result = LinkedHashMultimap.create();
for (T trackable : trackables) {
Integer line = trackable.getLine();
- if (line != null) {
+ if (hashSequence.hasLine(line)) {
result.put(line, trackable);
}
}
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/tracking/Tracker.java b/sonar-core/src/main/java/org/sonar/core/issue/tracking/Tracker.java
index 5da2ca5da04..e7cd196bbca 100644
--- a/sonar-core/src/main/java/org/sonar/core/issue/tracking/Tracker.java
+++ b/sonar-core/src/main/java/org/sonar/core/issue/tracking/Tracker.java
@@ -25,11 +25,11 @@ import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashSet;
import java.util.Objects;
import javax.annotation.Nonnull;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.rule.RuleKey;
-import org.sonar.api.utils.log.Loggers;
import static com.google.common.collect.FluentIterable.from;
@@ -38,6 +38,8 @@ public class Tracker<RAW extends Trackable, BASE extends Trackable> {
public Tracking<RAW, BASE> track(Input<RAW> rawInput, Input<BASE> baseInput) {
Tracking<RAW, BASE> tracking = new Tracking<>(rawInput, baseInput);
+ relocateManualIssues(rawInput, baseInput, tracking);
+
// 1. match issues with same rule, same line and same line hash, but not necessarily with same message
match(tracking, LineAndLineHashKeyFactory.INSTANCE);
@@ -54,9 +56,6 @@ public class Tracker<RAW extends Trackable, BASE extends Trackable> {
// See SONAR-2812
match(tracking, LineHashKeyFactory.INSTANCE);
- // TODO what about issues on line 0 ?
- relocateManualIssues(rawInput, tracking);
-
return tracking;
}
@@ -92,23 +91,28 @@ public class Tracker<RAW extends Trackable, BASE extends Trackable> {
tracking.markRawsAsAssociated(trackedRaws);
}
- private void relocateManualIssues(Input<RAW> rawInput, Tracking<RAW, BASE> tracking) {
- Iterable<BASE> manualIssues = from(tracking.getUnmatchedBases()).filter(IsManual.INSTANCE);
+ private void relocateManualIssues(Input<RAW> rawInput, Input<BASE> baseInput, Tracking<RAW, BASE> tracking) {
+ // FIXME copy of Set if required to avoid concurrent modifications (see tracking.associateManualIssueToLine())
+ Iterable<BASE> manualIssues = from(new HashSet<>(tracking.getUnmatchedBases())).filter(IsManual.INSTANCE);
for (BASE base : manualIssues) {
if (base.getLine() == null) {
// no need to relocate. Location is unchanged.
- tracking.associateManualIssueToLine(base, 0);
+ tracking.associateManualIssueToLine(base, null);
} else {
- String lineHash = base.getLineHash();
- if (!Strings.isNullOrEmpty(lineHash)) {
- int[] rawLines = rawInput.getLineHashSequence().getLinesForHash(lineHash);
+ String baseHash = base.getLineHash();
+ if (Strings.isNullOrEmpty(baseHash)) {
+ baseHash = baseInput.getLineHashSequence().getHashForLine(base.getLine());
+ }
+ if (!Strings.isNullOrEmpty(baseHash)) {
+ int[] rawLines = rawInput.getLineHashSequence().getLinesForHash(baseHash);
if (rawLines.length == 1) {
tracking.associateManualIssueToLine(base, rawLines[0]);
- } else if (rawLines.length == 0 && base.getLine() <= rawInput.getLineHashSequence().length()) {
+ } else if (rawLines.length == 0 && rawInput.getLineHashSequence().hasLine(base.getLine())) {
// still valid (???). We didn't manage to correctly detect code move, so the
// issue is kept at the same location, even if code changes
tracking.associateManualIssueToLine(base, base.getLine());
}
+ // TODO if hash found multiple times, , pick the closest line
}
}
}
@@ -147,13 +151,9 @@ public class Tracker<RAW extends Trackable, BASE extends Trackable> {
}
LineAndLineHashKey that = (LineAndLineHashKey) o;
// start with most discriminant field
- if (!Objects.equals(line, that.line)) {
- return false;
- }
- if (!lineHash.equals(that.lineHash)) {
- return false;
- }
- return ruleKey.equals(that.ruleKey);
+ return Objects.equals(line, that.line)
+ && lineHash.equals(that.lineHash)
+ && ruleKey.equals(that.ruleKey);
}
@Override
@@ -175,7 +175,8 @@ public class Tracker<RAW extends Trackable, BASE extends Trackable> {
private static class LineHashAndMessageKey implements SearchKey {
private final RuleKey ruleKey;
- private final String message, lineHash;
+ private final String message;
+ private final String lineHash;
LineHashAndMessageKey(Trackable trackable) {
this.ruleKey = trackable.getRuleKey();
@@ -190,13 +191,9 @@ public class Tracker<RAW extends Trackable, BASE extends Trackable> {
}
LineHashAndMessageKey that = (LineHashAndMessageKey) o;
// start with most discriminant field
- if (!lineHash.equals(that.lineHash)) {
- return false;
- }
- if (!message.equals(that.message)) {
- return false;
- }
- return ruleKey.equals(that.ruleKey);
+ return lineHash.equals(that.lineHash)
+ && message.equals(that.message)
+ && ruleKey.equals(that.ruleKey);
}
@Override
@@ -234,13 +231,9 @@ public class Tracker<RAW extends Trackable, BASE extends Trackable> {
}
LineAndMessageKey that = (LineAndMessageKey) o;
// start with most discriminant field
- if (!Objects.equals(line, that.line)) {
- return false;
- }
- if (!message.equals(that.message)) {
- return false;
- }
- return ruleKey.equals(that.ruleKey);
+ return Objects.equals(line, that.line)
+ && message.equals(that.message)
+ && ruleKey.equals(that.ruleKey);
}
@Override
@@ -276,10 +269,8 @@ public class Tracker<RAW extends Trackable, BASE extends Trackable> {
}
LineAndLineHashKey that = (LineAndLineHashKey) o;
// start with most discriminant field
- if (!lineHash.equals(that.lineHash)) {
- return false;
- }
- return ruleKey.equals(that.ruleKey);
+ return lineHash.equals(that.lineHash)
+ && ruleKey.equals(that.ruleKey);
}
@Override
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/tracking/Tracking.java b/sonar-core/src/main/java/org/sonar/core/issue/tracking/Tracking.java
index f2a5306e500..579c6f51623 100644
--- a/sonar-core/src/main/java/org/sonar/core/issue/tracking/Tracking.java
+++ b/sonar-core/src/main/java/org/sonar/core/issue/tracking/Tracking.java
@@ -28,6 +28,7 @@ import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
public class Tracking<RAW extends Trackable, BASE extends Trackable> {
@@ -112,7 +113,7 @@ public class Tracking<RAW extends Trackable, BASE extends Trackable> {
return openManualIssues;
}
- void associateManualIssueToLine(BASE manualIssue, int line) {
+ void associateManualIssueToLine(BASE manualIssue, @Nullable Integer line) {
openManualIssues.put(line, manualIssue);
unmatchedBases.remove(manualIssue);
}
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/workflow/IssueWorkflow.java b/sonar-core/src/main/java/org/sonar/core/issue/workflow/IssueWorkflow.java
index 5b54986c84f..5d5275f8502 100644
--- a/sonar-core/src/main/java/org/sonar/core/issue/workflow/IssueWorkflow.java
+++ b/sonar-core/src/main/java/org/sonar/core/issue/workflow/IssueWorkflow.java
@@ -157,8 +157,8 @@ public class IssueWorkflow implements Startable {
.build())
.transition(Transition.builder("automaticclosemanual")
.from(Issue.STATUS_RESOLVED).to(Issue.STATUS_CLOSED)
- .conditions(new NotCondition(IsBeingClosed.INSTANCE), IsManual.INSTANCE)
- .functions(new SetCloseDate(true))
+ .conditions(IsManual.INSTANCE)
+ .functions(SetClosed.INSTANCE, new SetCloseDate(true))
.automatic()
.build())
diff --git a/sonar-core/src/main/java/org/sonar/core/issue/workflow/SetClosed.java b/sonar-core/src/main/java/org/sonar/core/issue/workflow/SetClosed.java
index 49b874220e7..06b71d28ff0 100644
--- a/sonar-core/src/main/java/org/sonar/core/issue/workflow/SetClosed.java
+++ b/sonar-core/src/main/java/org/sonar/core/issue/workflow/SetClosed.java
@@ -28,9 +28,6 @@ public enum SetClosed implements Function {
@Override
public void execute(Context context) {
DefaultIssue issue = (DefaultIssue) context.issue();
- if (!issue.isBeingClosed()) {
- throw new IllegalStateException("Issue is still open: " + issue);
- }
if (issue.isOnDisabledRule()) {
context.setResolution(Issue.RESOLUTION_REMOVED);
} else {
diff --git a/sonar-core/src/main/java/org/sonar/core/measure/db/MeasureDto.java b/sonar-core/src/main/java/org/sonar/core/measure/db/MeasureDto.java
index 04b36bf3ef3..1e288b3cea3 100644
--- a/sonar-core/src/main/java/org/sonar/core/measure/db/MeasureDto.java
+++ b/sonar-core/src/main/java/org/sonar/core/measure/db/MeasureDto.java
@@ -20,6 +20,7 @@
package org.sonar.core.measure.db;
+import com.google.common.base.Objects;
import java.nio.charset.StandardCharsets;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
@@ -239,4 +240,29 @@ public class MeasureDto {
return this;
}
+ @Override
+ public String toString() {
+ return Objects.toStringHelper(this)
+ .add("id", id)
+ .add("value", value)
+ .add("textValue", textValue)
+ .add("dataValue", dataValue)
+ .add("variation1", variation1)
+ .add("variation2", variation2)
+ .add("variation3", variation3)
+ .add("variation4", variation4)
+ .add("variation5", variation5)
+ .add("alertStatus", alertStatus)
+ .add("alertText", alertText)
+ .add("description", description)
+ .add("componentId", componentId)
+ .add("snapshotId", snapshotId)
+ .add("metricId", metricId)
+ .add("ruleId", ruleId)
+ .add("characteristicId", characteristicId)
+ .add("personId", personId)
+ .add("metricKey", metricKey)
+ .add("componentKey", componentKey)
+ .toString();
+ }
}
diff --git a/sonar-core/src/main/java/org/sonar/core/rule/RuleKeyFunctions.java b/sonar-core/src/main/java/org/sonar/core/rule/RuleKeyFunctions.java
new file mode 100644
index 00000000000..9a4f9c0ea04
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/rule/RuleKeyFunctions.java
@@ -0,0 +1,49 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.core.rule;
+
+import com.google.common.base.Function;
+import javax.annotation.Nonnull;
+import org.sonar.api.rule.RuleKey;
+
+public final class RuleKeyFunctions {
+
+ private RuleKeyFunctions() {
+ // only static methods
+ }
+
+ public static Function<String, RuleKey> stringToRuleKey() {
+ return StringToRuleKey.INSTANCE;
+ }
+
+ /**
+ * Transforms a string representation of key to {@link RuleKey}. It
+ * does not accept null string inputs.
+ */
+ private enum StringToRuleKey implements Function<String, RuleKey> {
+ INSTANCE;
+ @Nonnull
+ @Override
+ public RuleKey apply(@Nonnull String input) {
+ return RuleKey.parse(input);
+ }
+ }
+
+}
diff --git a/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueChangeMapper.xml b/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueChangeMapper.xml
index e810f1dc33c..b4aa445dae3 100644
--- a/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueChangeMapper.xml
+++ b/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueChangeMapper.xml
@@ -58,14 +58,14 @@
order by created_at asc
</select>
- <select id="selectChangelogOfUnresolvedIssuesByComponent" parameterType="map" resultType="IssueChange">
+ <select id="selectChangelogOfNonClosedIssuesByComponent" parameterType="map" resultType="IssueChange">
select
<include refid="issueChangeColumns"/>
from issue_changes c
inner join issues i on i.kee = c.issue_key
where i.component_uuid=#{componentUuid}
and c.change_type=#{changeType}
- and i.resolution is null
+ and i.status &lt;&gt; 'CLOSED'
</select>
</mapper>
diff --git a/sonar-core/src/test/java/org/sonar/core/issue/DefaultIssueTest.java b/sonar-core/src/test/java/org/sonar/core/issue/DefaultIssueTest.java
new file mode 100644
index 00000000000..4572e8ac599
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/core/issue/DefaultIssueTest.java
@@ -0,0 +1,219 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.core.issue;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.commons.lang.StringUtils;
+import org.junit.Test;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.issue.IssueComment;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.utils.Duration;
+
+import java.text.SimpleDateFormat;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.entry;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
+public class DefaultIssueTest {
+
+ DefaultIssue issue = new DefaultIssue();
+
+ @Test
+ public void test_setters_and_getters() throws Exception {
+ issue.setKey("ABCD")
+ .setComponentKey("org.sample.Sample")
+ .setProjectKey("Sample")
+ .setRuleKey(RuleKey.of("squid", "S100"))
+ .setLanguage("xoo")
+ .setSeverity("MINOR")
+ .setManualSeverity(true)
+ .setMessage("a message")
+ .setLine(7)
+ .setEffortToFix(1.2d)
+ .setDebt(Duration.create(28800L))
+ .setActionPlanKey("BCDE")
+ .setStatus(Issue.STATUS_CLOSED)
+ .setResolution(Issue.RESOLUTION_FIXED)
+ .setReporter("simon")
+ .setAssignee("julien")
+ .setAuthorLogin("steph")
+ .setChecksum("c7b5db46591806455cf082bb348631e8")
+ .setNew(true)
+ .setBeingClosed(true)
+ .setOnDisabledRule(true)
+ .setChanged(true)
+ .setSendNotifications(true)
+ .setCreationDate(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-19"))
+ .setUpdateDate(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-20"))
+ .setCloseDate(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-21"))
+ .setSelectedAt(1400000000000L);
+
+ assertThat(issue.key()).isEqualTo("ABCD");
+ assertThat(issue.componentKey()).isEqualTo("org.sample.Sample");
+ assertThat(issue.projectKey()).isEqualTo("Sample");
+ assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("squid", "S100"));
+ assertThat(issue.language()).isEqualTo("xoo");
+ assertThat(issue.severity()).isEqualTo("MINOR");
+ assertThat(issue.manualSeverity()).isTrue();
+ assertThat(issue.message()).isEqualTo("a message");
+ assertThat(issue.line()).isEqualTo(7);
+ assertThat(issue.effortToFix()).isEqualTo(1.2d);
+ assertThat(issue.debt()).isEqualTo(Duration.create(28800L));
+ assertThat(issue.actionPlanKey()).isEqualTo("BCDE");
+ assertThat(issue.status()).isEqualTo(Issue.STATUS_CLOSED);
+ assertThat(issue.resolution()).isEqualTo(Issue.RESOLUTION_FIXED);
+ assertThat(issue.reporter()).isEqualTo("simon");
+ assertThat(issue.assignee()).isEqualTo("julien");
+ assertThat(issue.authorLogin()).isEqualTo("steph");
+ assertThat(issue.checksum()).isEqualTo("c7b5db46591806455cf082bb348631e8");
+ assertThat(issue.isNew()).isTrue();
+ assertThat(issue.isBeingClosed()).isTrue();
+ assertThat(issue.isOnDisabledRule()).isTrue();
+ assertThat(issue.isChanged()).isTrue();
+ assertThat(issue.mustSendNotifications()).isTrue();
+ assertThat(issue.creationDate()).isEqualTo(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-19"));
+ assertThat(issue.updateDate()).isEqualTo(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-20"));
+ assertThat(issue.closeDate()).isEqualTo(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-21"));
+ assertThat(issue.selectedAt()).isEqualTo(1400000000000L);
+ }
+
+ @Test
+ public void set_empty_dates() {
+ issue
+ .setCreationDate(null)
+ .setUpdateDate(null)
+ .setCloseDate(null)
+ .setSelectedAt(null);
+
+ assertThat(issue.creationDate()).isNull();
+ assertThat(issue.updateDate()).isNull();
+ assertThat(issue.closeDate()).isNull();
+ assertThat(issue.selectedAt()).isNull();
+ }
+
+ @Test
+ public void test_attributes() throws Exception {
+ assertThat(issue.attribute("foo")).isNull();
+ issue.setAttribute("foo", "bar");
+ assertThat(issue.attribute("foo")).isEqualTo("bar");
+ issue.setAttribute("foo", "newbar");
+ assertThat(issue.attribute("foo")).isEqualTo("newbar");
+ issue.setAttribute("foo", null);
+ assertThat(issue.attribute("foo")).isNull();
+ }
+
+ @Test
+ public void setAttributes_should_not_clear_existing_values() {
+ issue.setAttributes(ImmutableMap.of("1", "one"));
+ assertThat(issue.attribute("1")).isEqualTo("one");
+
+ issue.setAttributes(ImmutableMap.of("2", "two"));
+ assertThat(issue.attributes()).containsOnly(entry("1", "one"), entry("2", "two"));
+
+ issue.setAttributes(null);
+ assertThat(issue.attributes()).containsOnly(entry("1", "one"), entry("2", "two"));
+ }
+
+ @Test
+ public void fail_on_empty_status() {
+ try {
+ issue.setStatus("");
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertThat(e).hasMessage("Status must be set");
+ }
+ }
+
+ @Test
+ public void fail_on_bad_severity() {
+ try {
+ issue.setSeverity("FOO");
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertThat(e).hasMessage("Not a valid severity: FOO");
+ }
+ }
+
+ @Test
+ public void message_should_be_abbreviated_if_too_long() {
+ issue.setMessage(StringUtils.repeat("a", 5000));
+ assertThat(issue.message()).hasSize(4000);
+ }
+
+ @Test
+ public void message_should_be_trimmed() {
+ issue.setMessage(" foo ");
+ assertThat(issue.message()).isEqualTo("foo");
+ }
+
+ @Test
+ public void message_could_be_null() {
+ issue.setMessage(null);
+ assertThat(issue.message()).isNull();
+ }
+
+ @Test
+ public void test_nullable_fields() throws Exception {
+ issue.setEffortToFix(null).setSeverity(null).setLine(null);
+ assertThat(issue.effortToFix()).isNull();
+ assertThat(issue.severity()).isNull();
+ assertThat(issue.line()).isNull();
+ }
+
+ @Test
+ public void test_equals_and_hashCode() throws Exception {
+ DefaultIssue a1 = new DefaultIssue().setKey("AAA");
+ DefaultIssue a2 = new DefaultIssue().setKey("AAA");
+ DefaultIssue b = new DefaultIssue().setKey("BBB");
+ assertThat(a1).isEqualTo(a1);
+ assertThat(a1).isEqualTo(a2);
+ assertThat(a1).isNotEqualTo(b);
+ assertThat(a1.hashCode()).isEqualTo(a1.hashCode());
+ }
+
+ @Test
+ public void comments_should_not_be_modifiable() {
+ DefaultIssue issue = new DefaultIssue().setKey("AAA");
+
+ List<IssueComment> comments = issue.comments();
+ assertThat(comments).isEmpty();
+
+ try {
+ comments.add(new DefaultIssueComment());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // ok
+ } catch (Exception e) {
+ fail("Unexpected exception: " + e);
+ }
+ }
+
+ @Test
+ public void all_changes_contain_current_change() {
+ IssueChangeContext issueChangeContext = mock(IssueChangeContext.class);
+ DefaultIssue issue = new DefaultIssue().setKey("AAA").setFieldChange(issueChangeContext, "actionPlan", "1.0", "1.1");
+
+ assertThat(issue.changes()).hasSize(1);
+ }
+}
diff --git a/sonar-core/src/test/java/org/sonar/core/issue/FieldDiffsTest.java b/sonar-core/src/test/java/org/sonar/core/issue/FieldDiffsTest.java
new file mode 100644
index 00000000000..beb207f0385
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/core/issue/FieldDiffsTest.java
@@ -0,0 +1,149 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.core.issue;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class FieldDiffsTest {
+
+ FieldDiffs diffs = new FieldDiffs();
+
+ @Test
+ public void diffs_should_be_empty_by_default() {
+ assertThat(diffs.diffs()).isEmpty();
+ }
+
+ @Test
+ public void test_diff() throws Exception {
+ diffs.setDiff("severity", "BLOCKER", "INFO");
+ diffs.setDiff("resolution", "OPEN", "FIXED");
+
+ assertThat(diffs.diffs()).hasSize(2);
+
+ FieldDiffs.Diff diff = diffs.diffs().get("severity");
+ assertThat(diff.oldValue()).isEqualTo("BLOCKER");
+ assertThat(diff.newValue()).isEqualTo("INFO");
+
+ diff = diffs.diffs().get("resolution");
+ assertThat(diff.oldValue()).isEqualTo("OPEN");
+ assertThat(diff.newValue()).isEqualTo("FIXED");
+ }
+
+ @Test
+ public void diff_with_long_values() {
+ diffs.setDiff("technicalDebt", 50l, "100");
+
+ FieldDiffs.Diff diff = diffs.diffs().get("technicalDebt");
+ assertThat(diff.oldValueLong()).isEqualTo(50l);
+ assertThat(diff.newValueLong()).isEqualTo(100l);
+ }
+
+ @Test
+ public void diff_with_empty_long_values() {
+ diffs.setDiff("technicalDebt", null, "");
+
+ FieldDiffs.Diff diff = diffs.diffs().get("technicalDebt");
+ assertThat(diff.oldValueLong()).isNull();
+ assertThat(diff.newValueLong()).isNull();
+ }
+
+ @Test
+ public void test_diff_by_key() throws Exception {
+ diffs.setDiff("severity", "BLOCKER", "INFO");
+ diffs.setDiff("resolution", "OPEN", "FIXED");
+
+ assertThat(diffs.diffs()).hasSize(2);
+
+ FieldDiffs.Diff diff = diffs.diffs().get("severity");
+ assertThat(diff.oldValue()).isEqualTo("BLOCKER");
+ assertThat(diff.newValue()).isEqualTo("INFO");
+
+ diff = diffs.diffs().get("resolution");
+ assertThat(diff.oldValue()).isEqualTo("OPEN");
+ assertThat(diff.newValue()).isEqualTo("FIXED");
+ }
+
+ @Test
+ public void should_keep_old_value() {
+ diffs.setDiff("severity", "BLOCKER", "INFO");
+ diffs.setDiff("severity", null, "MAJOR");
+ FieldDiffs.Diff diff = diffs.diffs().get("severity");
+ assertThat(diff.oldValue()).isEqualTo("BLOCKER");
+ assertThat(diff.newValue()).isEqualTo("MAJOR");
+ }
+
+ @Test
+ public void test_toString() throws Exception {
+ diffs.setDiff("severity", "BLOCKER", "INFO");
+ diffs.setDiff("resolution", "OPEN", "FIXED");
+
+ assertThat(diffs.toString()).isEqualTo("severity=BLOCKER|INFO,resolution=OPEN|FIXED");
+ }
+
+ @Test
+ public void test_toString_with_null_values() throws Exception {
+ diffs.setDiff("severity", null, "INFO");
+ diffs.setDiff("assignee", "user1", null);
+
+ assertThat(diffs.toString()).isEqualTo("severity=INFO,assignee=");
+ }
+
+ @Test
+ public void test_parse() throws Exception {
+ diffs = FieldDiffs.parse("severity=BLOCKER|INFO,resolution=OPEN|FIXED");
+ assertThat(diffs.diffs()).hasSize(2);
+
+ FieldDiffs.Diff diff = diffs.diffs().get("severity");
+ assertThat(diff.oldValue()).isEqualTo("BLOCKER");
+ assertThat(diff.newValue()).isEqualTo("INFO");
+
+ diff = diffs.diffs().get("resolution");
+ assertThat(diff.oldValue()).isEqualTo("OPEN");
+ assertThat(diff.newValue()).isEqualTo("FIXED");
+ }
+
+ @Test
+ public void test_parse_empty_values() throws Exception {
+ diffs = FieldDiffs.parse("severity=INFO,resolution=");
+ assertThat(diffs.diffs()).hasSize(2);
+
+ FieldDiffs.Diff diff = diffs.diffs().get("severity");
+ assertThat(diff.oldValue()).isEqualTo("");
+ assertThat(diff.newValue()).isEqualTo("INFO");
+
+ diff = diffs.diffs().get("resolution");
+ assertThat(diff.oldValue()).isEqualTo("");
+ assertThat(diff.newValue()).isEqualTo("");
+ }
+
+ @Test
+ public void test_parse_null() throws Exception {
+ diffs = FieldDiffs.parse(null);
+ assertThat(diffs.diffs()).isEmpty();
+ }
+
+ @Test
+ public void test_parse_empty() throws Exception {
+ diffs = FieldDiffs.parse("");
+ assertThat(diffs.diffs()).isEmpty();
+ }
+}
diff --git a/sonar-core/src/test/java/org/sonar/core/issue/db/IssueChangeDaoTest.java b/sonar-core/src/test/java/org/sonar/core/issue/db/IssueChangeDaoTest.java
index 2d379cfee6c..91530c88983 100644
--- a/sonar-core/src/test/java/org/sonar/core/issue/db/IssueChangeDaoTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/issue/db/IssueChangeDaoTest.java
@@ -113,11 +113,11 @@ public class IssueChangeDaoTest extends AbstractDaoTestCase {
}
@Test
- public void selectChangelogOfUnresolvedIssuesByComponent() {
- setupData("selectChangelogOfUnresolvedIssuesByComponent");
+ public void selectChangelogOfNonClosedIssuesByComponent() {
+ setupData("selectChangelogOfNonClosedIssuesByComponent");
- List<IssueChangeDto> dtos = dao.selectChangelogOfUnresolvedIssuesByComponent("FILE_1");
- assertThat(dtos).extracting("id").containsExactly(100L);
+ List<IssueChangeDto> dtos = dao.selectChangelogOfNonClosedIssuesByComponent("FILE_1");
+ assertThat(dtos).extracting("id").containsExactly(100L, 103L);
}
@Test
diff --git a/sonar-core/src/test/java/org/sonar/core/issue/tracking/TrackerTest.java b/sonar-core/src/test/java/org/sonar/core/issue/tracking/TrackerTest.java
index a9fe647095e..79a77fbd9e2 100644
--- a/sonar-core/src/test/java/org/sonar/core/issue/tracking/TrackerTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/issue/tracking/TrackerTest.java
@@ -19,6 +19,8 @@
*/
package org.sonar.core.issue.tracking;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -39,6 +41,7 @@ public class TrackerTest {
public static final RuleKey RULE_UNUSED_LOCAL_VARIABLE = RuleKey.of("java", "UnusedLocalVariable");
public static final RuleKey RULE_UNUSED_PRIVATE_METHOD = RuleKey.of("java", "UnusedPrivateMethod");
public static final RuleKey RULE_NOT_DESIGNED_FOR_EXTENSION = RuleKey.of("java", "NotDesignedForExtension");
+ public static final RuleKey RULE_MANUAL = RuleKey.of(RuleKey.MANUAL_REPOSITORY_KEY, "CodeReview");
@Rule
public ExpectedException thrown = ExpectedException.none();
@@ -60,20 +63,6 @@ public class TrackerTest {
}
@Test
- @Ignore
- public void different_issues_do_not_match() {
- FakeInput baseInput = new FakeInput("H1");
- Issue base = baseInput.createIssueOnLine(1, RULE_SYSTEM_PRINT, "msg1");
-
- FakeInput rawInput = new FakeInput("H2", "H3", "H4", "H5", "H6");
- Issue raw = rawInput.createIssueOnLine(5, RULE_SYSTEM_PRINT, "msg2");
-
- Tracking<Issue, Issue> tracking = tracker.track(rawInput, baseInput);
- assertThat(tracking.baseFor(raw)).isNull();
- assertThat(tracking.getUnmatchedBases()).containsOnly(base);
- }
-
- @Test
public void line_hash_has_greater_priority_than_line() {
FakeInput baseInput = new FakeInput("H1", "H2", "H3");
Issue base1 = baseInput.createIssueOnLine(1, RULE_SYSTEM_PRINT, "msg");
@@ -369,6 +358,65 @@ public class TrackerTest {
assertThat(tracking.getUnmatchedBases()).containsOnly(base2);
}
+ @Test
+ public void move_manual_issue_to_line_with_same_hash() {
+ FakeInput baseInput = new FakeInput("H1", "H2");
+ Issue issue = baseInput.createIssueOnLine(1, RULE_MANUAL, "message");
+ FakeInput rawInput = new FakeInput("H3", "H4", "H1");
+
+ Tracking<Issue, Issue> tracking = tracker.track(rawInput, baseInput);
+
+ assertThat(tracking.getUnmatchedBases()).isEmpty();
+ Multimap<Integer, Issue> openManualIssues = tracking.getOpenManualIssuesByLine();
+ assertThat(openManualIssues.keySet()).containsOnly(3);
+ assertThat(Iterables.getOnlyElement(openManualIssues.get(3))).isSameAs(issue);
+ }
+
+ @Test
+ public void do_not_move_manual_issue_if_line_hash_not_found_in_raw() {
+ FakeInput baseInput = new FakeInput("H1", "H2");
+ Issue issue = baseInput.createIssueOnLine(1, RULE_MANUAL, "message");
+ FakeInput rawInput = new FakeInput("H3", "H4", "H5");
+
+ Tracking<Issue, Issue> tracking = tracker.track(rawInput, baseInput);
+
+ assertThat(tracking.getUnmatchedBases()).isEmpty();
+ Multimap<Integer, Issue> openManualIssues = tracking.getOpenManualIssuesByLine();
+ assertThat(openManualIssues.keySet()).containsOnly(1);
+ assertThat(Iterables.getOnlyElement(openManualIssues.get(1))).isSameAs(issue);
+ }
+
+ @Test
+ public void do_not_match_manual_issue_if_hash_and_line_do_not_exist() {
+ // manual issue is on line 3 (hash H3) but this hash does not exist
+ // anymore nor the line 3.
+ FakeInput baseInput = new FakeInput("H1", "H2", "H3");
+ Issue issue = baseInput.createIssueOnLine(3, RULE_MANUAL, "message");
+ FakeInput rawInput = new FakeInput("H4");
+
+ Tracking<Issue, Issue> tracking = tracker.track(rawInput, baseInput);
+
+ assertThat(tracking.getUnmatchedBases()).containsOnly(issue);
+ assertThat(tracking.getOpenManualIssuesByLine().isEmpty()).isTrue();
+ }
+
+ @Test
+ @Ignore("not implemented yet")
+ public void move_to_closest_line_if_manual_issue_matches_multiple_hashes() {
+ // manual issue is on line 3 (hash H3) but this hash does not exist
+ // anymore nor the line 3.
+ FakeInput baseInput = new FakeInput("H1", "H2");
+ Issue issue = baseInput.createIssueOnLine(1, RULE_MANUAL, "message");
+ FakeInput rawInput = new FakeInput("H1", "H3", "H1");
+
+ Tracking<Issue, Issue> tracking = tracker.track(rawInput, baseInput);
+
+ assertThat(tracking.getUnmatchedBases()).isEmpty();
+ Multimap<Integer, Issue> openManualIssues = tracking.getOpenManualIssuesByLine();
+ assertThat(openManualIssues.keySet()).containsOnly(1);
+ assertThat(Iterables.getOnlyElement(openManualIssues.get(1))).isSameAs(issue);
+ }
+
private static class Issue implements Trackable {
private final RuleKey ruleKey;
private final Integer line;
diff --git a/sonar-core/src/test/java/org/sonar/core/issue/workflow/IssueWorkflowTest.java b/sonar-core/src/test/java/org/sonar/core/issue/workflow/IssueWorkflowTest.java
index ad5b9bef6e8..5ea870b09fb 100644
--- a/sonar-core/src/test/java/org/sonar/core/issue/workflow/IssueWorkflowTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/issue/workflow/IssueWorkflowTest.java
@@ -146,6 +146,7 @@ public class IssueWorkflowTest {
DefaultIssue issue = new DefaultIssue()
.setKey("ABCDE")
+ .setRuleKey(RuleKey.of("js", "S001"))
.setResolution(Issue.RESOLUTION_FIXED)
.setStatus(Issue.STATUS_RESOLVED)
.setNew(false)
@@ -269,12 +270,10 @@ public class IssueWorkflowTest {
@Test
public void manual_issues_be_resolved_then_closed() {
- // Manual issue because of reporter
DefaultIssue issue = new DefaultIssue()
.setKey("ABCDE")
.setStatus(Issue.STATUS_OPEN)
- .setRuleKey(RuleKey.of("manual", "Performance"))
- .setReporter("simon");
+ .setRuleKey(RuleKey.of(RuleKey.MANUAL_REPOSITORY_KEY, "Performance"));
workflow.start();
diff --git a/sonar-core/src/test/java/org/sonar/core/issue/workflow/SetClosedTest.java b/sonar-core/src/test/java/org/sonar/core/issue/workflow/SetClosedTest.java
index 3418cd9d1d3..f288fcf4cfb 100644
--- a/sonar-core/src/test/java/org/sonar/core/issue/workflow/SetClosedTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/issue/workflow/SetClosedTest.java
@@ -23,9 +23,10 @@ import org.junit.Test;
import org.sonar.api.issue.Issue;
import org.sonar.core.issue.DefaultIssue;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import static org.sonar.core.issue.workflow.SetClosed.INSTANCE;
public class SetClosedTest {
@@ -49,19 +50,6 @@ public class SetClosedTest {
}
@Test
- public void should_fail_if_issue_is_not_resolved() {
- Issue issue = new DefaultIssue().setBeingClosed(false);
- when(context.issue()).thenReturn(issue);
- try {
- INSTANCE.execute(context);
- fail();
- } catch (IllegalStateException e) {
- assertThat(e.getMessage()).contains("Issue is still open");
- verify(context, never()).setResolution(anyString());
- }
- }
-
- @Test
public void line_number_must_be_unset() {
Issue issue = new DefaultIssue().setBeingClosed(true).setLine(10);
when(context.issue()).thenReturn(issue);
diff --git a/sonar-core/src/test/java/org/sonar/core/rule/RuleKeyFunctionsTest.java b/sonar-core/src/test/java/org/sonar/core/rule/RuleKeyFunctionsTest.java
new file mode 100644
index 00000000000..cf430a18265
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/core/rule/RuleKeyFunctionsTest.java
@@ -0,0 +1,46 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.core.rule;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import org.junit.Test;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.test.TestUtils;
+
+import static com.google.common.collect.FluentIterable.from;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class RuleKeyFunctionsTest {
+
+ @Test
+ public void stringToRuleKey() throws Exception {
+ Collection<String> strings = Arrays.asList("js:S001", "java:S002");
+ List<RuleKey> keys = from(strings).transform(RuleKeyFunctions.stringToRuleKey()).toList();
+
+ assertThat(keys).containsExactly(RuleKey.of("js", "S001"), RuleKey.of("java", "S002"));
+ }
+
+ @Test
+ public void on_static_methods() throws Exception {
+ assertThat(TestUtils.hasOnlyPrivateConstructors(RuleKeyFunctions.class)).isTrue();
+ }
+}
diff --git a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/selectChangelogOfUnresolvedIssuesByComponent.xml b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/selectChangelogOfNonClosedIssuesByComponent.xml
index 966f7c31cac..d809d01a49d 100644
--- a/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/selectChangelogOfUnresolvedIssuesByComponent.xml
+++ b/sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/selectChangelogOfNonClosedIssuesByComponent.xml
@@ -1,6 +1,6 @@
<dataset>
- <!-- Unresolved -->
+ <!-- Unresolved. To be included -->
<issues
id="1"
kee="UNRESOLVED_ON_FILE_1"
@@ -52,7 +52,7 @@
issue_change_creation_date="[null]"
/>
- <!-- Resolved: to be ignored -->
+ <!-- Resolved but not closed. To be included -->
<issues
id="2"
kee="RESOLVED_ON_FILE_1"
@@ -90,9 +90,47 @@
issue_change_creation_date="1410213600000"
/>
+ <!-- Closed. To be excluded -->
+ <issues
+ id="3"
+ kee="CLOSED_ON_FILE_1"
+ component_uuid="FILE_1"
+ project_uuid="PROJECT_1"
+ resolution="FIXED"
+ status="CLOSED"
+ rule_id="501"
+ severity="MAJOR"
+ manual_severity="[false]"
+ message="[null]"
+ line="120"
+ effort_to_fix="[null]"
+ checksum="[null]"
+ reporter="[null]"
+ assignee="user"
+ author_login="[null]"
+ issue_attributes="[null]"
+ issue_creation_date="1366063200000"
+ issue_update_date="1366063200000"
+ issue_close_date="1366063200000"
+ created_at="1400000000000"
+ updated_at="[null]"
+ />
+
+ <issue_changes
+ id="104"
+ kee="104"
+ issue_key="CLOSED_ON_FILE_1"
+ user_login="arthur"
+ change_type="diff"
+ change_data="severity=MAJOR|BLOCKER"
+ created_at="1410213600000"
+ updated_at="1410213600000"
+ issue_change_creation_date="1410213600000"
+ />
+
<!-- Unresolved on other file -->
<issues
- id="3"
+ id="4"
kee="UNRESOLVED_ON_FILE_2"
component_uuid="FILE_2"
project_uuid="PROJECT_1"
@@ -118,8 +156,8 @@
<!-- diff -->
<issue_changes
- id="104"
- kee="104"
+ id="105"
+ kee="105"
issue_key="UNRESOLVED_ON_FILE_2"
user_login="arthur"
change_type="diff"
@@ -131,8 +169,8 @@
<!-- comment -->
<issue_changes
- id="105"
- kee="105"
+ id="106"
+ kee="106"
issue_key="UNRESOLVED_ON_FILE_2"
user_login="arthur"
change_type="comment"
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/Rule.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/Rule.java
index 0866cb8b203..9d334b6d5c6 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/Rule.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/Rule.java
@@ -50,11 +50,19 @@ public interface Rule {
RuleStatus status();
/**
- * Remediation function : can by Linear (with a coefficient), Linear with offset (with a coefficient and an offset) or Constant per issue (with an offset)
- *
* @since 4.3
+ * @deprecated since 5.2 as any computation of data are moved to server's Compute Engine. Calling this method throws an exception.
*/
@CheckForNull
+ @Deprecated
+ String debtSubCharacteristic();
+
+ /**
+ * @since 4.3
+ * @deprecated since 5.2 as any computation of data are moved to server's Compute Engine. Calling this method throws an exception.
+ */
+ @CheckForNull
+ @Deprecated
DebtRemediationFunction debtRemediationFunction();
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultRule.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultRule.java
index 097694e146c..cd6a738e3f1 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultRule.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/DefaultRule.java
@@ -42,7 +42,6 @@ public class DefaultRule implements Rule {
private final String description;
private final String internalKey;
private final RuleStatus status;
- private final DebtRemediationFunction debtRemediationFunction;
private final Map<String, RuleParam> params;
DefaultRule(NewRule newRule) {
@@ -53,7 +52,6 @@ public class DefaultRule implements Rule {
this.description = newRule.description;
this.internalKey = newRule.internalKey;
this.status = newRule.status;
- this.debtRemediationFunction = newRule.debtRemediationFunction;
ImmutableMap.Builder<String, RuleParam> builder = ImmutableMap.builder();
for (NewRuleParam newRuleParam : newRule.params.values()) {
@@ -98,8 +96,13 @@ public class DefaultRule implements Rule {
}
@Override
+ public String debtSubCharacteristic() {
+ throw new UnsupportedOperationException("Debt characteristic is not available by analyzer since version 5.2 (data computation moved to server)");
+ }
+
+ @Override
public DebtRemediationFunction debtRemediationFunction() {
- return debtRemediationFunction;
+ throw new UnsupportedOperationException("Debt remediation function is not available by analyzer since version 5.2 (data computation moved to server)");
}
@Override
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/NewRule.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/NewRule.java
index 2e174b2b2ae..5d6e585b78f 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/NewRule.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/rule/internal/NewRule.java
@@ -19,18 +19,15 @@
*/
package org.sonar.api.batch.rule.internal;
+import java.util.HashMap;
+import java.util.Map;
+import javax.annotation.Nullable;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
-import org.sonar.api.batch.debt.DebtRemediationFunction;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.RuleStatus;
import org.sonar.api.rule.Severity;
-import javax.annotation.Nullable;
-
-import java.util.HashMap;
-import java.util.Map;
-
public class NewRule {
private static final String DEFAULT_SEVERITY = Severity.defaultSeverity();
@@ -41,7 +38,6 @@ public class NewRule {
String description;
String severity = DEFAULT_SEVERITY;
String internalKey;
- DebtRemediationFunction debtRemediationFunction;
RuleStatus status = RuleStatus.defaultStatus();
Map<String, NewRuleParam> params = new HashMap<>();
@@ -79,11 +75,6 @@ public class NewRule {
return this;
}
- public NewRule setDebtRemediationFunction(@Nullable DebtRemediationFunction f) {
- this.debtRemediationFunction = f;
- return this;
- }
-
public NewRuleParam addParam(String paramKey) {
if (params.containsKey(paramKey)) {
throw new IllegalStateException(String.format("Parameter '%s' already exists on rule '%s'", paramKey, key));
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/condition/HasIssuePropertyCondition.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/condition/HasIssuePropertyCondition.java
index 6f623742638..cde5ae2bc09 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/condition/HasIssuePropertyCondition.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/condition/HasIssuePropertyCondition.java
@@ -43,6 +43,6 @@ public final class HasIssuePropertyCondition implements Condition {
@Override
public boolean matches(Issue issue) {
- return !Strings.isNullOrEmpty(issue.attributes().get(propertyKey));
+ return !Strings.isNullOrEmpty(issue.attribute(propertyKey));
}
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java
index 391cc49eb99..b5f423c257e 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java
@@ -20,17 +20,19 @@
package org.sonar.api.measures;
import com.google.common.annotations.Beta;
-import java.io.Serializable;
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-import java.util.Date;
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.apache.commons.lang.math.NumberUtils;
import org.sonar.api.technicaldebt.batch.Characteristic;
import org.sonar.api.technicaldebt.batch.Requirement;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.Date;
+
/**
* A class to handle measures.
*
@@ -60,6 +62,8 @@ public class Measure<G extends Serializable> implements Serializable {
protected Double variation4;
protected Double variation5;
protected String url;
+ protected Characteristic characteristic;
+ protected Requirement requirement;
protected Integer personId;
protected PersistenceMode persistenceMode = PersistenceMode.FULL;
private boolean fromCore;
@@ -627,13 +631,14 @@ public class Measure<G extends Serializable> implements Serializable {
*/
@CheckForNull
public final Characteristic getCharacteristic() {
- return null;
+ return characteristic;
}
/**
* @since 4.1
*/
public final Measure<G> setCharacteristic(@Nullable Characteristic characteristic) {
+ this.characteristic = characteristic;
return this;
}
@@ -644,7 +649,7 @@ public class Measure<G extends Serializable> implements Serializable {
@Deprecated
@CheckForNull
public final Requirement getRequirement() {
- return null;
+ return requirement;
}
/**
@@ -653,6 +658,7 @@ public class Measure<G extends Serializable> implements Serializable {
*/
@Deprecated
public final Measure<G> setRequirement(@Nullable Requirement requirement) {
+ this.requirement = requirement;
return this;
}
@@ -731,12 +737,16 @@ public class Measure<G extends Serializable> implements Serializable {
if (metricKey != null ? !metricKey.equals(measure.metricKey) : (measure.metricKey != null)) {
return false;
}
+ if (characteristic != null ? !characteristic.equals(measure.characteristic) : (measure.characteristic != null)) {
+ return false;
+ }
return !(personId != null ? !personId.equals(measure.personId) : (measure.personId != null));
}
@Override
public int hashCode() {
int result = metricKey != null ? metricKey.hashCode() : 0;
+ result = 31 * result + (characteristic != null ? characteristic.hashCode() : 0);
result = 31 * result + (personId != null ? personId.hashCode() : 0);
return result;
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasuresFilters.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasuresFilters.java
index e3be32a43a0..f872d322aea 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasuresFilters.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasuresFilters.java
@@ -205,7 +205,7 @@ public final class MeasuresFilters {
}
/**
- * @deprecated since 5.2. Useless by design because of Compute Engine
+ * @deprecated since 5.2. The measures related to rules are computed on server side by Compute Engine.
*/
@Deprecated
private abstract static class AbstractRuleMeasureFilter<M> extends MetricFilter<M> {
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleKey.java b/sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleKey.java
index 7c23bc4734f..0caa78b2d5e 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleKey.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/rule/RuleKey.java
@@ -23,12 +23,14 @@ import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import java.io.Serializable;
+import javax.annotation.concurrent.Immutable;
/**
* Key of a rule. Unique among all the rule repositories.
*
* @since 3.6
*/
+@Immutable
public class RuleKey implements Serializable {
public static final String MANUAL_REPOSITORY_KEY = "manual";
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/debt/DebtRemediationFunction.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/debt/DebtRemediationFunction.java
index d8f6456e9f2..73160afcab2 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/server/debt/DebtRemediationFunction.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/debt/DebtRemediationFunction.java
@@ -43,7 +43,23 @@ import javax.annotation.CheckForNull;
public interface DebtRemediationFunction {
enum Type {
- LINEAR, LINEAR_OFFSET, CONSTANT_ISSUE
+ LINEAR(true, false), LINEAR_OFFSET(true, true), CONSTANT_ISSUE(false, true);
+
+ private final boolean usesCoefficient;
+ private final boolean usesOffset;
+
+ Type(boolean usesCoefficient, boolean usesOffset) {
+ this.usesCoefficient = usesCoefficient;
+ this.usesOffset = usesOffset;
+ }
+
+ public boolean usesCoefficient() {
+ return usesCoefficient;
+ }
+
+ public boolean usesOffset() {
+ return usesOffset;
+ }
}
Type type();
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/rule/internal/RulesBuilderTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/rule/internal/RulesBuilderTest.java
index 3c5270f6996..cceb006ae11 100644
--- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/rule/internal/RulesBuilderTest.java
+++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/rule/internal/RulesBuilderTest.java
@@ -51,7 +51,6 @@ public class RulesBuilderTest {
newSquid1.setInternalKey("foo=bar");
newSquid1.setSeverity(Severity.CRITICAL);
newSquid1.setStatus(RuleStatus.BETA);
- newSquid1.setDebtRemediationFunction(DebtRemediationFunction.create(DebtRemediationFunction.Type.LINEAR_OFFSET, Duration.create(10), Duration.create(60)));
newSquid1.addParam("min");
newSquid1.addParam("max").setDescription("Maximum");
// most simple rule
@@ -73,9 +72,6 @@ public class RulesBuilderTest {
assertThat(squid1.internalKey()).isEqualTo("foo=bar");
assertThat(squid1.status()).isEqualTo(RuleStatus.BETA);
assertThat(squid1.severity()).isEqualTo(Severity.CRITICAL);
- assertThat(squid1.debtRemediationFunction().type()).isEqualTo(DebtRemediationFunction.Type.LINEAR_OFFSET);
- assertThat(squid1.debtRemediationFunction().coefficient()).isEqualTo(Duration.create(10));
- assertThat(squid1.debtRemediationFunction().offset()).isEqualTo(Duration.create(60));
assertThat(squid1.params()).hasSize(2);
assertThat(squid1.param("min").key()).isEqualTo("min");
assertThat(squid1.param("min").description()).isNull();
@@ -89,7 +85,6 @@ public class RulesBuilderTest {
assertThat(squid2.internalKey()).isNull();
assertThat(squid2.status()).isEqualTo(RuleStatus.defaultStatus());
assertThat(squid2.severity()).isEqualTo(Severity.defaultSeverity());
- assertThat(squid2.debtRemediationFunction()).isNull();
assertThat(squid2.params()).isEmpty();
}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/issue/action/ActionTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/issue/action/ActionTest.java
index 3f0275401c0..3d7d40bc6b0 100644
--- a/sonar-plugin-api/src/test/java/org/sonar/api/issue/action/ActionTest.java
+++ b/sonar-plugin-api/src/test/java/org/sonar/api/issue/action/ActionTest.java
@@ -17,97 +17,77 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-///*
-// * 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.api.issue.action;
-//
-//import org.junit.Rule;
-//import org.junit.Test;
-//import org.junit.rules.ExpectedException;
-//import org.sonar.api.issue.condition.Condition;
-//import org.sonar.api.issue.internal.DefaultIssue;
-//
-//import static org.assertj.core.api.Assertions.assertThat;
-//import static org.mockito.Mockito.mock;
-//import static org.mockito.Mockito.when;
-//
-//public class ActionTest {
-// @Rule
-// public ExpectedException thrown = ExpectedException.none();
-//
-// Condition condition1 = mock(Condition.class);
-// Condition condition2 = mock(Condition.class);
-// Function function1 = mock(Function.class);
-// Function function2 = mock(Function.class);
-//
-// @Test
-// public void test_action() throws Exception {
-// Action action = new Action("link-to-jira")
-// .setConditions(condition1, condition2)
-// .setFunctions(function1, function2);
-//
-// assertThat(action.key()).isEqualTo("link-to-jira");
-// assertThat(action.conditions()).containsOnly(condition1, condition2);
-// assertThat(action.functions()).containsOnly(function1, function2);
-// }
-//
-// @Test
-// public void key_should_be_set() {
-// thrown.expectMessage("Action key must be set");
-//
-// new Action("");
-// }
-//
-// @Test
-// public void should_verify_conditions() {
-// DefaultIssue issue = new DefaultIssue();
-// Action action = new Action("link-to-jira")
-// .setConditions(condition1, condition2);
-//
-// when(condition1.matches(issue)).thenReturn(true);
-// when(condition2.matches(issue)).thenReturn(false);
-// assertThat(action.supports(issue)).isFalse();
-//
-// when(condition1.matches(issue)).thenReturn(true);
-// when(condition2.matches(issue)).thenReturn(true);
-// assertThat(action.supports(issue)).isTrue();
-// }
-//
-// @Test
-// public void test_equals_and_hashCode() throws Exception {
-// Action t1 = new Action("link-to-jira");
-// Action t2 = new Action("link-to-jira");
-// Action t3 = new Action("comment");
-//
-// assertThat(t1).isEqualTo(t1);
-// assertThat(t1).isEqualTo(t2);
-// assertThat(t1).isNotEqualTo(t3);
-//
-// assertThat(t1.hashCode()).isEqualTo(t1.hashCode());
-// }
-//
-// @Test
-// public void test_toString() throws Exception {
-// Action t1 = new Action("link-to-jira");
-// assertThat(t1.toString()).isEqualTo("link-to-jira");
-// }
-//
-//}
+package org.sonar.api.issue.action;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.issue.condition.Condition;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ActionTest {
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ Condition condition1 = mock(Condition.class);
+ Condition condition2 = mock(Condition.class);
+ Function function1 = mock(Function.class);
+ Function function2 = mock(Function.class);
+
+ @Test
+ public void test_action() throws Exception {
+ Action action = new Action("link-to-jira")
+ .setConditions(condition1, condition2)
+ .setFunctions(function1, function2);
+
+ assertThat(action.key()).isEqualTo("link-to-jira");
+ assertThat(action.conditions()).containsOnly(condition1, condition2);
+ assertThat(action.functions()).containsOnly(function1, function2);
+ }
+
+ @Test
+ public void key_should_be_set() {
+ thrown.expectMessage("Action key must be set");
+
+ new Action("");
+ }
+
+ @Test
+ public void should_verify_conditions() {
+ Issue issue = mock(Issue.class);
+ Action action = new Action("link-to-jira")
+ .setConditions(condition1, condition2);
+
+ when(condition1.matches(issue)).thenReturn(true);
+ when(condition2.matches(issue)).thenReturn(false);
+ assertThat(action.supports(issue)).isFalse();
+
+ when(condition1.matches(issue)).thenReturn(true);
+ when(condition2.matches(issue)).thenReturn(true);
+ assertThat(action.supports(issue)).isTrue();
+ }
+
+ @Test
+ public void test_equals_and_hashCode() throws Exception {
+ Action t1 = new Action("link-to-jira");
+ Action t2 = new Action("link-to-jira");
+ Action t3 = new Action("comment");
+
+ assertThat(t1).isEqualTo(t1);
+ assertThat(t1).isEqualTo(t2);
+ assertThat(t1).isNotEqualTo(t3);
+
+ assertThat(t1.hashCode()).isEqualTo(t1.hashCode());
+ }
+
+ @Test
+ public void test_toString() throws Exception {
+ Action t1 = new Action("link-to-jira");
+ assertThat(t1.toString()).isEqualTo("link-to-jira");
+ }
+
+}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/issue/condition/HasIssuePropertyConditionTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/issue/condition/HasIssuePropertyConditionTest.java
index 9f2d40e12cb..b9bef1f1f0e 100644
--- a/sonar-plugin-api/src/test/java/org/sonar/api/issue/condition/HasIssuePropertyConditionTest.java
+++ b/sonar-plugin-api/src/test/java/org/sonar/api/issue/condition/HasIssuePropertyConditionTest.java
@@ -17,66 +17,53 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-///*
-// * 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.api.issue.condition;
-//
-//import org.junit.Rule;
-//import org.junit.Test;
-//import org.junit.rules.ExpectedException;
-//import org.sonar.api.issue.internal.DefaultIssue;
-//
-//import static org.assertj.core.api.Assertions.assertThat;
-//
-//public class HasIssuePropertyConditionTest {
-//
-// @Rule
-// public ExpectedException thrown = ExpectedException.none();
-//
-// DefaultIssue issue = new DefaultIssue();
-//
-// @Test
-// public void should_match() {
-// HasIssuePropertyCondition condition = new HasIssuePropertyCondition("foo");
-//
-// assertThat(condition.matches(issue)).isFalse();
-// assertThat(condition.matches(issue.setAttribute("foo", ""))).isFalse();
-// assertThat(condition.matches(issue.setAttribute("foo", "bar"))).isTrue();
-// }
-//
-// @Test
-// public void should_get_property_key() {
-// HasIssuePropertyCondition condition = new HasIssuePropertyCondition("foo");
-// assertThat(condition.getPropertyKey()).isEqualTo("foo");
-// }
-//
-// @Test
-// public void shoul_fail_if_null_property() {
-// thrown.expect(IllegalArgumentException.class);
-// new HasIssuePropertyCondition(null);
-// }
-//
-// @Test
-// public void should_fail_if_empty_property() {
-// thrown.expect(IllegalArgumentException.class);
-// new HasIssuePropertyCondition("");
-// }
-//
-//}
+package org.sonar.api.issue.condition;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.issue.Issue;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class HasIssuePropertyConditionTest {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ Issue issue = mock(Issue.class);
+
+ @Test
+ public void should_match() {
+ HasIssuePropertyCondition condition = new HasIssuePropertyCondition("foo");
+
+ assertThat(condition.matches(issue)).isFalse();
+
+ when(issue.attribute("foo")).thenReturn("");
+ assertThat(condition.matches(issue)).isFalse();
+
+ when(issue.attribute("foo")).thenReturn("bar");
+ assertThat(condition.matches(issue)).isTrue();
+ }
+
+ @Test
+ public void should_get_property_key() {
+ HasIssuePropertyCondition condition = new HasIssuePropertyCondition("foo");
+ assertThat(condition.getPropertyKey()).isEqualTo("foo");
+ }
+
+ @Test
+ public void shoul_fail_if_null_property() {
+ thrown.expect(IllegalArgumentException.class);
+ new HasIssuePropertyCondition(null);
+ }
+
+ @Test
+ public void should_fail_if_empty_property() {
+ thrown.expect(IllegalArgumentException.class);
+ new HasIssuePropertyCondition("");
+ }
+
+}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/issue/condition/HasResolutionTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/issue/condition/HasResolutionTest.java
index 0762d06330b..4648aac3010 100644
--- a/sonar-plugin-api/src/test/java/org/sonar/api/issue/condition/HasResolutionTest.java
+++ b/sonar-plugin-api/src/test/java/org/sonar/api/issue/condition/HasResolutionTest.java
@@ -17,44 +17,30 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-///*
-// * 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.api.issue.condition;
-//
-//import org.junit.Test;
-//import org.sonar.api.issue.Issue;
-//import org.sonar.api.issue.internal.DefaultIssue;
-//
-//import static org.assertj.core.api.Assertions.assertThat;
-//
-//public class HasResolutionTest {
-//
-// DefaultIssue issue = new DefaultIssue();
-//
-// @Test
-// public void should_match() {
-// HasResolution condition = new HasResolution(Issue.RESOLUTION_FIXED, Issue.RESOLUTION_FALSE_POSITIVE);
-//
-// assertThat(condition.matches(issue.setResolution("FIXED"))).isTrue();
-// assertThat(condition.matches(issue.setResolution("FALSE-POSITIVE"))).isTrue();
-//
-// assertThat(condition.matches(issue.setResolution("Fixed"))).isFalse();
-// }
-//}
+package org.sonar.api.issue.condition;
+
+import org.junit.Test;
+import org.sonar.api.issue.Issue;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class HasResolutionTest {
+
+ Issue issue = mock(Issue.class);
+
+ @Test
+ public void should_match() {
+ HasResolution condition = new HasResolution(Issue.RESOLUTION_FIXED, Issue.RESOLUTION_FALSE_POSITIVE);
+
+ when(issue.resolution()).thenReturn("FIXED");
+ assertThat(condition.matches(issue)).isTrue();
+
+ when(issue.resolution()).thenReturn("FALSE-POSITIVE");
+ assertThat(condition.matches(issue)).isTrue();
+
+ when(issue.resolution()).thenReturn("Fixed");
+ assertThat(condition.matches(issue)).isFalse();
+ }
+}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/issue/condition/HasStatusTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/issue/condition/HasStatusTest.java
index 5d9258fa07b..68be04898ad 100644
--- a/sonar-plugin-api/src/test/java/org/sonar/api/issue/condition/HasStatusTest.java
+++ b/sonar-plugin-api/src/test/java/org/sonar/api/issue/condition/HasStatusTest.java
@@ -17,47 +17,37 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-///*
-// * 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.api.issue.condition;
-//
-//import org.junit.Test;
-//import org.sonar.api.issue.internal.DefaultIssue;
-//
-//import static org.assertj.core.api.Assertions.assertThat;
-//
-//public class HasStatusTest {
-//
-// DefaultIssue issue = new DefaultIssue();
-//
-// @Test
-// public void should_match() {
-// HasStatus condition = new HasStatus("OPEN", "REOPENED", "CONFIRMED");
-//
-// assertThat(condition.matches(issue.setStatus("OPEN"))).isTrue();
-// assertThat(condition.matches(issue.setStatus("REOPENED"))).isTrue();
-// assertThat(condition.matches(issue.setStatus("CONFIRMED"))).isTrue();
-//
-// assertThat(condition.matches(issue.setStatus("open"))).isFalse();
-// assertThat(condition.matches(issue.setStatus("CLOSED"))).isFalse();
-// }
-//
-//}
+package org.sonar.api.issue.condition;
+
+import org.junit.Test;
+import org.sonar.api.issue.Issue;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class HasStatusTest {
+
+ Issue issue = mock(Issue.class);
+
+ @Test
+ public void should_match() {
+ HasStatus condition = new HasStatus("OPEN", "REOPENED", "CONFIRMED");
+
+ when(issue.status()).thenReturn("OPEN");
+ assertThat(condition.matches(issue)).isTrue();
+
+ when(issue.status()).thenReturn("REOPENED");
+ assertThat(condition.matches(issue)).isTrue();
+
+ when(issue.status()).thenReturn("CONFIRMED");
+ assertThat(condition.matches(issue)).isTrue();
+
+ when(issue.status()).thenReturn("open");
+ assertThat(condition.matches(issue)).isFalse();
+
+ when(issue.status()).thenReturn("CLOSED");
+ assertThat(condition.matches(issue)).isFalse();
+ }
+
+}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/issue/condition/IsUnResolvedTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/issue/condition/IsUnResolvedTest.java
index 84159f3f242..65303bd2472 100644
--- a/sonar-plugin-api/src/test/java/org/sonar/api/issue/condition/IsUnResolvedTest.java
+++ b/sonar-plugin-api/src/test/java/org/sonar/api/issue/condition/IsUnResolvedTest.java
@@ -17,42 +17,26 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-///*
-// * 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.api.issue.condition;
-//
-//import org.junit.Test;
-//import org.sonar.api.issue.internal.DefaultIssue;
-//
-//import static org.assertj.core.api.Assertions.assertThat;
-//
-//public class IsUnResolvedTest {
-//
-// DefaultIssue issue = new DefaultIssue();
-//
-// @Test
-// public void should_match() {
-// IsUnResolved condition = new IsUnResolved();
-//
-// assertThat(condition.matches(issue)).isTrue();
-// assertThat(condition.matches(issue.setResolution("FIXED"))).isFalse();
-// }
-//}
+package org.sonar.api.issue.condition;
+
+import org.junit.Test;
+import org.sonar.api.issue.Issue;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class IsUnResolvedTest {
+
+ Issue issue = mock(Issue.class);
+
+ @Test
+ public void should_match() {
+ IsUnResolved condition = new IsUnResolved();
+
+ assertThat(condition.matches(issue)).isTrue();
+
+ when(issue.resolution()).thenReturn("FIXED");
+ assertThat(condition.matches(issue)).isFalse();
+ }
+}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/issue/condition/NotConditionTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/issue/condition/NotConditionTest.java
index ae2c76de370..aa646d14514 100644
--- a/sonar-plugin-api/src/test/java/org/sonar/api/issue/condition/NotConditionTest.java
+++ b/sonar-plugin-api/src/test/java/org/sonar/api/issue/condition/NotConditionTest.java
@@ -17,48 +17,30 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-///*
-// * 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.api.issue.condition;
-//
-//import org.junit.Test;
-//import org.mockito.Mockito;
-//import org.sonar.api.issue.Issue;
-//import org.sonar.api.issue.internal.DefaultIssue;
-//
-//import static org.assertj.core.api.Assertions.assertThat;
-//import static org.mockito.Matchers.any;
-//import static org.mockito.Mockito.when;
-//
-//public class NotConditionTest {
-//
-// Condition target = Mockito.mock(Condition.class);
-//
-// @Test
-// public void should_match_opposite() {
-// NotCondition condition = new NotCondition(target);
-//
-// when(target.matches(any(Issue.class))).thenReturn(true);
-// assertThat(condition.matches(new DefaultIssue())).isFalse();
-//
-// when(target.matches(any(Issue.class))).thenReturn(false);
-// assertThat(condition.matches(new DefaultIssue())).isTrue();
-// }
-//}
+package org.sonar.api.issue.condition;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.sonar.api.issue.Issue;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class NotConditionTest {
+
+ Condition target = Mockito.mock(Condition.class);
+ Issue issue = mock(Issue.class);
+
+ @Test
+ public void should_match_opposite() {
+ NotCondition condition = new NotCondition(target);
+
+ when(target.matches(any(Issue.class))).thenReturn(true);
+ assertThat(condition.matches(issue)).isFalse();
+
+ when(target.matches(any(Issue.class))).thenReturn(false);
+ assertThat(condition.matches(issue)).isTrue();
+ }
+}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/issue/internal/DefaultIssueTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/issue/internal/DefaultIssueTest.java
deleted file mode 100644
index 7db2fa9acb4..00000000000
--- a/sonar-plugin-api/src/test/java/org/sonar/api/issue/internal/DefaultIssueTest.java
+++ /dev/null
@@ -1,238 +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.
- */
-///*
-// * 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.api.issue.internal;
-//
-//import com.google.common.collect.ImmutableMap;
-//import org.apache.commons.lang.StringUtils;
-//import org.junit.Test;
-//import org.sonar.api.issue.Issue;
-//import org.sonar.api.issue.IssueComment;
-//import org.sonar.api.rule.RuleKey;
-//import org.sonar.api.utils.Duration;
-//
-//import java.text.SimpleDateFormat;
-//import java.util.List;
-//
-//import static org.assertj.core.api.Assertions.assertThat;
-//import static org.assertj.core.api.Assertions.entry;
-//import static org.junit.Assert.fail;
-//import static org.mockito.Mockito.mock;
-//
-//public class DefaultIssueTest {
-//
-// DefaultIssue issue = new DefaultIssue();
-//
-// @Test
-// public void test_setters_and_getters() throws Exception {
-// issue.setKey("ABCD")
-// .setComponentKey("org.sample.Sample")
-// .setProjectKey("Sample")
-// .setRuleKey(RuleKey.of("squid", "S100"))
-// .setLanguage("xoo")
-// .setSeverity("MINOR")
-// .setManualSeverity(true)
-// .setMessage("a message")
-// .setLine(7)
-// .setEffortToFix(1.2d)
-// .setDebt(Duration.create(28800L))
-// .setActionPlanKey("BCDE")
-// .setStatus(Issue.STATUS_CLOSED)
-// .setResolution(Issue.RESOLUTION_FIXED)
-// .setReporter("simon")
-// .setAssignee("julien")
-// .setAuthorLogin("steph")
-// .setChecksum("c7b5db46591806455cf082bb348631e8")
-// .setNew(true)
-// .setBeingClosed(true)
-// .setOnDisabledRule(true)
-// .setChanged(true)
-// .setSendNotifications(true)
-// .setCreationDate(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-19"))
-// .setUpdateDate(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-20"))
-// .setCloseDate(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-21"))
-// .setSelectedAt(1400000000000L);
-//
-// assertThat(issue.key()).isEqualTo("ABCD");
-// assertThat(issue.componentKey()).isEqualTo("org.sample.Sample");
-// assertThat(issue.projectKey()).isEqualTo("Sample");
-// assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("squid", "S100"));
-// assertThat(issue.language()).isEqualTo("xoo");
-// assertThat(issue.severity()).isEqualTo("MINOR");
-// assertThat(issue.manualSeverity()).isTrue();
-// assertThat(issue.message()).isEqualTo("a message");
-// assertThat(issue.line()).isEqualTo(7);
-// assertThat(issue.effortToFix()).isEqualTo(1.2d);
-// assertThat(issue.debt()).isEqualTo(Duration.create(28800L));
-// assertThat(issue.actionPlanKey()).isEqualTo("BCDE");
-// assertThat(issue.status()).isEqualTo(Issue.STATUS_CLOSED);
-// assertThat(issue.resolution()).isEqualTo(Issue.RESOLUTION_FIXED);
-// assertThat(issue.reporter()).isEqualTo("simon");
-// assertThat(issue.assignee()).isEqualTo("julien");
-// assertThat(issue.authorLogin()).isEqualTo("steph");
-// assertThat(issue.checksum()).isEqualTo("c7b5db46591806455cf082bb348631e8");
-// assertThat(issue.isNew()).isTrue();
-// assertThat(issue.isBeingClosed()).isTrue();
-// assertThat(issue.isOnDisabledRule()).isTrue();
-// assertThat(issue.isChanged()).isTrue();
-// assertThat(issue.mustSendNotifications()).isTrue();
-// assertThat(issue.creationDate()).isEqualTo(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-19"));
-// assertThat(issue.updateDate()).isEqualTo(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-20"));
-// assertThat(issue.closeDate()).isEqualTo(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-21"));
-// assertThat(issue.selectedAt()).isEqualTo(1400000000000L);
-// }
-//
-// @Test
-// public void set_empty_dates() {
-// issue
-// .setCreationDate(null)
-// .setUpdateDate(null)
-// .setCloseDate(null)
-// .setSelectedAt(null);
-//
-// assertThat(issue.creationDate()).isNull();
-// assertThat(issue.updateDate()).isNull();
-// assertThat(issue.closeDate()).isNull();
-// assertThat(issue.selectedAt()).isNull();
-// }
-//
-// @Test
-// public void test_attributes() throws Exception {
-// assertThat(issue.attribute("foo")).isNull();
-// issue.setAttribute("foo", "bar");
-// assertThat(issue.attribute("foo")).isEqualTo("bar");
-// issue.setAttribute("foo", "newbar");
-// assertThat(issue.attribute("foo")).isEqualTo("newbar");
-// issue.setAttribute("foo", null);
-// assertThat(issue.attribute("foo")).isNull();
-// }
-//
-// @Test
-// public void setAttributes_should_not_clear_existing_values() {
-// issue.setAttributes(ImmutableMap.of("1", "one"));
-// assertThat(issue.attribute("1")).isEqualTo("one");
-//
-// issue.setAttributes(ImmutableMap.of("2", "two"));
-// assertThat(issue.attributes()).containsOnly(entry("1", "one"), entry("2", "two"));
-//
-// issue.setAttributes(null);
-// assertThat(issue.attributes()).containsOnly(entry("1", "one"), entry("2", "two"));
-// }
-//
-// @Test
-// public void fail_on_empty_status() {
-// try {
-// issue.setStatus("");
-// fail();
-// } catch (IllegalArgumentException e) {
-// assertThat(e).hasMessage("Status must be set");
-// }
-// }
-//
-// @Test
-// public void fail_on_bad_severity() {
-// try {
-// issue.setSeverity("FOO");
-// fail();
-// } catch (IllegalArgumentException e) {
-// assertThat(e).hasMessage("Not a valid severity: FOO");
-// }
-// }
-//
-// @Test
-// public void message_should_be_abbreviated_if_too_long() {
-// issue.setMessage(StringUtils.repeat("a", 5000));
-// assertThat(issue.message()).hasSize(4000);
-// }
-//
-// @Test
-// public void message_should_be_trimmed() {
-// issue.setMessage(" foo ");
-// assertThat(issue.message()).isEqualTo("foo");
-// }
-//
-// @Test
-// public void message_could_be_null() {
-// issue.setMessage(null);
-// assertThat(issue.message()).isNull();
-// }
-//
-// @Test
-// public void test_nullable_fields() throws Exception {
-// issue.setEffortToFix(null).setSeverity(null).setLine(null);
-// assertThat(issue.effortToFix()).isNull();
-// assertThat(issue.severity()).isNull();
-// assertThat(issue.line()).isNull();
-// }
-//
-// @Test
-// public void test_equals_and_hashCode() throws Exception {
-// DefaultIssue a1 = new DefaultIssue().setKey("AAA");
-// DefaultIssue a2 = new DefaultIssue().setKey("AAA");
-// DefaultIssue b = new DefaultIssue().setKey("BBB");
-// assertThat(a1).isEqualTo(a1);
-// assertThat(a1).isEqualTo(a2);
-// assertThat(a1).isNotEqualTo(b);
-// assertThat(a1.hashCode()).isEqualTo(a1.hashCode());
-// }
-//
-// @Test
-// public void comments_should_not_be_modifiable() {
-// DefaultIssue issue = new DefaultIssue().setKey("AAA");
-//
-// List<IssueComment> comments = issue.comments();
-// assertThat(comments).isEmpty();
-//
-// try {
-// comments.add(new DefaultIssueComment());
-// fail();
-// } catch (UnsupportedOperationException e) {
-// // ok
-// } catch (Exception e) {
-// fail("Unexpected exception: " + e);
-// }
-// }
-//
-// @Test
-// public void all_changes_contain_current_change() {
-// IssueChangeContext issueChangeContext = mock(IssueChangeContext.class);
-// DefaultIssue issue = new DefaultIssue().setKey("AAA").setFieldChange(issueChangeContext, "actionPlan", "1.0", "1.1");
-//
-// assertThat(issue.changes()).hasSize(1);
-// }
-//}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/issue/internal/FieldDiffsTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/issue/internal/FieldDiffsTest.java
deleted file mode 100644
index 031b3e9dcb7..00000000000
--- a/sonar-plugin-api/src/test/java/org/sonar/api/issue/internal/FieldDiffsTest.java
+++ /dev/null
@@ -1,168 +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.
- */
-///*
-// * 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.api.issue.internal;
-//
-//import org.junit.Test;
-//
-//import static org.assertj.core.api.Assertions.assertThat;
-//
-//public class FieldDiffsTest {
-//
-// FieldDiffs diffs = new FieldDiffs();
-//
-// @Test
-// public void diffs_should_be_empty_by_default() {
-// assertThat(diffs.diffs()).isEmpty();
-// }
-//
-// @Test
-// public void test_diff() throws Exception {
-// diffs.setDiff("severity", "BLOCKER", "INFO");
-// diffs.setDiff("resolution", "OPEN", "FIXED");
-//
-// assertThat(diffs.diffs()).hasSize(2);
-//
-// FieldDiffs.Diff diff = diffs.diffs().get("severity");
-// assertThat(diff.oldValue()).isEqualTo("BLOCKER");
-// assertThat(diff.newValue()).isEqualTo("INFO");
-//
-// diff = diffs.diffs().get("resolution");
-// assertThat(diff.oldValue()).isEqualTo("OPEN");
-// assertThat(diff.newValue()).isEqualTo("FIXED");
-// }
-//
-// @Test
-// public void diff_with_long_values() {
-// diffs.setDiff("technicalDebt", 50l, "100");
-//
-// FieldDiffs.Diff diff = diffs.diffs().get("technicalDebt");
-// assertThat(diff.oldValueLong()).isEqualTo(50l);
-// assertThat(diff.newValueLong()).isEqualTo(100l);
-// }
-//
-// @Test
-// public void diff_with_empty_long_values() {
-// diffs.setDiff("technicalDebt", null, "");
-//
-// FieldDiffs.Diff diff = diffs.diffs().get("technicalDebt");
-// assertThat(diff.oldValueLong()).isNull();
-// assertThat(diff.newValueLong()).isNull();
-// }
-//
-// @Test
-// public void test_diff_by_key() throws Exception {
-// diffs.setDiff("severity", "BLOCKER", "INFO");
-// diffs.setDiff("resolution", "OPEN", "FIXED");
-//
-// assertThat(diffs.diffs()).hasSize(2);
-//
-// FieldDiffs.Diff diff = diffs.diffs().get("severity");
-// assertThat(diff.oldValue()).isEqualTo("BLOCKER");
-// assertThat(diff.newValue()).isEqualTo("INFO");
-//
-// diff = diffs.diffs().get("resolution");
-// assertThat(diff.oldValue()).isEqualTo("OPEN");
-// assertThat(diff.newValue()).isEqualTo("FIXED");
-// }
-//
-// @Test
-// public void should_keep_old_value() {
-// diffs.setDiff("severity", "BLOCKER", "INFO");
-// diffs.setDiff("severity", null, "MAJOR");
-// FieldDiffs.Diff diff = diffs.diffs().get("severity");
-// assertThat(diff.oldValue()).isEqualTo("BLOCKER");
-// assertThat(diff.newValue()).isEqualTo("MAJOR");
-// }
-//
-// @Test
-// public void test_toString() throws Exception {
-// diffs.setDiff("severity", "BLOCKER", "INFO");
-// diffs.setDiff("resolution", "OPEN", "FIXED");
-//
-// assertThat(diffs.toString()).isEqualTo("severity=BLOCKER|INFO,resolution=OPEN|FIXED");
-// }
-//
-// @Test
-// public void test_toString_with_null_values() throws Exception {
-// diffs.setDiff("severity", null, "INFO");
-// diffs.setDiff("assignee", "user1", null);
-//
-// assertThat(diffs.toString()).isEqualTo("severity=INFO,assignee=");
-// }
-//
-// @Test
-// public void test_parse() throws Exception {
-// diffs = FieldDiffs.parse("severity=BLOCKER|INFO,resolution=OPEN|FIXED");
-// assertThat(diffs.diffs()).hasSize(2);
-//
-// FieldDiffs.Diff diff = diffs.diffs().get("severity");
-// assertThat(diff.oldValue()).isEqualTo("BLOCKER");
-// assertThat(diff.newValue()).isEqualTo("INFO");
-//
-// diff = diffs.diffs().get("resolution");
-// assertThat(diff.oldValue()).isEqualTo("OPEN");
-// assertThat(diff.newValue()).isEqualTo("FIXED");
-// }
-//
-// @Test
-// public void test_parse_empty_values() throws Exception {
-// diffs = FieldDiffs.parse("severity=INFO,resolution=");
-// assertThat(diffs.diffs()).hasSize(2);
-//
-// FieldDiffs.Diff diff = diffs.diffs().get("severity");
-// assertThat(diff.oldValue()).isEqualTo("");
-// assertThat(diff.newValue()).isEqualTo("INFO");
-//
-// diff = diffs.diffs().get("resolution");
-// assertThat(diff.oldValue()).isEqualTo("");
-// assertThat(diff.newValue()).isEqualTo("");
-// }
-//
-// @Test
-// public void test_parse_null() throws Exception {
-// diffs = FieldDiffs.parse(null);
-// assertThat(diffs.diffs()).isEmpty();
-// }
-//
-// @Test
-// public void test_parse_empty() throws Exception {
-// diffs = FieldDiffs.parse("");
-// assertThat(diffs.diffs()).isEmpty();
-// }
-//}