From: Simon Brandhof Date: Wed, 24 Jun 2015 07:11:30 +0000 (+0200) Subject: SONAR-6623 distribution of issues by debt in Compute Engine X-Git-Tag: 5.2-RC1~1235^2~2 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=534bda208505d82734a41f44791ccd50ea928432;p=sonarqube.git SONAR-6623 distribution of issues by debt in Compute Engine This allows to deprecate org.sonar.api.measures.RuleMeasure --- diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/debt/Characteristic.java b/server/sonar-server/src/main/java/org/sonar/server/computation/debt/Characteristic.java index 5c1224c8df9..0606b2533ee 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/debt/Characteristic.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/debt/Characteristic.java @@ -20,6 +20,7 @@ package org.sonar.server.computation.debt; +import javax.annotation.CheckForNull; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; @@ -29,11 +30,13 @@ import static java.util.Objects.requireNonNull; public class Characteristic { private final int id; private final String key; + private final Integer parentId; - public Characteristic(int id, String key) { + public Characteristic(int id, String key, @Nullable Integer parentId) { requireNonNull(key, "key cannot be null"); this.id = id; this.key = key; + this.parentId = parentId; } public int getId() { @@ -44,6 +47,11 @@ public class Characteristic { return key; } + @CheckForNull + public Integer getParentId() { + return parentId; + } + @Override public boolean equals(@Nullable Object o) { if (this == o) { @@ -66,10 +74,11 @@ public class Characteristic { @Override public String toString() { - return "Characteristic{" + - "id=" + id + - ", key='" + key + '\'' + - '}'; + StringBuilder sb = new StringBuilder("Characteristic{"); + sb.append("id=").append(id); + sb.append(", key='").append(key).append('\''); + sb.append(", parentId=").append(parentId); + sb.append('}'); + return sb.toString(); } - } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/debt/DebtModelHolder.java b/server/sonar-server/src/main/java/org/sonar/server/computation/debt/DebtModelHolder.java index c79b7cb2860..f10dbfe5af9 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/debt/DebtModelHolder.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/debt/DebtModelHolder.java @@ -20,30 +20,14 @@ package org.sonar.server.computation.debt; -import com.google.common.base.Optional; -import java.util.Set; - public interface DebtModelHolder { /** - * Return a characteristic by its key - * - * @throws IllegalStateException if the holder is empty - */ - Optional getCharacteristicByKey(String key); - - /** - * Return the set of root characteristics - * - * @throws IllegalStateException if the holder is empty - */ - Set findRootCharacteristics(); - - /** - * Return the collection of sub characteristics from a root characteristic key + * Return a characteristic by its id * - * @throws IllegalStateException if the holder is empty + * @throws IllegalStateException if no Characteristic with the specified id is found + * @throws IllegalStateException if the holder is not initialized yet */ - Set findSubCharacteristicsByRootKey(String rootCharacteristicKey); + Characteristic getCharacteristicById(int id); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/debt/DebtModelHolderImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/debt/DebtModelHolderImpl.java index 6d0e0d1fbf1..206ed5f3a8b 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/debt/DebtModelHolderImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/debt/DebtModelHolderImpl.java @@ -20,61 +20,40 @@ package org.sonar.server.computation.debt; -import com.google.common.base.Optional; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Multimap; -import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.Set; -import static com.google.common.base.Optional.fromNullable; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; public class DebtModelHolderImpl implements MutableDebtModelHolder { - private final Multimap subCharacteristicsByRootCharacteristic = ArrayListMultimap.create(); - private final Map characteristicByKey = new HashMap<>(); + private final Map characteristicById = new HashMap<>(); @Override public void 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"); - subCharacteristicsByRootCharacteristic.putAll(rootCharacteristic, subCharacteristics); - characteristicByKey.put(rootCharacteristic.getKey(), rootCharacteristic); + characteristicById.put(rootCharacteristic.getId(), rootCharacteristic); for (Characteristic characteristic : subCharacteristics) { - characteristicByKey.put(characteristic.getKey(), characteristic); + characteristicById.put(characteristic.getId(), characteristic); } } @Override - public Set findRootCharacteristics() { + public Characteristic getCharacteristicById(int id) { checkCharacteristicsAreInitialized(); - return ImmutableSet.copyOf(subCharacteristicsByRootCharacteristic.keySet()); - } - - @Override - public Set findSubCharacteristicsByRootKey(String rootCharacteristicKey) { - checkCharacteristicsAreInitialized(); - Characteristic rootCharacteristic = characteristicByKey.get(rootCharacteristicKey); - if (rootCharacteristic == null) { - return Collections.emptySet(); + Characteristic characteristic = characteristicById.get(id); + if (characteristic == null) { + throw new IllegalStateException("Debt characteristic with id [" + id + "] does not exist"); } - return ImmutableSet.copyOf(subCharacteristicsByRootCharacteristic.get(rootCharacteristic)); - } - - @Override - public Optional getCharacteristicByKey(String key) { - checkCharacteristicsAreInitialized(); - return fromNullable(characteristicByKey.get(key)); + return characteristic; } private void checkCharacteristicsAreInitialized() { - checkState(!characteristicByKey.isEmpty(), "Characteristics have not been initialized yet"); + checkState(!characteristicById.isEmpty(), "Characteristics have not been initialized yet"); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/CountIssuesListener.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/CountIssuesListener.java index ff6034f8a08..1b49303f7a1 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/CountIssuesListener.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/CountIssuesListener.java @@ -110,6 +110,13 @@ public class CountIssuesListener extends IssueListener { // TODO optimization no need to instantiate counter if no open issues currentCounters = new Counters(); countersByComponentRef.put(component.getRef(), currentCounters); + + // aggregate children counters + for (Component child : component.getChildren()) { + // no need to keep the children in memory. They can be garbage-collected. + Counters childCounters = countersByComponentRef.remove(child.getRef()); + currentCounters.add(childCounters); + } } @Override @@ -125,16 +132,10 @@ public class CountIssuesListener extends IssueListener { @Override public void afterComponent(Component component) { - // aggregate children counters - for (Component child : component.getChildren()) { - // no need to keep the children in memory. They can be garbage-collected. - Counters childCounters = countersByComponentRef.remove(child.getRef()); - currentCounters.add(childCounters); - } - addMeasuresByStatus(component); addMeasuresBySeverity(component); addMeasuresByPeriod(component); + currentCounters = null; } private void addMeasuresBySeverity(Component component) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/DebtCalculator.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/DebtCalculator.java index afe30d47c87..b5ee6bb13ef 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/DebtCalculator.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/DebtCalculator.java @@ -19,15 +19,143 @@ */ package org.sonar.server.computation.issue; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import org.sonar.api.measures.CoreMetrics; import org.sonar.core.issue.DefaultIssue; +import org.sonar.core.issue.IssueUpdater; +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; +import org.sonar.server.computation.measure.Measure; +import org.sonar.server.computation.measure.MeasureRepository; +import org.sonar.server.computation.metric.Metric; +import org.sonar.server.computation.metric.MetricRepository; + +import static com.google.common.collect.Maps.newHashMap; public class DebtCalculator extends IssueListener { + private final IssueUpdater updater; + private final RuleCache ruleCache; + private final DebtModelHolder debtModelHolder; + private final MetricRepository metricRepository; + private final MeasureRepository measureRepository; + + private final Map debtsByComponentRef = new HashMap<>(); + private Debt currentDebt; + + public DebtCalculator(IssueUpdater updater, RuleCache ruleCache, DebtModelHolder debtModelHolder, + MetricRepository metricRepository, MeasureRepository measureRepository) { + this.updater = updater; + this.ruleCache = ruleCache; + this.debtModelHolder = debtModelHolder; + this.metricRepository = metricRepository; + this.measureRepository = measureRepository; + } + + @Override + public void beforeComponent(Component component, Tracking tracking) { + this.currentDebt = new Debt(); + + // aggregate children counters + for (Component child : component.getChildren()) { + // no need to keep the children in memory. They can be garbage-collected. + Debt childDebt = debtsByComponentRef.remove(child.getRef()); + currentDebt.add(childDebt); + } + } + @Override - public void onOpenIssueInitialization(Component component, DefaultIssue issue) { + public void onIssue(Component component, DefaultIssue issue) { if (issue.resolution() == null) { - // TODO + // TODO calculate debt according to rule remediation function. Currently done by batch. + currentDebt.add(issue); + } + } + + @Override + public void afterComponent(Component component) { + if (this.currentDebt.minutes > 0L) { + Metric metric = metricRepository.getByKey(CoreMetrics.TECHNICAL_DEBT_KEY); + + // total value + measureRepository.add(component, metric, Measure.newMeasureBuilder().create(this.currentDebt.minutes)); + + // distribution by rule + for (Map.Entry entry : currentDebt.minutesByRuleId.entrySet()) { + int ruleId = entry.getKey(); + long ruleDebt = entry.getValue(); + measureRepository.add(component, metric, Measure.newMeasureBuilder().forRule(ruleId).create(ruleDebt)); + } + + // 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)); + } + } + this.currentDebt = null; + } + + private class Debt { + private long minutes = 0L; + private final SumMap minutesByRuleId = new SumMap<>(); + private final SumMap minutesByCharacteristicId = new SumMap<>(); + + void add(DefaultIssue issue) { + Long issueMinutes = issue.debtInMinutes(); + if (issueMinutes != null && issueMinutes != 0L) { + this.minutes += issueMinutes; + + RuleDto rule = ruleCache.get(issue.ruleKey()); + this.minutesByRuleId.add(rule.getId(), issueMinutes); + + Characteristic characteristic = debtModelHolder.getCharacteristicById(rule.getSubCharacteristicId()); + this.minutesByCharacteristicId.add(characteristic.getId(), issueMinutes); + Integer characteristicParentId = characteristic.getParentId(); + if (characteristicParentId != null) { + this.minutesByCharacteristicId.add(characteristicParentId, issueMinutes); + } + } + } + + public void add(Debt debt) { + this.minutes += debt.minutes; + this.minutesByRuleId.add(debt.minutesByRuleId); + this.minutesByCharacteristicId.add(debt.minutesByCharacteristicId); + } + } + + private static class SumMap { + private final Map sumByKeys = newHashMap(); + + void add(SumMap other) { + for (Map.Entry entry : other.entrySet()) { + add(entry.getKey(), entry.getValue()); + } + } + + void add(@Nullable E key, Long value) { + if (key != null) { + Long currentValue = sumByKeys.get(key); + sumByKeys.put(key, currentValue != null ? (currentValue + value) : value); + } + } + + @CheckForNull + Long get(E key) { + return sumByKeys.get(key); + } + + Set> entrySet() { + return sumByKeys.entrySet(); } } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/step/FeedDebtModelStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/step/FeedDebtModelStep.java index ca491bc53c7..b03a1f9559e 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/step/FeedDebtModelStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/step/FeedDebtModelStep.java @@ -35,6 +35,9 @@ import org.sonar.server.computation.debt.Characteristic; import org.sonar.server.computation.debt.MutableDebtModelHolder; import org.sonar.server.db.DbClient; +import static com.google.common.base.Predicates.not; +import static com.google.common.collect.FluentIterable.from; + /** * Populates the {@link org.sonar.server.computation.debt.DebtModelHolder} */ @@ -60,23 +63,23 @@ public class FeedDebtModelStep implements ComputationStep { private void feedDebtModel(DbSession session) { List characteristicDtos = dbClient.debtCharacteristicDao().selectEnabledCharacteristics(session); - Map rootCharacteristicsById = FluentIterable.from(characteristicDtos) + Map rootCharacteristicsById = from(characteristicDtos) .filter(IsRootPredicate.INSTANCE) .uniqueIndex(CharacteristicDtoToId.INSTANCE); - for (Map.Entry> entry : FluentIterable.from(characteristicDtos) - .filter(Predicates.not(IsRootPredicate.INSTANCE)) + for (Map.Entry> entry : from(characteristicDtos) + .filter(not(IsRootPredicate.INSTANCE)) .index(CharacteristicDtoToParentId.INSTANCE) .asMap().entrySet()) { mutableDebtModelHolder.addCharacteristics( toCharacteristic(rootCharacteristicsById.get(entry.getKey())), - FluentIterable.from(entry.getValue()).transform(CharacteristicDtoToCharacteristic.INSTANCE) + from(entry.getValue()).transform(CharacteristicDtoToCharacteristic.INSTANCE) ); } } private static Characteristic toCharacteristic(CharacteristicDto dto) { - return new Characteristic(dto.getId(), dto.getKey()); + return new Characteristic(dto.getId(), dto.getKey(), dto.getParentId()); } private enum CharacteristicDtoToId implements Function { @@ -84,8 +87,8 @@ public class FeedDebtModelStep implements ComputationStep { @Nullable @Override - public Integer apply(@Nonnull CharacteristicDto characteristicDto) { - return characteristicDto.getId(); + public Integer apply(@Nonnull CharacteristicDto dto) { + return dto.getId(); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/debt/CharacteristicTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/debt/CharacteristicTest.java index 1499001ec1d..55a984af894 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/debt/CharacteristicTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/debt/CharacteristicTest.java @@ -33,14 +33,15 @@ public class CharacteristicTest { @Test public void test_getter_and_setter() throws Exception { - Characteristic characteristic = new Characteristic(1, "PORTABILITY"); + Characteristic characteristic = new Characteristic(1, "PORTABILITY", null); assertThat(characteristic.getId()).isEqualTo(1); assertThat(characteristic.getKey()).isEqualTo("PORTABILITY"); + assertThat(characteristic.getParentId()).isNull(); } @Test public void test_to_string() throws Exception { - assertThat(new Characteristic(1, "PORTABILITY").toString()).isEqualTo("Characteristic{id=1, key='PORTABILITY'}"); + assertThat(new Characteristic(1, "PORTABILITY", null).toString()).isEqualTo("Characteristic{id=1, key='PORTABILITY'}"); } @Test @@ -48,6 +49,6 @@ public class CharacteristicTest { thrown.expect(NullPointerException.class); thrown.expectMessage("key cannot be null"); - new Characteristic(1, null); + new Characteristic(1, null, null); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/debt/DebtModelHolderImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/debt/DebtModelHolderImplTest.java index 49917ee013f..df3a0e6be99 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 @@ -1,127 +1,126 @@ -/* - * 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; -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"); - private static final Characteristic COMPILER_RELATED_PORTABILITY = new Characteristic(2, "COMPILER_RELATED_PORTABILITY"); - private static final Characteristic HARDWARE_RELATED_PORTABILITY = new Characteristic(3, "HARDWARE_RELATED_PORTABILITY"); - - private static final Characteristic MAINTAINABILITY = new Characteristic(4, "MAINTAINABILITY"); - private static final Characteristic READABILITY = new Characteristic(5, "READABILITY"); - - 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"); - } -} +///* +// * 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"); +// } +//} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureRepositoryImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureRepositoryImplTest.java index ad6f55545e4..22f1a271827 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureRepositoryImplTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureRepositoryImplTest.java @@ -89,7 +89,7 @@ public class MeasureRepositoryImplTest { private static final Measure SOME_MEASURE = Measure.newMeasureBuilder().create("some value"); private static final String SOME_DATA = "some data"; private static final RuleDto SOME_RULE = RuleDto.createFor(RuleKey.of("A", "1")).setId(963); - private static final Characteristic SOME_CHARACTERISTIC = new Characteristic(741, "key"); + private static final Characteristic SOME_CHARACTERISTIC = new Characteristic(741, "key", null); private DbClient dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new MeasureDao(), new SnapshotDao(), new MetricDao(), new ComponentDao()); private MetricRepository metricRepository = mock(MetricRepository.class); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/FeedDebtModelStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/FeedDebtModelStepTest.java index e57a43636a8..97ae6bf520e 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 @@ -1,87 +1,87 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -package org.sonar.server.computation.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"); - } -} +///* +// * 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"); +// } +//} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/step/FillMeasuresWithVariationsStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/step/FillMeasuresWithVariationsStepTest.java index 9d4d6504b99..573b543f541 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/step/FillMeasuresWithVariationsStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/step/FillMeasuresWithVariationsStepTest.java @@ -311,9 +311,9 @@ public class FillMeasuresWithVariationsStepTest { sut.execute(); assertThat(measureRepository.getRawMeasure(PROJECT, toMetric(ISSUES_METRIC)).get().getVariations().getVariation1()).isEqualTo(20d); - assertThat(measureRepository.getRawMeasure(PROJECT, toMetric(ISSUES_METRIC), new Characteristic(char1.getId(), char1.getKey())).get().getVariations().getVariation1()) + assertThat(measureRepository.getRawMeasure(PROJECT, toMetric(ISSUES_METRIC), new Characteristic(char1.getId(), char1.getKey(), null)).get().getVariations().getVariation1()) .isEqualTo(5d); - assertThat(measureRepository.getRawMeasure(PROJECT, toMetric(ISSUES_METRIC), new Characteristic(char2.getId(), char2.getKey())).get().getVariations().getVariation1()) + assertThat(measureRepository.getRawMeasure(PROJECT, toMetric(ISSUES_METRIC), new Characteristic(char2.getId(), char2.getKey(), null)).get().getVariations().getVariation1()) .isEqualTo(15d); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java index 879ae9afb82..58a2e630d3c 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java @@ -37,7 +37,6 @@ import org.sonar.batch.compute.OverallCoverageDecorator; import org.sonar.batch.compute.OverallLineCoverageDecorator; import org.sonar.batch.compute.UnitTestDecorator; import org.sonar.batch.cpd.CpdComponents; -import org.sonar.batch.debt.DebtDecorator; import org.sonar.batch.debt.IssueChangelogDebtCalculator; import org.sonar.batch.debt.NewDebtDecorator; import org.sonar.batch.issue.tracking.IssueTracking; @@ -86,7 +85,6 @@ public class BatchComponents { // Debt IssueChangelogDebtCalculator.class, - DebtDecorator.class, NewDebtDecorator.class, // to be moved to compute engine diff --git a/sonar-batch/src/main/java/org/sonar/batch/debt/DebtDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/debt/DebtDecorator.java deleted file mode 100644 index b339ead086a..00000000000 --- a/sonar-batch/src/main/java/org/sonar/batch/debt/DebtDecorator.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -package org.sonar.batch.debt; - -import com.google.common.annotations.VisibleForTesting; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Set; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; -import org.sonar.api.batch.Decorator; -import org.sonar.api.batch.DecoratorBarriers; -import org.sonar.api.batch.DecoratorContext; -import org.sonar.api.batch.DependedUpon; -import org.sonar.api.batch.DependsUpon; -import org.sonar.api.batch.RequiresDB; -import org.sonar.api.batch.rule.Rule; -import org.sonar.api.batch.rule.Rules; -import org.sonar.api.component.ResourcePerspectives; -import org.sonar.api.issue.Issuable; -import org.sonar.api.issue.Issue; -import org.sonar.core.issue.DefaultIssue; -import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.measures.Measure; -import org.sonar.api.measures.MeasuresFilters; -import org.sonar.api.measures.Metric; -import org.sonar.api.measures.PersistenceMode; -import org.sonar.api.measures.RuleMeasure; -import org.sonar.api.resources.Project; -import org.sonar.api.resources.Resource; -import org.sonar.api.resources.ResourceUtils; -import org.sonar.api.rule.RuleKey; -import org.sonar.api.rules.RuleFinder; -import org.sonar.api.technicaldebt.batch.Characteristic; -import org.sonar.api.technicaldebt.batch.TechnicalDebtModel; - -import static com.google.common.collect.Lists.newArrayList; -import static com.google.common.collect.Maps.newHashMap; - -/** - * Decorator that computes the technical debt metric - */ -@DependsUpon(DecoratorBarriers.ISSUES_TRACKED) -@RequiresDB -public final class DebtDecorator implements Decorator { - - private final ResourcePerspectives perspectives; - private final TechnicalDebtModel model; - private final Rules rules; - - /** - * ruleFinder is needed to load "old" rule in order to persist rule measure - */ - private final RuleFinder ruleFinder; - - public DebtDecorator(ResourcePerspectives perspectives, TechnicalDebtModel model, Rules rules, RuleFinder ruleFinder) { - this.perspectives = perspectives; - this.model = model; - this.rules = rules; - this.ruleFinder = ruleFinder; - } - - @Override - public boolean shouldExecuteOnProject(Project project) { - return true; - } - - @DependedUpon - public List generatesMetrics() { - return Arrays.asList(CoreMetrics.TECHNICAL_DEBT); - } - - @Override - public void decorate(Resource resource, DecoratorContext context) { - Issuable issuable = perspectives.as(Issuable.class, resource); - if (issuable != null && shouldSaveMeasure(context)) { - List issues = newArrayList(issuable.issues()); - saveMeasures(context, issues); - } - } - - private void saveMeasures(DecoratorContext context, List issues) { - Long total = 0L; - SumMap ruleDebts = new SumMap<>(); - SumMap characteristicDebts = new SumMap<>(); - - // Aggregate rules debt from current issues (and populate current characteristic debt) - for (Issue issue : issues) { - Long debt = ((DefaultIssue) issue).debtInMinutes(); - total += computeDebt(debt, issue.ruleKey(), ruleDebts, characteristicDebts); - } - - // Aggregate rules debt from children (and populate children characteristics debt) - for (Measure measure : context.getChildrenMeasures(MeasuresFilters.rules(CoreMetrics.TECHNICAL_DEBT))) { - Long debt = measure.getValue().longValue(); - RuleMeasure ruleMeasure = (RuleMeasure) measure; - total += computeDebt(debt, ruleMeasure.ruleKey(), ruleDebts, characteristicDebts); - } - - context.saveMeasure(CoreMetrics.TECHNICAL_DEBT, total.doubleValue()); - saveOnRule(context, ruleDebts); - for (Characteristic characteristic : model.characteristics()) { - Long debt = characteristicDebts.get(characteristic); - saveCharacteristicMeasure(context, characteristic, debt != null ? debt.doubleValue() : 0d, false); - } - } - - private Long computeDebt(@Nullable Long debt, RuleKey ruleKey, SumMap ruleDebts, SumMap characteristicDebts) { - if (debt != null) { - Rule rule = rules.find(ruleKey); - if (rule != null) { - String characteristicKey = rule.debtSubCharacteristic(); - if (characteristicKey != null) { - Characteristic characteristic = model.characteristicByKey(characteristicKey); - if (characteristic != null) { - ruleDebts.add(ruleKey, debt); - characteristicDebts.add(characteristic, debt); - propagateTechnicalDebtInParents(characteristic.parent(), debt, characteristicDebts); - return debt; - } - } - } - } - return 0L; - } - - private void propagateTechnicalDebtInParents(@Nullable Characteristic characteristic, long value, SumMap characteristicDebts) { - if (characteristic != null) { - characteristicDebts.add(characteristic, value); - propagateTechnicalDebtInParents(characteristic.parent(), value, characteristicDebts); - } - } - - private void saveOnRule(DecoratorContext context, SumMap ruleDebts) { - for (Map.Entry entry : ruleDebts.entrySet()) { - org.sonar.api.rules.Rule oldRule = ruleFinder.findByKey(entry.getKey()); - if (oldRule != null) { - saveRuleMeasure(context, oldRule, entry.getValue().doubleValue(), ResourceUtils.isEntity(context.getResource())); - } - } - } - - @VisibleForTesting - void saveCharacteristicMeasure(DecoratorContext context, Characteristic characteristic, Double value, boolean inMemory) { - // we need the value on projects (root or module) even if value==0 in order to display correctly the SQALE history chart (see SQALE-122) - // BUT we don't want to save zero-values for non top-characteristics (see SQALE-147) - if (value > 0.0 || (ResourceUtils.isProject(context.getResource()) && characteristic.isRoot())) { - Measure measure = new Measure(CoreMetrics.TECHNICAL_DEBT); - measure.setCharacteristic(characteristic); - saveMeasure(context, measure, value, inMemory); - } - } - - @VisibleForTesting - void saveRuleMeasure(DecoratorContext context, org.sonar.api.rules.Rule rule, Double value, boolean inMemory) { - // we need the value on projects (root or module) even if value==0 in order to display correctly the SQALE history chart (see SQALE-122) - // BUT we don't want to save zero-values for non top-characteristics (see SQALE-147) - if (value > 0.0) { - RuleMeasure measure = new RuleMeasure(CoreMetrics.TECHNICAL_DEBT, rule, null, null); - saveMeasure(context, measure, value, inMemory); - } - } - - private static void saveMeasure(DecoratorContext context, Measure measure, Double value, boolean inMemory) { - measure.setValue(value); - if (inMemory) { - measure.setPersistenceMode(PersistenceMode.MEMORY); - } - context.saveMeasure(measure); - } - - private static boolean shouldSaveMeasure(DecoratorContext context) { - return context.getMeasure(CoreMetrics.TECHNICAL_DEBT) == null; - } - - private static class SumMap { - private Map sumByKeys; - - public SumMap() { - sumByKeys = newHashMap(); - } - - public void add(@Nullable E key, Long value) { - if (key != null) { - Long currentValue = sumByKeys.get(key); - sumByKeys.put(key, currentValue != null ? (currentValue + value) : value); - } - } - - @CheckForNull - public Long get(E key) { - return sumByKeys.get(key); - } - - public Set> entrySet() { - return sumByKeys.entrySet(); - } - } -} diff --git a/sonar-batch/src/main/java/org/sonar/batch/report/MeasuresPublisher.java b/sonar-batch/src/main/java/org/sonar/batch/report/MeasuresPublisher.java index fe57b624502..9aad7276659 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/report/MeasuresPublisher.java +++ b/sonar-batch/src/main/java/org/sonar/batch/report/MeasuresPublisher.java @@ -111,13 +111,6 @@ public class MeasuresPublisher implements ReportPublisherStep { if (description != null) { builder.setDescription(description); } - if (measure instanceof RuleMeasure) { - RuleMeasure ruleMeasure = (RuleMeasure) measure; - RuleKey ruleKey = ruleMeasure.ruleKey(); - if (ruleKey != null) { - builder.setRuleKey(ruleKey.toString()); - } - } Level alertStatus = measure.getAlertStatus(); if (alertStatus != null) { builder.setAlertStatus(alertStatus.toString()); diff --git a/sonar-batch/src/test/java/org/sonar/batch/debt/DebtDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/debt/DebtDecoratorTest.java index 94a58c79b0d..4b96aba9ac0 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/debt/DebtDecoratorTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/debt/DebtDecoratorTest.java @@ -1,353 +1,353 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -package org.sonar.batch.debt; - -import com.google.common.collect.Lists; -import org.apache.commons.lang.ObjectUtils; -import org.apache.commons.lang.builder.ToStringBuilder; -import org.apache.commons.lang.builder.ToStringStyle; -import org.hamcrest.Description; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentMatcher; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; -import org.sonar.api.batch.DecoratorContext; -import org.sonar.api.batch.rule.Rules; -import org.sonar.api.batch.rule.internal.RulesBuilder; -import org.sonar.api.component.ResourcePerspectives; -import org.sonar.api.issue.Issuable; -import org.sonar.api.issue.Issue; -import org.sonar.core.issue.DefaultIssue; -import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.measures.Measure; -import org.sonar.api.measures.MeasuresFilter; -import org.sonar.api.measures.Metric; -import org.sonar.api.measures.RuleMeasure; -import org.sonar.api.resources.File; -import org.sonar.api.resources.Project; -import org.sonar.api.resources.Resource; -import org.sonar.api.rule.RuleKey; -import org.sonar.api.rules.RuleFinder; -import org.sonar.api.technicaldebt.batch.Characteristic; -import org.sonar.api.technicaldebt.batch.TechnicalDebtModel; -import org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic; -import org.sonar.api.test.IsMeasure; -import org.sonar.api.utils.Duration; - -import java.util.Collections; - -import static com.google.common.collect.Lists.newArrayList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.argThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@RunWith(MockitoJUnitRunner.class) -public class DebtDecoratorTest { - - static final int HOURS_IN_DAY = 8; - - static final Long ONE_DAY_IN_MINUTES = 1L * HOURS_IN_DAY * 60; - - @Mock - DecoratorContext context; - - @Mock - Resource resource; - - @Mock - TechnicalDebtModel debtModel; - - @Mock - Issuable issuable; - - @Mock - ResourcePerspectives perspectives; - - @Mock - RuleFinder ruleFinder; - - RuleKey ruleKey1 = RuleKey.of("repo1", "rule1"); - RuleKey ruleKey2 = RuleKey.of("repo2", "rule2"); - Rules rules; - - DefaultCharacteristic efficiency = new DefaultCharacteristic().setKey("EFFICIENCY"); - DefaultCharacteristic memoryEfficiency = new DefaultCharacteristic().setKey("MEMORY_EFFICIENCY").setParent(efficiency); - - DefaultCharacteristic reusability = new DefaultCharacteristic().setKey("REUSABILITY"); - DefaultCharacteristic modularity = new DefaultCharacteristic().setKey("MODULARITY").setParent(reusability); - - DebtDecorator decorator; - - @Before - public void before() { - when(perspectives.as(Issuable.class, resource)).thenReturn(issuable); - RulesBuilder rulesBuilder = new RulesBuilder(); - rulesBuilder.add(ruleKey1).setName("rule1").setDebtSubCharacteristic("MEMORY_EFFICIENCY"); - rulesBuilder.add(ruleKey2).setName("rule2").setDebtSubCharacteristic("MODULARITY"); - rules = rulesBuilder.build(); - - when(ruleFinder.findByKey(ruleKey1)).thenReturn(org.sonar.api.rules.Rule.create(ruleKey1.repository(), ruleKey1.rule())); - when(ruleFinder.findByKey(ruleKey2)).thenReturn(org.sonar.api.rules.Rule.create(ruleKey2.repository(), ruleKey2.rule())); - - when(debtModel.characteristics()).thenReturn(newArrayList(efficiency, memoryEfficiency, reusability, modularity)); - when(debtModel.characteristicByKey("EFFICIENCY")).thenReturn(efficiency); - when(debtModel.characteristicByKey("MEMORY_EFFICIENCY")).thenReturn(memoryEfficiency); - when(debtModel.characteristicByKey("REUSABILITY")).thenReturn(reusability); - when(debtModel.characteristicByKey("MODULARITY")).thenReturn(modularity); - - decorator = new DebtDecorator(perspectives, debtModel, rules, ruleFinder); - } - - @Test - public void generates_metrics() { - assertThat(decorator.generatesMetrics()).hasSize(1); - } - - @Test - public void execute_on_project() { - assertThat(decorator.shouldExecuteOnProject(null)).isTrue(); - } - - @Test - public void not_save_if_measure_already_computed() { - when(context.getMeasure(CoreMetrics.TECHNICAL_DEBT)).thenReturn(new Measure()); - - decorator.decorate(resource, context); - - verify(context, never()).saveMeasure(argThat(new IsMeasure(CoreMetrics.TECHNICAL_DEBT))); - } - - @Test - public void add_technical_debt_from_one_issue_and_no_parent() { - Issue issue = createIssue("rule1", "repo1").setDebt(Duration.create(ONE_DAY_IN_MINUTES)); - when(issuable.issues()).thenReturn(newArrayList(issue)); - - decorator.decorate(resource, context); - - verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, ONE_DAY_IN_MINUTES.doubleValue()); - verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, ONE_DAY_IN_MINUTES.doubleValue()))); - } - - @Test - public void add_technical_debt_from_one_issue_without_debt() { - Issue issue = createIssue("rule1", "repo1").setDebt(null); - when(issuable.issues()).thenReturn(newArrayList(issue)); - - decorator.decorate(resource, context); - - verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, 0.0); - } - - @Test - public void add_technical_debt_from_one_issue_and_propagate_to_parents() { - Issue issue = createIssue("rule1", "repo1").setDebt(Duration.create(ONE_DAY_IN_MINUTES)); - when(issuable.issues()).thenReturn(newArrayList(issue)); - - decorator.decorate(resource, context); - - verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, ONE_DAY_IN_MINUTES.doubleValue()); - verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, ONE_DAY_IN_MINUTES.doubleValue()))); - verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, efficiency, ONE_DAY_IN_MINUTES.doubleValue()))); - verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, memoryEfficiency, ONE_DAY_IN_MINUTES.doubleValue()))); - } - - @Test - public void add_technical_debt_from_issues() { - Long technicalDebt1 = ONE_DAY_IN_MINUTES; - Long technicalDebt2 = 2 * ONE_DAY_IN_MINUTES; - - Issue issue1 = createIssue("rule1", "repo1").setDebt(Duration.create(technicalDebt1)); - Issue issue2 = createIssue("rule1", "repo1").setDebt(Duration.create(technicalDebt1)); - Issue issue3 = createIssue("rule2", "repo2").setDebt(Duration.create(technicalDebt2)); - Issue issue4 = createIssue("rule2", "repo2").setDebt(Duration.create(technicalDebt2)); - when(issuable.issues()).thenReturn(newArrayList(issue1, issue2, issue3, issue4)); - - decorator.decorate(resource, context); - - verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, 6d * ONE_DAY_IN_MINUTES); - verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, 2d * ONE_DAY_IN_MINUTES))); - verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey2, 4d * ONE_DAY_IN_MINUTES))); - } - - @Test - public void add_technical_debt_from_current_and_children_measures() { - Issue issue1 = createIssue("rule1", "repo1").setDebt(Duration.create(ONE_DAY_IN_MINUTES)); - Issue issue2 = createIssue("rule1", "repo1").setDebt(Duration.create(ONE_DAY_IN_MINUTES)); - when(issuable.issues()).thenReturn(newArrayList(issue1, issue2)); - - when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Lists.newArrayList( - new RuleMeasure(CoreMetrics.TECHNICAL_DEBT, - org.sonar.api.rules.Rule.create(ruleKey1.repository(), ruleKey1.rule()), null, null) - .setValue(5d * ONE_DAY_IN_MINUTES) - )); - decorator.decorate(resource, context); - - verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, 7d * ONE_DAY_IN_MINUTES); - verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, 7d * ONE_DAY_IN_MINUTES))); - verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, memoryEfficiency, 7d * ONE_DAY_IN_MINUTES))); - verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, efficiency, 7d * ONE_DAY_IN_MINUTES))); - } - - @Test - public void add_technical_debt_only_from_children_measures() { - when(issuable.issues()).thenReturn(Collections.emptyList()); - - when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Lists.newArrayList( - new RuleMeasure(CoreMetrics.TECHNICAL_DEBT, - org.sonar.api.rules.Rule.create(ruleKey1.repository(), ruleKey1.rule()) - , null, null).setValue(5d * ONE_DAY_IN_MINUTES), - - new RuleMeasure(CoreMetrics.TECHNICAL_DEBT, - org.sonar.api.rules.Rule.create(ruleKey2.repository(), ruleKey2.rule()) - , null, null).setValue(10d * ONE_DAY_IN_MINUTES) - )); - decorator.decorate(resource, context); - - verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, 15d * ONE_DAY_IN_MINUTES); - verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, 5d * ONE_DAY_IN_MINUTES))); - verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey2, 10d * ONE_DAY_IN_MINUTES))); - verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, memoryEfficiency, 5d * ONE_DAY_IN_MINUTES))); - verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, efficiency, 5d * ONE_DAY_IN_MINUTES))); - verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, reusability, 10d * ONE_DAY_IN_MINUTES))); - verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, modularity, 10d * ONE_DAY_IN_MINUTES))); - } - - @Test - public void always_save_technical_debt_for_positive_values() { - // for a project - DecoratorContext context = mock(DecoratorContext.class); - when(context.getResource()).thenReturn(new Project("foo")); - decorator.saveCharacteristicMeasure(context, (Characteristic) null, 12.0, false); - verify(context, times(1)).saveMeasure(new Measure(CoreMetrics.TECHNICAL_DEBT)); - - // or for a file - context = mock(DecoratorContext.class); - when(context.getResource()).thenReturn(File.create("foo")); - decorator.saveCharacteristicMeasure(context, (Characteristic) null, 12.0, false); - verify(context, times(1)).saveMeasure(new Measure(CoreMetrics.TECHNICAL_DEBT)); - } - - @Test - public void always_save_technical_debt_for_project_if_top_characteristic() { - DecoratorContext context = mock(DecoratorContext.class); - when(context.getResource()).thenReturn(new Project("foo")); - - // this is a top characteristic - DefaultCharacteristic rootCharacteristic = new DefaultCharacteristic().setKey("root"); - - decorator.saveCharacteristicMeasure(context, rootCharacteristic, 0.0, true); - verify(context, times(1)).saveMeasure(new Measure(CoreMetrics.TECHNICAL_DEBT).setCharacteristic(rootCharacteristic)); - } - - /** - * SQALE-147 - */ - @Test - public void never_save_technical_debt_for_project_if_not_top_characteristic() { - DecoratorContext context = mock(DecoratorContext.class); - when(context.getResource()).thenReturn(new Project("foo")); - - DefaultCharacteristic rootCharacteristic = new DefaultCharacteristic().setKey("EFFICIENCY"); - DefaultCharacteristic characteristic = new DefaultCharacteristic().setKey("MEMORY_EFFICIENCY").setParent(rootCharacteristic); - - decorator.saveCharacteristicMeasure(context, characteristic, 0.0, true); - verify(context, never()).saveMeasure(any(Measure.class)); - } - - @Test - public void not_save_technical_debt_for_file_if_zero() { - DecoratorContext context = mock(DecoratorContext.class); - when(context.getResource()).thenReturn(File.create("foo")); - - decorator.saveCharacteristicMeasure(context, null, 0.0, true); - verify(context, never()).saveMeasure(new Measure(CoreMetrics.TECHNICAL_DEBT)); - } - - private DefaultIssue createIssue(String ruleKey, String repositoryKey) { - return new DefaultIssue().setRuleKey(RuleKey.of(repositoryKey, ruleKey)); - } - - class IsCharacteristicMeasure extends ArgumentMatcher { - Metric metric = null; - Characteristic characteristic = null; - Double value = null; - - public IsCharacteristicMeasure(Metric metric, Characteristic characteristic, Double value) { - this.metric = metric; - this.characteristic = characteristic; - this.value = value; - } - - @Override - public boolean matches(Object o) { - if (!(o instanceof Measure)) { - return false; - } - Measure m = (Measure) o; - return ObjectUtils.equals(metric, m.getMetric()) && - ObjectUtils.equals(characteristic, m.getCharacteristic()) && - ObjectUtils.equals(value, m.getValue()); - } - - @Override - public void describeTo(Description description) { - description.appendText(new StringBuilder() - .append("value=").append(value).append(",") - .append("characteristic=").append(characteristic.key()).append(",") - .append("metric=").append(metric.getKey()).toString()); - } - } - - class IsRuleMeasure extends ArgumentMatcher { - Metric metric = null; - RuleKey ruleKey = null; - Double value = null; - - public IsRuleMeasure(Metric metric, RuleKey ruleKey, Double value) { - this.metric = metric; - this.ruleKey = ruleKey; - this.value = value; - } - - @Override - public boolean matches(Object o) { - if (!(o instanceof RuleMeasure)) { - return false; - } - RuleMeasure m = (RuleMeasure) o; - return ObjectUtils.equals(metric, m.getMetric()) && - ObjectUtils.equals(ruleKey, m.ruleKey()) && - ObjectUtils.equals(value, m.getValue()); - } - - @Override - public void describeTo(Description description) { - description.appendText(ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE)); - } - } -} +///* +// * SonarQube, open source software quality management tool. +// * Copyright (C) 2008-2014 SonarSource +// * mailto:contact AT sonarsource DOT com +// * +// * SonarQube is free software; you can redistribute it and/or +// * modify it under the terms of the GNU Lesser General Public +// * License as published by the Free Software Foundation; either +// * version 3 of the License, or (at your option) any later version. +// * +// * SonarQube is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// * Lesser General Public License for more details. +// * +// * You should have received a copy of the GNU Lesser General Public License +// * along with this program; if not, write to the Free Software Foundation, +// * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// */ +// +//package org.sonar.batch.debt; +// +//import com.google.common.collect.Lists; +//import org.apache.commons.lang.ObjectUtils; +//import org.apache.commons.lang.builder.ToStringBuilder; +//import org.apache.commons.lang.builder.ToStringStyle; +//import org.hamcrest.Description; +//import org.junit.Before; +//import org.junit.Test; +//import org.junit.runner.RunWith; +//import org.mockito.ArgumentMatcher; +//import org.mockito.Mock; +//import org.mockito.runners.MockitoJUnitRunner; +//import org.sonar.api.batch.DecoratorContext; +//import org.sonar.api.batch.rule.Rules; +//import org.sonar.api.batch.rule.internal.RulesBuilder; +//import org.sonar.api.component.ResourcePerspectives; +//import org.sonar.api.issue.Issuable; +//import org.sonar.api.issue.Issue; +//import org.sonar.core.issue.DefaultIssue; +//import org.sonar.api.measures.CoreMetrics; +//import org.sonar.api.measures.Measure; +//import org.sonar.api.measures.MeasuresFilter; +//import org.sonar.api.measures.Metric; +//import org.sonar.api.measures.RuleMeasure; +//import org.sonar.api.resources.File; +//import org.sonar.api.resources.Project; +//import org.sonar.api.resources.Resource; +//import org.sonar.api.rule.RuleKey; +//import org.sonar.api.rules.RuleFinder; +//import org.sonar.api.technicaldebt.batch.Characteristic; +//import org.sonar.api.technicaldebt.batch.TechnicalDebtModel; +//import org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic; +//import org.sonar.api.test.IsMeasure; +//import org.sonar.api.utils.Duration; +// +//import java.util.Collections; +// +//import static com.google.common.collect.Lists.newArrayList; +//import static org.assertj.core.api.Assertions.assertThat; +//import static org.mockito.Matchers.any; +//import static org.mockito.Matchers.argThat; +//import static org.mockito.Mockito.mock; +//import static org.mockito.Mockito.never; +//import static org.mockito.Mockito.times; +//import static org.mockito.Mockito.verify; +//import static org.mockito.Mockito.when; +// +//@RunWith(MockitoJUnitRunner.class) +//public class DebtDecoratorTest { +// +// static final int HOURS_IN_DAY = 8; +// +// static final Long ONE_DAY_IN_MINUTES = 1L * HOURS_IN_DAY * 60; +// +// @Mock +// DecoratorContext context; +// +// @Mock +// Resource resource; +// +// @Mock +// TechnicalDebtModel debtModel; +// +// @Mock +// Issuable issuable; +// +// @Mock +// ResourcePerspectives perspectives; +// +// @Mock +// RuleFinder ruleFinder; +// +// RuleKey ruleKey1 = RuleKey.of("repo1", "rule1"); +// RuleKey ruleKey2 = RuleKey.of("repo2", "rule2"); +// Rules rules; +// +// DefaultCharacteristic efficiency = new DefaultCharacteristic().setKey("EFFICIENCY"); +// DefaultCharacteristic memoryEfficiency = new DefaultCharacteristic().setKey("MEMORY_EFFICIENCY").setParent(efficiency); +// +// DefaultCharacteristic reusability = new DefaultCharacteristic().setKey("REUSABILITY"); +// DefaultCharacteristic modularity = new DefaultCharacteristic().setKey("MODULARITY").setParent(reusability); +// +// DebtDecorator decorator; +// +// @Before +// public void before() { +// when(perspectives.as(Issuable.class, resource)).thenReturn(issuable); +// RulesBuilder rulesBuilder = new RulesBuilder(); +// rulesBuilder.add(ruleKey1).setName("rule1").setDebtSubCharacteristic("MEMORY_EFFICIENCY"); +// rulesBuilder.add(ruleKey2).setName("rule2").setDebtSubCharacteristic("MODULARITY"); +// rules = rulesBuilder.build(); +// +// when(ruleFinder.findByKey(ruleKey1)).thenReturn(org.sonar.api.rules.Rule.create(ruleKey1.repository(), ruleKey1.rule())); +// when(ruleFinder.findByKey(ruleKey2)).thenReturn(org.sonar.api.rules.Rule.create(ruleKey2.repository(), ruleKey2.rule())); +// +// when(debtModel.characteristics()).thenReturn(newArrayList(efficiency, memoryEfficiency, reusability, modularity)); +// when(debtModel.characteristicByKey("EFFICIENCY")).thenReturn(efficiency); +// when(debtModel.characteristicByKey("MEMORY_EFFICIENCY")).thenReturn(memoryEfficiency); +// when(debtModel.characteristicByKey("REUSABILITY")).thenReturn(reusability); +// when(debtModel.characteristicByKey("MODULARITY")).thenReturn(modularity); +// +// decorator = new DebtDecorator(perspectives, debtModel, rules, ruleFinder); +// } +// +// @Test +// public void generates_metrics() { +// assertThat(decorator.generatesMetrics()).hasSize(1); +// } +// +// @Test +// public void execute_on_project() { +// assertThat(decorator.shouldExecuteOnProject(null)).isTrue(); +// } +// +// @Test +// public void not_save_if_measure_already_computed() { +// when(context.getMeasure(CoreMetrics.TECHNICAL_DEBT)).thenReturn(new Measure()); +// +// decorator.decorate(resource, context); +// +// verify(context, never()).saveMeasure(argThat(new IsMeasure(CoreMetrics.TECHNICAL_DEBT))); +// } +// +// @Test +// public void add_technical_debt_from_one_issue_and_no_parent() { +// Issue issue = createIssue("rule1", "repo1").setDebt(Duration.create(ONE_DAY_IN_MINUTES)); +// when(issuable.issues()).thenReturn(newArrayList(issue)); +// +// decorator.decorate(resource, context); +// +// verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, ONE_DAY_IN_MINUTES.doubleValue()); +// verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, ONE_DAY_IN_MINUTES.doubleValue()))); +// } +// +// @Test +// public void add_technical_debt_from_one_issue_without_debt() { +// Issue issue = createIssue("rule1", "repo1").setDebt(null); +// when(issuable.issues()).thenReturn(newArrayList(issue)); +// +// decorator.decorate(resource, context); +// +// verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, 0.0); +// } +// +// @Test +// public void add_technical_debt_from_one_issue_and_propagate_to_parents() { +// Issue issue = createIssue("rule1", "repo1").setDebt(Duration.create(ONE_DAY_IN_MINUTES)); +// when(issuable.issues()).thenReturn(newArrayList(issue)); +// +// decorator.decorate(resource, context); +// +// verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, ONE_DAY_IN_MINUTES.doubleValue()); +// verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, ONE_DAY_IN_MINUTES.doubleValue()))); +// verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, efficiency, ONE_DAY_IN_MINUTES.doubleValue()))); +// verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, memoryEfficiency, ONE_DAY_IN_MINUTES.doubleValue()))); +// } +// +// @Test +// public void add_technical_debt_from_issues() { +// Long technicalDebt1 = ONE_DAY_IN_MINUTES; +// Long technicalDebt2 = 2 * ONE_DAY_IN_MINUTES; +// +// Issue issue1 = createIssue("rule1", "repo1").setDebt(Duration.create(technicalDebt1)); +// Issue issue2 = createIssue("rule1", "repo1").setDebt(Duration.create(technicalDebt1)); +// Issue issue3 = createIssue("rule2", "repo2").setDebt(Duration.create(technicalDebt2)); +// Issue issue4 = createIssue("rule2", "repo2").setDebt(Duration.create(technicalDebt2)); +// when(issuable.issues()).thenReturn(newArrayList(issue1, issue2, issue3, issue4)); +// +// decorator.decorate(resource, context); +// +// verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, 6d * ONE_DAY_IN_MINUTES); +// verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, 2d * ONE_DAY_IN_MINUTES))); +// verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey2, 4d * ONE_DAY_IN_MINUTES))); +// } +// +// @Test +// public void add_technical_debt_from_current_and_children_measures() { +// Issue issue1 = createIssue("rule1", "repo1").setDebt(Duration.create(ONE_DAY_IN_MINUTES)); +// Issue issue2 = createIssue("rule1", "repo1").setDebt(Duration.create(ONE_DAY_IN_MINUTES)); +// when(issuable.issues()).thenReturn(newArrayList(issue1, issue2)); +// +// when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Lists.newArrayList( +// new RuleMeasure(CoreMetrics.TECHNICAL_DEBT, +// org.sonar.api.rules.Rule.create(ruleKey1.repository(), ruleKey1.rule()), null, null) +// .setValue(5d * ONE_DAY_IN_MINUTES) +// )); +// decorator.decorate(resource, context); +// +// verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, 7d * ONE_DAY_IN_MINUTES); +// verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, 7d * ONE_DAY_IN_MINUTES))); +// verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, memoryEfficiency, 7d * ONE_DAY_IN_MINUTES))); +// verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, efficiency, 7d * ONE_DAY_IN_MINUTES))); +// } +// +// @Test +// public void add_technical_debt_only_from_children_measures() { +// when(issuable.issues()).thenReturn(Collections.emptyList()); +// +// when(context.getChildrenMeasures(any(MeasuresFilter.class))).thenReturn(Lists.newArrayList( +// new RuleMeasure(CoreMetrics.TECHNICAL_DEBT, +// org.sonar.api.rules.Rule.create(ruleKey1.repository(), ruleKey1.rule()) +// , null, null).setValue(5d * ONE_DAY_IN_MINUTES), +// +// new RuleMeasure(CoreMetrics.TECHNICAL_DEBT, +// org.sonar.api.rules.Rule.create(ruleKey2.repository(), ruleKey2.rule()) +// , null, null).setValue(10d * ONE_DAY_IN_MINUTES) +// )); +// decorator.decorate(resource, context); +// +// verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, 15d * ONE_DAY_IN_MINUTES); +// verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey1, 5d * ONE_DAY_IN_MINUTES))); +// verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.TECHNICAL_DEBT, ruleKey2, 10d * ONE_DAY_IN_MINUTES))); +// verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, memoryEfficiency, 5d * ONE_DAY_IN_MINUTES))); +// verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, efficiency, 5d * ONE_DAY_IN_MINUTES))); +// verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, reusability, 10d * ONE_DAY_IN_MINUTES))); +// verify(context).saveMeasure(argThat(new IsCharacteristicMeasure(CoreMetrics.TECHNICAL_DEBT, modularity, 10d * ONE_DAY_IN_MINUTES))); +// } +// +// @Test +// public void always_save_technical_debt_for_positive_values() { +// // for a project +// DecoratorContext context = mock(DecoratorContext.class); +// when(context.getResource()).thenReturn(new Project("foo")); +// decorator.saveCharacteristicMeasure(context, (Characteristic) null, 12.0, false); +// verify(context, times(1)).saveMeasure(new Measure(CoreMetrics.TECHNICAL_DEBT)); +// +// // or for a file +// context = mock(DecoratorContext.class); +// when(context.getResource()).thenReturn(File.create("foo")); +// decorator.saveCharacteristicMeasure(context, (Characteristic) null, 12.0, false); +// verify(context, times(1)).saveMeasure(new Measure(CoreMetrics.TECHNICAL_DEBT)); +// } +// +// @Test +// public void always_save_technical_debt_for_project_if_top_characteristic() { +// DecoratorContext context = mock(DecoratorContext.class); +// when(context.getResource()).thenReturn(new Project("foo")); +// +// // this is a top characteristic +// DefaultCharacteristic rootCharacteristic = new DefaultCharacteristic().setKey("root"); +// +// decorator.saveCharacteristicMeasure(context, rootCharacteristic, 0.0, true); +// verify(context, times(1)).saveMeasure(new Measure(CoreMetrics.TECHNICAL_DEBT).setCharacteristic(rootCharacteristic)); +// } +// +// /** +// * SQALE-147 +// */ +// @Test +// public void never_save_technical_debt_for_project_if_not_top_characteristic() { +// DecoratorContext context = mock(DecoratorContext.class); +// when(context.getResource()).thenReturn(new Project("foo")); +// +// DefaultCharacteristic rootCharacteristic = new DefaultCharacteristic().setKey("EFFICIENCY"); +// DefaultCharacteristic characteristic = new DefaultCharacteristic().setKey("MEMORY_EFFICIENCY").setParent(rootCharacteristic); +// +// decorator.saveCharacteristicMeasure(context, characteristic, 0.0, true); +// verify(context, never()).saveMeasure(any(Measure.class)); +// } +// +// @Test +// public void not_save_technical_debt_for_file_if_zero() { +// DecoratorContext context = mock(DecoratorContext.class); +// when(context.getResource()).thenReturn(File.create("foo")); +// +// decorator.saveCharacteristicMeasure(context, null, 0.0, true); +// verify(context, never()).saveMeasure(new Measure(CoreMetrics.TECHNICAL_DEBT)); +// } +// +// private DefaultIssue createIssue(String ruleKey, String repositoryKey) { +// return new DefaultIssue().setRuleKey(RuleKey.of(repositoryKey, ruleKey)); +// } +// +// class IsCharacteristicMeasure extends ArgumentMatcher { +// Metric metric = null; +// Characteristic characteristic = null; +// Double value = null; +// +// public IsCharacteristicMeasure(Metric metric, Characteristic characteristic, Double value) { +// this.metric = metric; +// this.characteristic = characteristic; +// this.value = value; +// } +// +// @Override +// public boolean matches(Object o) { +// if (!(o instanceof Measure)) { +// return false; +// } +// Measure m = (Measure) o; +// return ObjectUtils.equals(metric, m.getMetric()) && +// ObjectUtils.equals(characteristic, m.getCharacteristic()) && +// ObjectUtils.equals(value, m.getValue()); +// } +// +// @Override +// public void describeTo(Description description) { +// description.appendText(new StringBuilder() +// .append("value=").append(value).append(",") +// .append("characteristic=").append(characteristic.key()).append(",") +// .append("metric=").append(metric.getKey()).toString()); +// } +// } +// +// class IsRuleMeasure extends ArgumentMatcher { +// Metric metric = null; +// RuleKey ruleKey = null; +// Double value = null; +// +// public IsRuleMeasure(Metric metric, RuleKey ruleKey, Double value) { +// this.metric = metric; +// this.ruleKey = ruleKey; +// this.value = value; +// } +// +// @Override +// public boolean matches(Object o) { +// if (!(o instanceof RuleMeasure)) { +// return false; +// } +// RuleMeasure m = (RuleMeasure) o; +// return ObjectUtils.equals(metric, m.getMetric()) && +// ObjectUtils.equals(ruleKey, m.ruleKey()) && +// ObjectUtils.equals(value, m.getValue()); +// } +// +// @Override +// public void describeTo(Description description) { +// description.appendText(ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE)); +// } +// } +//} diff --git a/sonar-batch/src/test/java/org/sonar/batch/report/MeasuresPublisherTest.java b/sonar-batch/src/test/java/org/sonar/batch/report/MeasuresPublisherTest.java index 658cafcc62d..94204a3afbc 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/report/MeasuresPublisherTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/report/MeasuresPublisherTest.java @@ -19,6 +19,11 @@ */ package org.sonar.batch.report; +import java.io.File; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.List; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -30,23 +35,14 @@ import org.sonar.api.measures.Metric; import org.sonar.api.measures.Metric.Level; import org.sonar.api.measures.Metric.ValueType; import org.sonar.api.measures.MetricFinder; -import org.sonar.api.measures.RuleMeasure; import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; -import org.sonar.api.rule.RuleKey; -import org.sonar.api.rules.RulePriority; import org.sonar.api.technicaldebt.batch.Characteristic; import org.sonar.batch.index.BatchComponentCache; import org.sonar.batch.protocol.output.BatchReportReader; import org.sonar.batch.protocol.output.BatchReportWriter; import org.sonar.batch.scan.measure.MeasureCache; -import java.io.File; -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; -import java.util.List; - import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; @@ -103,9 +99,6 @@ public class MeasuresPublisherTest { Measure manual = new Measure<>(new Metric<>("manual_metric", ValueType.BOOL)) .setValue(1.0) .setDescription("Manual"); - // Rule measure - RuleMeasure ruleMeasureBySeverity = RuleMeasure.createForPriority(CoreMetrics.NCLOC, RulePriority.BLOCKER, 1.0); - RuleMeasure ruleMeasureByRule = RuleMeasure.createForRule(CoreMetrics.NCLOC, RuleKey.of("squid", "S12345"), 1.0); // Sqale rating have both a value and a data Measure rating = new Measure<>(CoreMetrics.SQALE_RATING) .setValue(2.0) @@ -117,7 +110,7 @@ public class MeasuresPublisherTest { Measure stringMeasure = new Measure<>(CoreMetrics.NCLOC_LANGUAGE_DISTRIBUTION) .setData("foo bar"); - when(measureCache.byResource(sampleFile)).thenReturn(Arrays.asList(measure1, measure2, manual, ruleMeasureBySeverity, ruleMeasureByRule, rating, longMeasure, stringMeasure)); + when(measureCache.byResource(sampleFile)).thenReturn(Arrays.asList(measure1, measure2, manual, rating, longMeasure, stringMeasure)); File outputDir = temp.newFolder(); BatchReportWriter writer = new BatchReportWriter(outputDir); diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java index 0575a850182..58dcf64ffdf 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java @@ -19,7 +19,8 @@ */ package org.sonar.batch.scan.measure; -import org.sonar.batch.index.AbstractCachesTest; +import java.util.Date; +import java.util.Iterator; import org.apache.commons.lang.builder.EqualsBuilder; import org.junit.Before; import org.junit.Rule; @@ -29,22 +30,15 @@ import org.sonar.api.batch.measure.MetricFinder; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Measure; import org.sonar.api.measures.Metric.Level; -import org.sonar.api.measures.RuleMeasure; import org.sonar.api.resources.Directory; import org.sonar.api.resources.File; import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; -import org.sonar.api.rule.RuleKey; -import org.sonar.api.rules.RulePriority; import org.sonar.api.technicaldebt.batch.Characteristic; import org.sonar.api.technicaldebt.batch.Requirement; -import org.sonar.api.technicaldebt.batch.TechnicalDebtModel; -import org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic; +import org.sonar.batch.index.AbstractCachesTest; import org.sonar.batch.index.Cache.Entry; -import java.util.Date; -import java.util.Iterator; - import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -55,8 +49,6 @@ public class MeasureCacheTest extends AbstractCachesTest { private MetricFinder metricFinder; - private TechnicalDebtModel techDebtModel; - private MeasureCache measureCache; @Before @@ -64,8 +56,7 @@ public class MeasureCacheTest extends AbstractCachesTest { super.start(); metricFinder = mock(MetricFinder.class); when(metricFinder.findByKey(CoreMetrics.NCLOC_KEY)).thenReturn(CoreMetrics.NCLOC); - techDebtModel = mock(TechnicalDebtModel.class); - measureCache = new MeasureCache(caches, metricFinder, techDebtModel); + measureCache = new MeasureCache(caches, metricFinder); } @Test @@ -89,13 +80,6 @@ public class MeasureCacheTest extends AbstractCachesTest { assertThat(measureCache.byResource(p)).hasSize(1); assertThat(measureCache.byResource(p).iterator().next()).isEqualTo(m); - - Measure mRule = RuleMeasure.createForPriority(CoreMetrics.CRITICAL_VIOLATIONS, RulePriority.BLOCKER, 1.0); - measureCache.put(p, mRule); - - assertThat(measureCache.entries()).hasSize(2); - - assertThat(measureCache.byResource(p)).hasSize(2); } @Test @@ -112,7 +96,7 @@ public class MeasureCacheTest extends AbstractCachesTest { for (int i = 0; i < 1_048_575; i++) { data.append("a"); } - + m.setData(data.toString()); measureCache.put(p, m); @@ -127,12 +111,6 @@ public class MeasureCacheTest extends AbstractCachesTest { assertThat(measureCache.byResource(p)).hasSize(1); assertThat(measureCache.byResource(p).iterator().next()).isEqualTo(m); - - RuleMeasure mRule = RuleMeasure.createForPriority(CoreMetrics.CRITICAL_VIOLATIONS, RulePriority.BLOCKER, 1.0); - mRule.setRuleKey(RuleKey.of("repo", "rule")); - measureCache.put(p, mRule); - - assertThat(measureCache.entries()).hasSize(2); } /** @@ -166,11 +144,6 @@ public class MeasureCacheTest extends AbstractCachesTest { assertThat(measureCache.byResource(p)).hasSize(1); assertThat(measureCache.byResource(p).iterator().next()).isEqualTo(m); - RuleMeasure mRule = RuleMeasure.createForPriority(CoreMetrics.CRITICAL_VIOLATIONS, RulePriority.BLOCKER, 1.0); - mRule.setRuleKey(RuleKey.of("repo", "rule")); - measureCache.put(p, mRule); - - assertThat(measureCache.entries()).hasSize(2); } @Test @@ -203,17 +176,12 @@ public class MeasureCacheTest extends AbstractCachesTest { assertThat(measureCache.byResource(p)).hasSize(0); Measure m1 = new Measure(CoreMetrics.NCLOC, 1.0); - Measure m2 = new Measure(CoreMetrics.NCLOC, 1.0).setCharacteristic(new DefaultCharacteristic().setKey("charac")); - Measure m3 = new Measure(CoreMetrics.NCLOC, 1.0).setPersonId(2); - Measure m4 = new RuleMeasure(CoreMetrics.NCLOC, RuleKey.of("repo", "rule"), RulePriority.BLOCKER, null); + Measure m2 = new Measure(CoreMetrics.NCLOC, 1.0).setPersonId(2); measureCache.put(p, m1); measureCache.put(p, m2); - measureCache.put(p, m3); - measureCache.put(p, m4); - - assertThat(measureCache.entries()).hasSize(4); - assertThat(measureCache.byResource(p)).hasSize(4); + assertThat(measureCache.entries()).hasSize(2); + assertThat(measureCache.byResource(p)).hasSize(2); } @Test 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 42530abf132..e3be32a43a0 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 @@ -204,6 +204,10 @@ public final class MeasuresFilters { } } + /** + * @deprecated since 5.2. Useless by design because of Compute Engine + */ + @Deprecated private abstract static class AbstractRuleMeasureFilter extends MetricFilter { protected AbstractRuleMeasureFilter(Metric metric) { super(metric); @@ -232,6 +236,10 @@ public final class MeasuresFilters { } } + /** + * @deprecated since 5.2. Useless by design because of Compute Engine + */ + @Deprecated private static class RuleFilter extends AbstractRuleMeasureFilter { private RuleKey ruleKey; diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/RuleMeasure.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/RuleMeasure.java index ca201e7ca06..ba265ef413d 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/measures/RuleMeasure.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/RuleMeasure.java @@ -31,7 +31,9 @@ import javax.annotation.Nullable; /** * @since 1.10 + * @deprecated since 5.2. Ignored by design because of Compute Engine. */ +@Deprecated public class RuleMeasure extends Measure { private RuleKey ruleKey; diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/test/IsRuleMeasure.java b/sonar-plugin-api/src/test/java/org/sonar/api/test/IsRuleMeasure.java deleted file mode 100644 index 3f9f67ce236..00000000000 --- a/sonar-plugin-api/src/test/java/org/sonar/api/test/IsRuleMeasure.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.api.test; - -import org.apache.commons.lang.ObjectUtils; -import org.apache.commons.lang.math.NumberUtils; -import org.mockito.ArgumentMatcher; -import org.sonar.api.measures.Measure; -import org.sonar.api.measures.Metric; -import org.sonar.api.measures.RuleMeasure; -import org.sonar.api.rules.Rule; - -public class IsRuleMeasure extends ArgumentMatcher { - - private Metric metric = null; - private Rule rule = null; - private Double value = null; - - public IsRuleMeasure(Metric metric, Rule rule, Double value) { - this.metric = metric; - this.rule = rule; - this.value = value; - } - - @Override - public boolean matches(Object o) { - if (!(o instanceof RuleMeasure)) { - return false; - } - RuleMeasure m = (RuleMeasure) o; - return ObjectUtils.equals(metric, m.getMetric()) && - ObjectUtils.equals(rule.ruleKey(), m.ruleKey()) && - NumberUtils.compare(value, m.getValue()) == 0; - } -}