From 1c0b9a97e3bc6d58c4697b4350b39adc81a6efc1 Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Tue, 30 Jun 2015 09:02:14 +0200 Subject: [PATCH] SONAR-6588 move computation of debt to Compute Engine --- .../container/ComputeEngineContainerImpl.java | 10 +- .../computation/debt/DebtModelHolder.java | 3 + .../computation/debt/DebtModelHolderImpl.java | 17 +- .../debt/MutableDebtModelHolder.java | 2 +- .../computation/issue/BaseIssuesLoader.java | 28 +- .../computation/issue/DebtAggregator.java | 51 +- .../computation/issue/DebtCalculator.java | 69 +++ .../computation/issue/DefaultAssignee.java | 2 +- .../server/computation/issue/IssueCache.java | 1 - .../computation/issue/IssueCounter.java | 22 +- .../computation/issue/IssueLifecycle.java | 22 +- .../computation/issue/IssueVisitor.java | 5 +- .../computation/issue/NewDebtAggregator.java | 18 +- .../computation/issue/NewDebtCalculator.java | 24 +- .../sonar/server/computation/issue/Rule.java | 53 +++ .../computation/issue/RuleCacheLoader.java | 26 +- .../server/computation/issue/RuleImpl.java | 155 ++++++ .../computation/issue/RuleRepository.java | 10 +- ...RuleCache.java => RuleRepositoryImpl.java} | 19 +- .../computation/issue/RuleTagsCopier.java | 13 +- .../issue/ScmAccountToUserLoader.java | 34 +- .../issue/TrackerRawInputFactory.java | 32 +- .../computation/period/PeriodsHolderImpl.java | 2 +- .../computation/step/ComputationSteps.java | 4 +- .../computation/step/IntegrateIssuesStep.java | 24 +- .../computation/step/PersistIssuesStep.java | 17 +- .../step/SendIssueNotificationsStep.java | 11 +- .../sonar/server/issue/IssueQueryService.java | 8 +- .../debt/DebtModelHolderImplTest.java | 209 ++++----- .../computation/issue/DebtAggregatorTest.java | 155 ++++++ .../computation/issue/DebtCalculatorTest.java | 98 ++++ .../issue/DefaultAssigneeTest.java | 73 +++ .../server/computation/issue/DumbRule.java | 118 +++++ .../computation/issue/IssueAssignerTest.java | 124 +++++ ...istenerTest.java => IssueCounterTest.java} | 10 +- .../issue/NewDebtAggregatorTest.java | 443 ++++-------------- .../issue/NewDebtCalculatorTest.java | 233 +++++---- .../issue/RuleCacheLoaderTest.java | 33 +- ...eTest.java => RuleRepositoryImplTest.java} | 24 +- .../computation/issue/RuleRepositoryRule.java | 59 +++ .../computation/issue/RuleTagsCopierTest.java | 72 +++ .../issue/ScmAccountToUserLoaderTest.java | 13 +- .../issue/SourceAuthorsHolderTest.java | 137 ------ .../measure/MeasureRepositoryRule.java | 8 + .../step/FeedDebtModelStepTest.java | 153 +++--- .../step/PersistIssuesStepTest.java | 12 +- .../step/PersistMeasuresStepTest.java | 143 +----- .../step/SendIssueNotificationsStepTest.java | 6 +- .../issue/RuleCacheLoaderTest/shared.xml | 4 +- .../insert_new_issue.xml | 2 +- .../batch/protocol/output/BatchReport.java | 193 ++------ .../protocol/output/BatchReportReader.java | 1 - .../src/main/protobuf/batch_report.proto | 5 +- .../org/sonar/batch/issue/ModuleIssues.java | 29 -- .../org/sonar/batch/phases/PhaseExecutor.java | 2 +- .../sonar/batch/report/IssuesPublisher.java | 5 - .../batch/scan/measure/MeasureValueCoder.java | 1 - .../sonar/batch/issue/ModuleIssuesTest.java | 109 +---- .../batch/report/IssuesPublisherTest.java | 2 - .../sonar/core/issue/db/IssueChangeDao.java | 4 +- .../core/issue/db/IssueChangeMapper.java | 2 +- .../core/issue/db/UpdateConflictResolver.java | 2 +- .../issue/tracking/BlockHashSequence.java | 5 + .../core/issue/tracking/BlockRecognizer.java | 20 +- .../sonar/core/issue/tracking/Tracker.java | 65 ++- .../sonar/core/issue/tracking/Tracking.java | 3 +- .../core/issue/workflow/IssueWorkflow.java | 4 +- .../sonar/core/issue/workflow/SetClosed.java | 3 - .../org/sonar/core/measure/db/MeasureDto.java | 26 + .../org/sonar/core/rule/RuleKeyFunctions.java | 49 ++ .../sonar/core/issue/db/IssueChangeMapper.xml | 4 +- .../sonar/core/issue/DefaultIssueTest.java | 219 +++++++++ .../org/sonar/core/issue/FieldDiffsTest.java | 149 ++++++ .../core/issue/db/IssueChangeDaoTest.java | 8 +- .../core/issue/tracking/TrackerTest.java | 76 ++- .../issue/workflow/IssueWorkflowTest.java | 5 +- .../core/issue/workflow/SetClosedTest.java | 20 +- .../sonar/core/rule/RuleKeyFunctionsTest.java | 46 ++ ...ChangelogOfNonClosedIssuesByComponent.xml} | 52 +- .../java/org/sonar/api/batch/rule/Rule.java | 12 +- .../api/batch/rule/internal/DefaultRule.java | 9 +- .../api/batch/rule/internal/NewRule.java | 15 +- .../condition/HasIssuePropertyCondition.java | 2 +- .../java/org/sonar/api/measures/Measure.java | 26 +- .../sonar/api/measures/MeasuresFilters.java | 2 +- .../main/java/org/sonar/api/rule/RuleKey.java | 2 + .../server/debt/DebtRemediationFunction.java | 18 +- .../batch/rule/internal/RulesBuilderTest.java | 5 - .../sonar/api/issue/action/ActionTest.java | 168 +++---- .../HasIssuePropertyConditionTest.java | 113 ++--- .../issue/condition/HasResolutionTest.java | 68 ++- .../api/issue/condition/HasStatusTest.java | 78 ++- .../api/issue/condition/IsUnResolvedTest.java | 62 +-- .../api/issue/condition/NotConditionTest.java | 72 ++- .../api/issue/internal/DefaultIssueTest.java | 238 ---------- .../api/issue/internal/FieldDiffsTest.java | 168 ------- 96 files changed, 2624 insertions(+), 2354 deletions(-) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/issue/DebtCalculator.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/issue/Rule.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/issue/RuleImpl.java rename sonar-core/src/main/java/org/sonar/core/computation/package-info.java => server/sonar-server/src/main/java/org/sonar/server/computation/issue/RuleRepository.java (85%) rename server/sonar-server/src/main/java/org/sonar/server/computation/issue/{RuleCache.java => RuleRepositoryImpl.java} (71%) create mode 100644 server/sonar-server/src/test/java/org/sonar/server/computation/issue/DebtAggregatorTest.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/computation/issue/DebtCalculatorTest.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/computation/issue/DefaultAssigneeTest.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/computation/issue/DumbRule.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueAssignerTest.java rename server/sonar-server/src/test/java/org/sonar/server/computation/issue/{CountIssuesListenerTest.java => IssueCounterTest.java} (96%) rename server/sonar-server/src/test/java/org/sonar/server/computation/issue/{RuleCacheTest.java => RuleRepositoryImplTest.java} (61%) create mode 100644 server/sonar-server/src/test/java/org/sonar/server/computation/issue/RuleRepositoryRule.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/computation/issue/RuleTagsCopierTest.java delete mode 100644 server/sonar-server/src/test/java/org/sonar/server/computation/issue/SourceAuthorsHolderTest.java create mode 100644 sonar-core/src/main/java/org/sonar/core/rule/RuleKeyFunctions.java create mode 100644 sonar-core/src/test/java/org/sonar/core/issue/DefaultIssueTest.java create mode 100644 sonar-core/src/test/java/org/sonar/core/issue/FieldDiffsTest.java create mode 100644 sonar-core/src/test/java/org/sonar/core/rule/RuleKeyFunctionsTest.java rename sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/{selectChangelogOfUnresolvedIssuesByComponent.xml => selectChangelogOfNonClosedIssuesByComponent.xml} (77%) delete mode 100644 sonar-plugin-api/src/test/java/org/sonar/api/issue/internal/DefaultIssueTest.java delete mode 100644 sonar-plugin-api/src/test/java/org/sonar/api/issue/internal/FieldDiffsTest.java 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 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 rootCharacteristics = new ArrayList<>(); private final Map characteristicById = new HashMap<>(); @Override - public void addCharacteristics(Characteristic rootCharacteristic, Iterable subCharacteristics) { + public DebtModelHolderImpl addCharacteristics(Characteristic rootCharacteristic, Iterable 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 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 subCharacteristics); + MutableDebtModelHolder addCharacteristics(Characteristic rootCharacteristic, Iterable 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 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 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 loadComponentUuids() { + public Set 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 { - 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 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 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 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 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 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 { 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 SEVERITY_TO_METRIC_KEY = ImmutableMap.of( + private static final Map 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 SEVERITY_TO_NEW_METRIC_KEY = ImmutableMap.of( + private static final Map 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 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 : + *
    + *
  • newly created
  • + *
  • merged the related base issue
  • + *
  • relocated (only manual issues)
  • + *
+ */ 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 changes = dbClient.issueChangeDao().selectChangelogOfUnresolvedIssuesByComponent(component.getUuid()); + List 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 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 debtChangelog, Period period) { + public long calculate(DefaultIssue issue, Collection 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 debtChangelog, long periodDate) { + private long calculateFromChangelog(DefaultIssue issue, Collection debtChangelog, long periodDate) { List 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 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 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 { +public class RuleCacheLoader implements CacheLoader { private final DbClient dbClient; + private final Set 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 loadAll(Collection keys) { - throw new UnsupportedOperationException("See RuleDao"); + public Map loadAll(Collection 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 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 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 similarity index 85% rename from sonar-core/src/main/java/org/sonar/core/computation/package-info.java rename to 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 similarity index 71% rename from server/sonar-server/src/main/java/org/sonar/server/computation/issue/RuleCache.java rename to 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 { + private final MemoryCache 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 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 { - 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 { if (!users.isEmpty()) { // multiple users are associated to the same SCM account, for example // the same email - Collection logins = Collections2.transform(users, new Function() { - @Override - public String apply(UserDoc input) { - return input.login(); - } - }); + Collection 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 { public Map loadAll(Collection scmAccounts) { throw new UnsupportedOperationException("Loading by multiple scm accounts is not supported yet"); } + + private enum UserDocToLogin implements Function { + 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 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 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 unprocessedComponentUuids = Sets.newHashSet(baseIssuesLoader.loadComponentUuids()); + final Set 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 tracking = tracker.track(component); - Loggers.get(getClass()).info("----- tracking ------"); - Loggers.get(getClass()).info("" + tracking); DiskCache.DiskAppender cacheAppender = issueCache.newAppender(); try { issueVisitors.beforeComponent(component, tracking); @@ -92,44 +90,35 @@ public class IntegrateIssuesStep implements ComputationStep { private void fillNewOpenIssues(Component component, Tracking tracking, DiskCache.DiskAppender cacheAppender) { Set 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 tracking, DiskCache.DiskAppender cacheAppender) { - Loggers.get(getClass()).info("----- fillExistingOpenIssues on " + component.getKey()); for (Map.Entry 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 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 tracking, DiskCache.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.DiskAppender cacheAppender) { @@ -145,10 +134,9 @@ public class IntegrateIssuesStep implements ComputationStep { List 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 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 stringsToRules(@Nullable Collection rules) { if (rules != null) { - return newArrayList(Iterables.transform(rules, new Function() { - @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.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.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 debtMeasure(Component component) { + return measureRepository.getRawMeasure(component, metricRepository.getByKey(CoreMetrics.TECHNICAL_DEBT_KEY)); + } + + @CheckForNull + private Optional debtRuleMeasure(Component component, int ruleId) { + return measureRepository.getRawRuleMeasure(component, metricRepository.getByKey(CoreMetrics.TECHNICAL_DEBT_KEY), ruleId); + } + + @CheckForNull + private Optional 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 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 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 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 similarity index 96% rename from server/sonar-server/src/test/java/org/sonar/server/computation/issue/CountIssuesListenerTest.java rename to 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 { -// 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 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.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 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 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 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.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 similarity index 61% rename from server/sonar-server/src/test/java/org/sonar/server/computation/issue/RuleCacheTest.java rename to 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 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.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 getRawRuleMeasure(Component component, Metric metric, int ruleId) { + return Optional.fromNullable(rawMeasures.get(new InternalKey(component, metric, ruleId, null))); + } + @Override public Optional getRawMeasure(Component component, Metric metric, Characteristic characteristic) { return Optional.fromNullable(rawMeasures.get(new InternalKey(component, metric, null, characteristic.getId()))); } + public Optional getRawCharacteristicMeasure(Component component, Metric metric, int characteristicId) { + return Optional.fromNullable(rawMeasures.get(new InternalKey(component, metric, null, characteristicId))); + } + @Override public SetMultimap getRawMeasures(Component component) { ImmutableSetMultimap.Builder 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 rootChars = debtModelHolder.findRootCharacteristics(); -// assertThat(rootChars).extracting("id").containsOnly(1); -// assertThat(rootChars).extracting("key").containsOnly("PORTABILITY"); -// -// Collection 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 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> dtos = retrieveDtos(); Map 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> dtos = retrieveDtos(); - - Map 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> dtos = retrieveDtos(); - - Map 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 @@ - - + * TODO to be removed. It prevents streaming + * */ public static final class Measures extends com.google.protobuf.GeneratedMessage implements @@ -6040,6 +6044,10 @@ public final class BatchReport { } /** * Protobuf type {@code Measures} + * + *
+     * TODO to be removed. It prevents streaming 
+     * 
*/ public static final class Builder extends com.google.protobuf.GeneratedMessage.Builder implements @@ -6587,23 +6595,6 @@ public final class BatchReport { */ com.google.protobuf.ByteString getAttributesBytes(); - - /** - * optional int64 debt_in_minutes = 9; - * - *
-     * TODO should it be moved to compute engine?
-     * 
- */ - boolean hasDebtInMinutes(); - /** - * optional int64 debt_in_minutes = 9; - * - *
-     * TODO should it be moved to compute engine?
-     * 
- */ - 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_; - /** - * optional int64 debt_in_minutes = 9; - * - *
-     * TODO should it be moved to compute engine?
-     * 
- */ - public boolean hasDebtInMinutes() { - return ((bitField0_ & 0x00000080) == 0x00000080); - } - /** - * optional int64 debt_in_minutes = 9; - * - *
-     * TODO should it be moved to compute engine?
-     * 
- */ - 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_ ; - /** - * optional int64 debt_in_minutes = 9; - * - *
-       * TODO should it be moved to compute engine?
-       * 
- */ - public boolean hasDebtInMinutes() { - return ((bitField0_ & 0x00000100) == 0x00000100); - } - /** - * optional int64 debt_in_minutes = 9; - * - *
-       * TODO should it be moved to compute engine?
-       * 
- */ - public long getDebtInMinutes() { - return debtInMinutes_; - } - /** - * optional int64 debt_in_minutes = 9; - * - *
-       * TODO should it be moved to compute engine?
-       * 
- */ - public Builder setDebtInMinutes(long value) { - bitField0_ |= 0x00000100; - debtInMinutes_ = value; - onChanged(); - return this; - } - /** - * optional int64 debt_in_minutes = 9; - * - *
-       * TODO should it be moved to compute engine?
-       * 
- */ - 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} + * + *
+   * TODO to be removed. It prevents streaming 
+   * 
*/ public static final class Issues extends com.google.protobuf.GeneratedMessage implements @@ -8280,6 +8182,10 @@ public final class BatchReport { } /** * Protobuf type {@code Issues} + * + *
+     * TODO to be removed. It prevents streaming 
+     * 
*/ public static final class Builder extends com.google.protobuf.GeneratedMessage.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 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 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 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 selectChangelogOfUnresolvedIssuesByComponent(String componentUuid) { + public List 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 selectByIssuesAndType(@Param("issueKeys") List issueKeys, @Param("changeType") String changeType); - List selectChangelogOfUnresolvedIssuesByComponent(@Param("componentUuid") String componentUuid, @Param("changeType") String changeType); + List 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 { * Only the issues associated to a line can be matched here. */ void match(Input rawInput, Input baseInput, Tracking tracking) { - BlockHashSequence baseHashSequence = baseInput.getBlockHashSequence(); BlockHashSequence rawHashSequence = rawInput.getBlockHashSequence(); + BlockHashSequence baseHashSequence = baseInput.getBlockHashSequence(); - Multimap rawsByLine = groupByLine(tracking.getUnmatchedRaws()); - Multimap basesByLine = groupByLine(tracking.getUnmatchedBases()); - Map map = new HashMap<>(); + Multimap rawsByLine = groupByLine(tracking.getUnmatchedRaws(), rawHashSequence); + Multimap basesByLine = groupByLine(tracking.getUnmatchedBases(), baseHashSequence); + Map 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 { 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 { } } - private static Multimap groupByLine(Collection trackables) { + private static Multimap groupByLine(Collection trackables, BlockHashSequence hashSequence) { Multimap 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 { public Tracking track(Input rawInput, Input baseInput) { Tracking 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 { // 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 { tracking.markRawsAsAssociated(trackedRaws); } - private void relocateManualIssues(Input rawInput, Tracking tracking) { - Iterable manualIssues = from(tracking.getUnmatchedBases()).filter(IsManual.INSTANCE); + private void relocateManualIssues(Input rawInput, Input baseInput, Tracking tracking) { + // FIXME copy of Set if required to avoid concurrent modifications (see tracking.associateManualIssueToLine()) + Iterable 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 { } 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 { 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 { } 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 { } 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 { } 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 { @@ -112,7 +113,7 @@ public class Tracking { 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 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 { + 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 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 <> 'CLOSED' 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 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 dtos = dao.selectChangelogOfUnresolvedIssuesByComponent("FILE_1"); - assertThat(dtos).extracting("id").containsExactly(100L); + List 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(); @@ -59,20 +62,6 @@ public class TrackerTest { assertThat(tracking.baseFor(raw)).isNull(); } - @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 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"); @@ -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 tracking = tracker.track(rawInput, baseInput); + + assertThat(tracking.getUnmatchedBases()).isEmpty(); + Multimap 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 tracking = tracker.track(rawInput, baseInput); + + assertThat(tracking.getUnmatchedBases()).isEmpty(); + Multimap 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 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 tracking = tracker.track(rawInput, baseInput); + + assertThat(tracking.getUnmatchedBases()).isEmpty(); + Multimap 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 { @@ -48,19 +49,6 @@ public class SetClosedTest { verify(context, times(1)).setResolution(Issue.RESOLUTION_REMOVED); } - @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); 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 strings = Arrays.asList("js:S001", "java:S002"); + List 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 similarity index 77% rename from sonar-core/src/test/resources/org/sonar/core/issue/db/IssueChangeDaoTest/selectChangelogOfUnresolvedIssuesByComponent.xml rename to 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 @@ - + - + + + + + + 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 builder = ImmutableMap.builder(); for (NewRuleParam newRuleParam : newRule.params.values()) { @@ -97,9 +95,14 @@ public class DefaultRule implements Rule { return status; } + @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 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 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 implements Serializable { */ @CheckForNull public final Characteristic getCharacteristic() { - return null; + return characteristic; } /** * @since 4.1 */ public final Measure setCharacteristic(@Nullable Characteristic characteristic) { + this.characteristic = characteristic; return this; } @@ -644,7 +649,7 @@ public class Measure implements Serializable { @Deprecated @CheckForNull public final Requirement getRequirement() { - return null; + return requirement; } /** @@ -653,6 +658,7 @@ public class Measure implements Serializable { */ @Deprecated public final Measure setRequirement(@Nullable Requirement requirement) { + this.requirement = requirement; return this; } @@ -731,12 +737,16 @@ public class Measure 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 extends MetricFilter { 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 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(); -// } -//} -- 2.39.5