@@ -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, |
@@ -20,6 +20,8 @@ | |||
package org.sonar.server.computation.debt; | |||
import java.util.List; | |||
public interface DebtModelHolder { | |||
/** | |||
@@ -30,4 +32,5 @@ public interface DebtModelHolder { | |||
*/ | |||
Characteristic getCharacteristicById(int id); | |||
List<Characteristic> getRootCharacteristics(); | |||
} |
@@ -20,7 +20,9 @@ | |||
package org.sonar.server.computation.debt; | |||
import java.util.ArrayList; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import static com.google.common.base.Preconditions.checkArgument; | |||
@@ -29,23 +31,26 @@ import static java.util.Objects.requireNonNull; | |||
public class DebtModelHolderImpl implements MutableDebtModelHolder { | |||
private final List<Characteristic> rootCharacteristics = new ArrayList<>(); | |||
private final Map<Integer, Characteristic> characteristicById = new HashMap<>(); | |||
@Override | |||
public void addCharacteristics(Characteristic rootCharacteristic, Iterable<? extends Characteristic> subCharacteristics) { | |||
public DebtModelHolderImpl addCharacteristics(Characteristic rootCharacteristic, Iterable<? extends Characteristic> subCharacteristics) { | |||
requireNonNull(rootCharacteristic, "rootCharacteristic cannot be null"); | |||
requireNonNull(subCharacteristics, "subCharacteristics cannot be null"); | |||
checkArgument(subCharacteristics.iterator().hasNext(), "subCharacteristics cannot be empty"); | |||
rootCharacteristics.add(rootCharacteristic); | |||
characteristicById.put(rootCharacteristic.getId(), rootCharacteristic); | |||
for (Characteristic characteristic : subCharacteristics) { | |||
characteristicById.put(characteristic.getId(), characteristic); | |||
} | |||
return this; | |||
} | |||
@Override | |||
public Characteristic getCharacteristicById(int id) { | |||
checkCharacteristicsAreInitialized(); | |||
checkInitialized(); | |||
Characteristic characteristic = characteristicById.get(id); | |||
if (characteristic == null) { | |||
throw new IllegalStateException("Debt characteristic with id [" + id + "] does not exist"); | |||
@@ -53,7 +58,13 @@ public class DebtModelHolderImpl implements MutableDebtModelHolder { | |||
return characteristic; | |||
} | |||
private void checkCharacteristicsAreInitialized() { | |||
@Override | |||
public List<Characteristic> getRootCharacteristics() { | |||
checkInitialized(); | |||
return rootCharacteristics; | |||
} | |||
private void checkInitialized() { | |||
checkState(!characteristicById.isEmpty(), "Characteristics have not been initialized yet"); | |||
} | |||
} |
@@ -31,5 +31,5 @@ public interface MutableDebtModelHolder extends DebtModelHolder { | |||
* @throws NullPointerException if {@code rootCharacteristic} is {@code null} | |||
* @throws NullPointerException if {@code subCharacteristics} is {@code null} | |||
*/ | |||
void addCharacteristics(Characteristic rootCharacteristic, Iterable<? extends Characteristic> subCharacteristics); | |||
MutableDebtModelHolder addCharacteristics(Characteristic rootCharacteristic, Iterable<? extends Characteristic> subCharacteristics); | |||
} |
@@ -19,29 +19,26 @@ | |||
*/ | |||
package org.sonar.server.computation.issue; | |||
import com.google.common.base.Function; | |||
import com.google.common.collect.ImmutableMap; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import javax.annotation.Nonnull; | |||
import org.apache.ibatis.session.ResultContext; | |||
import org.apache.ibatis.session.ResultHandler; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.rule.RuleStatus; | |||
import org.sonar.api.utils.log.Loggers; | |||
import org.sonar.core.issue.DefaultIssue; | |||
import org.sonar.core.issue.db.IssueDto; | |||
import org.sonar.core.issue.db.IssueMapper; | |||
import org.sonar.core.persistence.DbSession; | |||
import org.sonar.core.persistence.MyBatis; | |||
import org.sonar.core.rule.RuleDto; | |||
import org.sonar.server.computation.batch.BatchReportReader; | |||
import org.sonar.server.computation.component.TreeRootHolder; | |||
import org.sonar.server.db.DbClient; | |||
import static com.google.common.collect.FluentIterable.from; | |||
import static org.sonar.core.rule.RuleKeyFunctions.stringToRuleKey; | |||
/** | |||
* Loads all the project open issues from database, including manual issues. | |||
@@ -52,14 +49,14 @@ public class BaseIssuesLoader { | |||
private final Set<RuleKey> activeRuleKeys; | |||
private final TreeRootHolder treeRootHolder; | |||
private final DbClient dbClient; | |||
private final RuleCache ruleCache; | |||
private final RuleRepository ruleRepository; | |||
public BaseIssuesLoader(BatchReportReader reportReader, TreeRootHolder treeRootHolder, | |||
DbClient dbClient, RuleCache ruleCache) { | |||
this.activeRuleKeys = from(reportReader.readMetadata().getActiveRuleKeyList()).transform(ToRuleKey.INSTANCE).toSet(); | |||
DbClient dbClient, RuleRepository ruleRepository) { | |||
this.activeRuleKeys = from(reportReader.readMetadata().getActiveRuleKeyList()).transform(stringToRuleKey()).toSet(); | |||
this.treeRootHolder = treeRootHolder; | |||
this.dbClient = dbClient; | |||
this.ruleCache = ruleCache; | |||
this.ruleRepository = ruleRepository; | |||
} | |||
public List<DefaultIssue> loadForComponentUuid(String componentUuid) { | |||
@@ -73,15 +70,13 @@ public class BaseIssuesLoader { | |||
DefaultIssue issue = ((IssueDto) resultContext.getResultObject()).toDefaultIssue(); | |||
// TODO this field should be set outside this class | |||
RuleDto rule = ruleCache.getNullable(issue.ruleKey()); | |||
if (rule == null || rule.getStatus() == RuleStatus.REMOVED || !isActive(issue.ruleKey())) { | |||
if (!isActive(issue.ruleKey()) || ruleRepository.getByKey(issue.ruleKey()).getStatus() == RuleStatus.REMOVED) { | |||
issue.setOnDisabledRule(true); | |||
// TODO to be improved, why setOnDisabledRule(true) is not enough ? | |||
issue.setBeingClosed(true); | |||
} | |||
// FIXME | |||
issue.setSelectedAt(System.currentTimeMillis()); | |||
Loggers.get(getClass()).info("Loaded from db: " + issue); | |||
result.add(issue); | |||
} | |||
}); | |||
@@ -98,7 +93,7 @@ public class BaseIssuesLoader { | |||
/** | |||
* Uuids of all the components that have open issues on this project. | |||
*/ | |||
public Set<String> loadComponentUuids() { | |||
public Set<String> loadUuidsOfComponentsWithOpenIssues() { | |||
DbSession session = dbClient.openSession(false); | |||
try { | |||
return dbClient.issueDao().selectComponentUuidsOfOpenIssuesForProjectUuid(session, treeRootHolder.getRoot().getUuid()); | |||
@@ -106,13 +101,4 @@ public class BaseIssuesLoader { | |||
MyBatis.closeQuietly(session); | |||
} | |||
} | |||
private enum ToRuleKey implements Function<String, RuleKey> { | |||
INSTANCE; | |||
@Nonnull | |||
@Override | |||
public RuleKey apply(@Nonnull String input) { | |||
return RuleKey.parse(input); | |||
} | |||
} | |||
} |
@@ -27,7 +27,6 @@ import javax.annotation.Nullable; | |||
import org.sonar.api.measures.CoreMetrics; | |||
import org.sonar.core.issue.DefaultIssue; | |||
import org.sonar.core.issue.tracking.Tracking; | |||
import org.sonar.core.rule.RuleDto; | |||
import org.sonar.server.computation.component.Component; | |||
import org.sonar.server.computation.debt.Characteristic; | |||
import org.sonar.server.computation.debt.DebtModelHolder; | |||
@@ -40,7 +39,7 @@ import static com.google.common.collect.Maps.newHashMap; | |||
public class DebtAggregator extends IssueVisitor { | |||
private final RuleCache ruleCache; | |||
private final RuleRepository ruleRepository; | |||
private final DebtModelHolder debtModelHolder; | |||
private final MetricRepository metricRepository; | |||
private final MeasureRepository measureRepository; | |||
@@ -48,9 +47,9 @@ public class DebtAggregator extends IssueVisitor { | |||
private final Map<Integer, Debt> debtsByComponentRef = new HashMap<>(); | |||
private Debt currentDebt; | |||
public DebtAggregator(RuleCache ruleCache, DebtModelHolder debtModelHolder, | |||
public DebtAggregator(RuleRepository ruleRepository, DebtModelHolder debtModelHolder, | |||
MetricRepository metricRepository, MeasureRepository measureRepository) { | |||
this.ruleCache = ruleCache; | |||
this.ruleRepository = ruleRepository; | |||
this.debtModelHolder = debtModelHolder; | |||
this.metricRepository = metricRepository; | |||
this.measureRepository = measureRepository; | |||
@@ -58,7 +57,8 @@ public class DebtAggregator extends IssueVisitor { | |||
@Override | |||
public void beforeComponent(Component component, Tracking tracking) { | |||
this.currentDebt = new Debt(); | |||
currentDebt = new Debt(); | |||
debtsByComponentRef.put(component.getRef(), currentDebt); | |||
// aggregate children counters | |||
for (Component child : component.getChildren()) { | |||
@@ -79,26 +79,35 @@ public class DebtAggregator extends IssueVisitor { | |||
@Override | |||
public void afterComponent(Component component) { | |||
if (this.currentDebt.minutes > 0L) { | |||
Metric metric = metricRepository.getByKey(CoreMetrics.TECHNICAL_DEBT_KEY); | |||
Metric metric = metricRepository.getByKey(CoreMetrics.TECHNICAL_DEBT_KEY); | |||
// total value | |||
measureRepository.add(component, metric, Measure.newMeasureBuilder().create(this.currentDebt.minutes)); | |||
// total value | |||
measureRepository.add(component, metric, Measure.newMeasureBuilder().create(this.currentDebt.minutes)); | |||
// distribution by rule | |||
for (Map.Entry<Integer, Long> entry : currentDebt.minutesByRuleId.entrySet()) { | |||
int ruleId = entry.getKey(); | |||
long ruleDebt = entry.getValue(); | |||
measureRepository.add(component, metric, Measure.newMeasureBuilder().forRule(ruleId).create(ruleDebt)); | |||
} | |||
// distribution by rule | |||
for (Map.Entry<Integer, Long> entry : currentDebt.minutesByRuleId.entrySet()) { | |||
int ruleId = entry.getKey(); | |||
long ruleDebt = entry.getValue(); | |||
// debt can't be zero. | |||
measureRepository.add(component, metric, Measure.newMeasureBuilder().forRule(ruleId).create(ruleDebt)); | |||
} | |||
// distribution by characteristic/sub-characteristic | |||
for (Map.Entry<Integer, Long> entry : currentDebt.minutesByCharacteristicId.entrySet()) { | |||
int characteristicId = entry.getKey(); | |||
long characteristicDebt = entry.getValue(); | |||
// debt can't be zero | |||
measureRepository.add(component, metric, Measure.newMeasureBuilder().forCharacteristic(characteristicId).create(characteristicDebt)); | |||
} | |||
// distribution by characteristic | |||
for (Map.Entry<Integer, Long> entry : currentDebt.minutesByCharacteristicId.entrySet()) { | |||
int characteristicId = entry.getKey(); | |||
long characteristicDebt = entry.getValue(); | |||
measureRepository.add(component, metric, Measure.newMeasureBuilder().forCharacteristic(characteristicId).create(characteristicDebt)); | |||
if (!component.getType().isDeeperThan(Component.Type.MODULE)) { | |||
for (Characteristic rootCharacteristic : debtModelHolder.getRootCharacteristics()) { | |||
if (currentDebt.minutesByCharacteristicId.get(rootCharacteristic.getId()) == null) { | |||
measureRepository.add(component, metric, Measure.newMeasureBuilder().forCharacteristic(rootCharacteristic.getId()).create(0L)); | |||
} | |||
} | |||
} | |||
this.currentDebt = null; | |||
} | |||
@@ -112,7 +121,7 @@ public class DebtAggregator extends IssueVisitor { | |||
if (issueMinutes != null && issueMinutes != 0L) { | |||
this.minutes += issueMinutes; | |||
RuleDto rule = ruleCache.get(issue.ruleKey()); | |||
Rule rule = ruleRepository.getByKey(issue.ruleKey()); | |||
this.minutesByRuleId.add(rule.getId(), issueMinutes); | |||
Integer subCharacteristicId = rule.getSubCharacteristicId(); |
@@ -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."); | |||
} | |||
} | |||
} |
@@ -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; |
@@ -29,7 +29,6 @@ import org.sonar.server.util.cache.DiskCache; | |||
/** | |||
* Cache of all the issues involved in the analysis. Their state is as it will be | |||
* persisted in database (after issue tracking, auto-assignment, ...) | |||
* | |||
*/ | |||
public class IssueCache extends DiskCache<DefaultIssue> { | |||
@@ -75,7 +75,7 @@ import static org.sonar.api.rule.Severity.MINOR; | |||
*/ | |||
public class IssueCounter extends IssueVisitor { | |||
private final static Map<String, String> SEVERITY_TO_METRIC_KEY = ImmutableMap.of( | |||
private static final Map<String, String> SEVERITY_TO_METRIC_KEY = ImmutableMap.of( | |||
BLOCKER, BLOCKER_VIOLATIONS_KEY, | |||
CRITICAL, CRITICAL_VIOLATIONS_KEY, | |||
MAJOR, MAJOR_VIOLATIONS_KEY, | |||
@@ -83,7 +83,7 @@ public class IssueCounter extends IssueVisitor { | |||
INFO, INFO_VIOLATIONS_KEY | |||
); | |||
private final static Map<String, String> SEVERITY_TO_NEW_METRIC_KEY = ImmutableMap.of( | |||
private static final Map<String, String> SEVERITY_TO_NEW_METRIC_KEY = ImmutableMap.of( | |||
BLOCKER, NEW_BLOCKER_VIOLATIONS_KEY, | |||
CRITICAL, NEW_CRITICAL_VIOLATIONS_KEY, | |||
MAJOR, NEW_MAJOR_VIOLATIONS_KEY, | |||
@@ -99,7 +99,7 @@ public class IssueCounter extends IssueVisitor { | |||
private Counters currentCounters; | |||
public IssueCounter(PeriodsHolder periodsHolder, | |||
MetricRepository metricRepository, MeasureRepository measureRepository) { | |||
MetricRepository metricRepository, MeasureRepository measureRepository) { | |||
this.periodsHolder = periodsHolder; | |||
this.metricRepository = metricRepository; | |||
this.measureRepository = measureRepository; | |||
@@ -173,20 +173,14 @@ public class IssueCounter extends IssueVisitor { | |||
String severity = entry.getKey(); | |||
String metricKey = entry.getValue(); | |||
Double[] variations = new Double[PeriodsHolder.MAX_NUMBER_OF_PERIODS]; | |||
boolean set = false; | |||
for (Period period : periodsHolder.getPeriods()) { | |||
Multiset<String> bag = currentCounters.counterForPeriod(period.getIndex()).severityBag; | |||
if (bag.contains(severity)) { | |||
variations[period.getIndex() - 1] = new Double(bag.count(severity)); | |||
set = true; | |||
} | |||
} | |||
if (set) { | |||
Metric metric = metricRepository.getByKey(metricKey); | |||
measureRepository.add(component, metric, Measure.newMeasureBuilder() | |||
.setVariations(new MeasureVariations(variations)) | |||
.createNoValue()); | |||
variations[period.getIndex() - 1] = new Double(bag.count(severity)); | |||
} | |||
Metric metric = metricRepository.getByKey(metricKey); | |||
measureRepository.add(component, metric, Measure.newMeasureBuilder() | |||
.setVariations(new MeasureVariations(variations)) | |||
.createNoValue()); | |||
} | |||
} | |||
} |
@@ -20,6 +20,7 @@ | |||
package org.sonar.server.computation.issue; | |||
import java.util.Date; | |||
import javax.annotation.Nullable; | |||
import org.sonar.api.issue.Issue; | |||
import org.sonar.api.utils.internal.Uuids; | |||
import org.sonar.core.issue.DefaultIssue; | |||
@@ -28,15 +29,25 @@ import org.sonar.core.issue.IssueUpdater; | |||
import org.sonar.core.issue.workflow.IssueWorkflow; | |||
import org.sonar.server.computation.batch.BatchReportReader; | |||
/** | |||
* Sets the appropriate fields when an issue is : | |||
* <ul> | |||
* <li>newly created</li> | |||
* <li>merged the related base issue</li> | |||
* <li>relocated (only manual issues)</li> | |||
* </ul> | |||
*/ | |||
public class IssueLifecycle { | |||
private final IssueWorkflow workflow; | |||
private final IssueChangeContext changeContext; | |||
private final IssueUpdater updater; | |||
private final DebtCalculator debtCalculator; | |||
public IssueLifecycle(BatchReportReader reportReader, IssueWorkflow workflow, IssueUpdater updater) { | |||
public IssueLifecycle(BatchReportReader reportReader, IssueWorkflow workflow, IssueUpdater updater, DebtCalculator debtCalculator) { | |||
this.workflow = workflow; | |||
this.updater = updater; | |||
this.debtCalculator = debtCalculator; | |||
this.changeContext = IssueChangeContext.createScan(new Date(reportReader.readMetadata().getAnalysisDate())); | |||
} | |||
@@ -45,6 +56,7 @@ public class IssueLifecycle { | |||
issue.setCreationDate(changeContext.date()); | |||
issue.setUpdateDate(changeContext.date()); | |||
issue.setStatus(Issue.STATUS_OPEN); | |||
issue.setDebt(debtCalculator.calculate(issue)); | |||
} | |||
public void mergeExistingOpenIssue(DefaultIssue raw, DefaultIssue base) { | |||
@@ -59,6 +71,8 @@ public class IssueLifecycle { | |||
raw.setAssignee(base.assignee()); | |||
raw.setAuthorLogin(base.authorLogin()); | |||
raw.setTags(base.tags()); | |||
raw.setDebt(debtCalculator.calculate(raw)); | |||
raw.setOnDisabledRule(base.isOnDisabledRule()); | |||
if (base.manualSeverity()) { | |||
raw.setManualSeverity(true); | |||
raw.setSeverity(base.severity()); | |||
@@ -66,7 +80,7 @@ public class IssueLifecycle { | |||
updater.setPastSeverity(raw, base.severity(), changeContext); | |||
} | |||
// TODO attributes + changelog | |||
// TODO attributes | |||
// fields coming from raw | |||
updater.setPastLine(raw, base.getLine()); | |||
@@ -76,6 +90,10 @@ public class IssueLifecycle { | |||
raw.setSelectedAt(base.selectedAt()); | |||
} | |||
public void moveOpenManualIssue(DefaultIssue manualIssue, @Nullable Integer toLine) { | |||
updater.setLine(manualIssue, toLine); | |||
} | |||
public void doAutomaticTransition(DefaultIssue issue) { | |||
workflow.doAutomaticTransition(issue, changeContext); | |||
} |
@@ -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) { | |||
@@ -64,27 +64,27 @@ public class NewDebtAggregator extends IssueVisitor { | |||
public void beforeComponent(Component component, Tracking tracking) { | |||
currentSum = new DebtSum(); | |||
sumsByComponentRef.put(component.getRef(), currentSum); | |||
List<IssueChangeDto> changes = dbClient.issueChangeDao().selectChangelogOfUnresolvedIssuesByComponent(component.getUuid()); | |||
List<IssueChangeDto> changes = dbClient.issueChangeDao().selectChangelogOfNonClosedIssuesByComponent(component.getUuid()); | |||
for (IssueChangeDto change : changes) { | |||
changesByIssueUuid.put(change.getIssueKey(), change); | |||
} | |||
for (Component child : component.getChildren()) { | |||
DebtSum childSum = sumsByComponentRef.remove(child.getRef()); | |||
if (childSum != null) { | |||
currentSum.add(childSum); | |||
} | |||
} | |||
} | |||
@Override | |||
public void onIssue(Component component, DefaultIssue issue) { | |||
if (issue.debtInMinutes() != null && !periodsHolder.getPeriods().isEmpty()) { | |||
if (issue.resolution() == null && issue.debtInMinutes() != null && !periodsHolder.getPeriods().isEmpty()) { | |||
List<IssueChangeDto> changelog = changesByIssueUuid.get(issue.key()); | |||
for (Period period : periodsHolder.getPeriods()) { | |||
long newDebt = calculator.calculate(issue, changelog, period); | |||
currentSum.add(period.getIndex(), newDebt); | |||
} | |||
} | |||
for (Component child : component.getChildren()) { | |||
DebtSum childSum = sumsByComponentRef.remove(child.getRef()); | |||
if (childSum != null) { | |||
currentSum.add(childSum); | |||
} | |||
} | |||
} | |||
@Override | |||
@@ -99,7 +99,7 @@ public class NewDebtAggregator extends IssueVisitor { | |||
} | |||
private static class DebtSum { | |||
private Double[] sums = new Double[PeriodsHolder.MAX_NUMBER_OF_PERIODS]; | |||
private final Double[] sums = new Double[PeriodsHolder.MAX_NUMBER_OF_PERIODS]; | |||
private boolean isEmpty = true; | |||
void add(int periodIndex, long newDebt) { |
@@ -22,8 +22,10 @@ package org.sonar.server.computation.issue; | |||
import com.google.common.base.Function; | |||
import com.google.common.base.Objects; | |||
import com.google.common.base.Predicate; | |||
import com.google.common.collect.Lists; | |||
import com.google.common.collect.Ordering; | |||
import java.util.Calendar; | |||
import java.util.Collection; | |||
import java.util.Comparator; | |||
import java.util.Date; | |||
import java.util.Iterator; | |||
@@ -46,16 +48,22 @@ import static com.google.common.collect.FluentIterable.from; | |||
*/ | |||
public class NewDebtCalculator { | |||
public long calculate(DefaultIssue issue, List<IssueChangeDto> debtChangelog, Period period) { | |||
public long calculate(DefaultIssue issue, Collection<IssueChangeDto> debtChangelog, Period period) { | |||
if (issue.creationDate().getTime() > period.getSnapshotDate() + 1000L) { | |||
return Objects.firstNonNull(issue.debtInMinutes(), 0L); | |||
} | |||
return calculateFromChangelog(issue, debtChangelog, period.getSnapshotDate()); | |||
} | |||
private long calculateFromChangelog(DefaultIssue issue, List<IssueChangeDto> debtChangelog, long periodDate) { | |||
private long calculateFromChangelog(DefaultIssue issue, Collection<IssueChangeDto> debtChangelog, long periodDate) { | |||
List<FieldDiffs> debtDiffs = from(debtChangelog).transform(ToFieldDiffs.INSTANCE).filter(HasDebtChange.INSTANCE).toSortedList(CHANGE_ORDERING); | |||
long newDebt = issue.debtInMinutes().longValue(); | |||
FieldDiffs currentChange = issue.currentChange(); | |||
if (currentChange != null && HasDebtChange.INSTANCE.apply(currentChange)) { | |||
debtDiffs = Lists.newArrayList(debtDiffs); | |||
debtDiffs.add(currentChange); | |||
} | |||
long newDebt = issue.debtInMinutes(); | |||
for (Iterator<FieldDiffs> it = debtDiffs.iterator(); it.hasNext();) { | |||
FieldDiffs diffs = it.next(); | |||
@@ -80,7 +88,7 @@ public class NewDebtCalculator { | |||
@CheckForNull | |||
private static long subtract(long newDebt, @Nullable Long with) { | |||
if (with != null) { | |||
return Math.min(0L, newDebt - with); | |||
return Math.max(0L, newDebt - with); | |||
} | |||
return newDebt; | |||
} | |||
@@ -108,13 +116,7 @@ public class NewDebtCalculator { | |||
INSTANCE; | |||
@Override | |||
public FieldDiffs apply(@Nonnull IssueChangeDto dto) { | |||
FieldDiffs diffs = FieldDiffs.parse(dto.getChangeData()); | |||
diffs.setIssueKey(dto.getIssueKey()); | |||
Long date = dto.getIssueChangeCreationDate(); | |||
if (date != null) { | |||
diffs.setCreationDate(new Date(date)); | |||
} | |||
return diffs; | |||
return dto.toFieldDiffs(); | |||
} | |||
} | |||
@@ -0,0 +1,53 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.server.computation.issue; | |||
import java.util.Set; | |||
import javax.annotation.CheckForNull; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.rule.RuleStatus; | |||
import org.sonar.api.server.debt.DebtRemediationFunction; | |||
public interface Rule { | |||
int getId(); | |||
RuleKey getKey(); | |||
String getName(); | |||
RuleStatus getStatus(); | |||
/** | |||
* Is activated in a Quality Profile associated to the project | |||
*/ | |||
boolean isActivated(); | |||
/** | |||
* Get all tags, whatever system or user tags. | |||
*/ | |||
Set<String> getTags(); | |||
@CheckForNull | |||
Integer getSubCharacteristicId(); | |||
@CheckForNull | |||
DebtRemediationFunction getRemediationFunction(); | |||
} |
@@ -19,36 +19,46 @@ | |||
*/ | |||
package org.sonar.server.computation.issue; | |||
import java.util.Collection; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.core.persistence.DbSession; | |||
import org.sonar.core.persistence.MyBatis; | |||
import org.sonar.core.rule.RuleDto; | |||
import org.sonar.server.computation.batch.BatchReportReader; | |||
import org.sonar.server.db.DbClient; | |||
import org.sonar.server.util.cache.CacheLoader; | |||
import java.util.Collection; | |||
import java.util.Map; | |||
import static com.google.common.collect.FluentIterable.from; | |||
import static org.sonar.core.rule.RuleKeyFunctions.stringToRuleKey; | |||
public class RuleCacheLoader implements CacheLoader<RuleKey, RuleDto> { | |||
public class RuleCacheLoader implements CacheLoader<RuleKey, Rule> { | |||
private final DbClient dbClient; | |||
private final Set<RuleKey> activatedKeys; | |||
public RuleCacheLoader(DbClient dbClient) { | |||
public RuleCacheLoader(DbClient dbClient, BatchReportReader batchReportReader) { | |||
this.dbClient = dbClient; | |||
this.activatedKeys = from(batchReportReader.readMetadata().getActiveRuleKeyList()).transform(stringToRuleKey()).toSet(); | |||
} | |||
@Override | |||
public RuleDto load(RuleKey key) { | |||
public Rule load(RuleKey key) { | |||
DbSession session = dbClient.openSession(false); | |||
try { | |||
return dbClient.ruleDao().getNullableByKey(session, key); | |||
RuleDto dto = dbClient.ruleDao().getNullableByKey(session, key); | |||
if (dto != null) { | |||
return new RuleImpl(dto, activatedKeys.contains(key)); | |||
} | |||
return null; | |||
} finally { | |||
MyBatis.closeQuietly(session); | |||
} | |||
} | |||
@Override | |||
public Map<RuleKey, RuleDto> loadAll(Collection<? extends RuleKey> keys) { | |||
throw new UnsupportedOperationException("See RuleDao"); | |||
public Map<RuleKey, Rule> loadAll(Collection<? extends RuleKey> keys) { | |||
throw new UnsupportedOperationException("Not implemented yet"); | |||
} | |||
} |
@@ -0,0 +1,155 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.server.computation.issue; | |||
import com.google.common.base.Objects; | |||
import java.util.Set; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
import javax.annotation.concurrent.Immutable; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.rule.RuleStatus; | |||
import org.sonar.api.server.debt.DebtRemediationFunction; | |||
import org.sonar.api.server.debt.internal.DefaultDebtRemediationFunction; | |||
import org.sonar.core.rule.RuleDto; | |||
import static com.google.common.collect.Sets.union; | |||
@Immutable | |||
public class RuleImpl implements Rule { | |||
private final int id; | |||
private final RuleKey key; | |||
private final String name; | |||
private final RuleStatus status; | |||
private final boolean isActivated; | |||
private final Integer subCharacteristicId; | |||
private final Set<String> tags; | |||
private final DebtRemediationFunction remediationFunction; | |||
public RuleImpl(RuleDto dto, boolean isActivated) { | |||
this.id = dto.getId(); | |||
this.key = dto.getKey(); | |||
this.name = dto.getName(); | |||
this.status = dto.getStatus(); | |||
this.subCharacteristicId = effectiveCharacteristicId(dto); | |||
this.tags = union(dto.getSystemTags(), dto.getTags()); | |||
this.remediationFunction = effectiveRemediationFunction(dto); | |||
this.isActivated = isActivated; | |||
} | |||
@Override | |||
public int getId() { | |||
return id; | |||
} | |||
@Override | |||
public RuleKey getKey() { | |||
return key; | |||
} | |||
@Override | |||
public String getName() { | |||
return name; | |||
} | |||
@Override | |||
public RuleStatus getStatus() { | |||
return status; | |||
} | |||
@Override | |||
public boolean isActivated() { | |||
return isActivated; | |||
} | |||
@Override | |||
public Set<String> getTags() { | |||
return tags; | |||
} | |||
@Override | |||
public Integer getSubCharacteristicId() { | |||
return subCharacteristicId; | |||
} | |||
@Override | |||
public DebtRemediationFunction getRemediationFunction() { | |||
return remediationFunction; | |||
} | |||
@Override | |||
public boolean equals(@Nullable Object o) { | |||
if (this == o) { | |||
return true; | |||
} | |||
if (o == null || getClass() != o.getClass()) { | |||
return false; | |||
} | |||
RuleImpl rule = (RuleImpl) o; | |||
return key.equals(rule.key); | |||
} | |||
@Override | |||
public int hashCode() { | |||
return key.hashCode(); | |||
} | |||
@Override | |||
public String toString() { | |||
return Objects.toStringHelper(this) | |||
.add("id", id) | |||
.add("key", key) | |||
.add("name", name) | |||
.add("status", status) | |||
.add("isActivated", isActivated) | |||
.add("subCharacteristicId", subCharacteristicId) | |||
.add("tags", tags) | |||
.toString(); | |||
} | |||
@CheckForNull | |||
private static Integer effectiveCharacteristicId(RuleDto dto) { | |||
if (isEnabledCharacteristicId(dto.getSubCharacteristicId())) { | |||
return dto.getSubCharacteristicId(); | |||
} | |||
if (isEnabledCharacteristicId(dto.getDefaultSubCharacteristicId())) { | |||
return dto.getDefaultSubCharacteristicId(); | |||
} | |||
return null; | |||
} | |||
private static boolean isEnabledCharacteristicId(@Nullable Integer id) { | |||
return (id != null) && (id.intValue() != RuleDto.DISABLED_CHARACTERISTIC_ID); | |||
} | |||
@CheckForNull | |||
private static DebtRemediationFunction effectiveRemediationFunction(RuleDto dto) { | |||
String fn = dto.getRemediationFunction(); | |||
if (fn != null) { | |||
return new DefaultDebtRemediationFunction(DebtRemediationFunction.Type.valueOf(fn), dto.getRemediationCoefficient(), dto.getRemediationOffset()); | |||
} | |||
String defaultFn = dto.getDefaultRemediationFunction(); | |||
if (defaultFn != null) { | |||
return new DefaultDebtRemediationFunction(DebtRemediationFunction.Type.valueOf(defaultFn), dto.getDefaultRemediationCoefficient(), dto.getDefaultRemediationOffset()); | |||
} | |||
return null; | |||
} | |||
} |
@@ -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); | |||
} |
@@ -20,23 +20,18 @@ | |||
package org.sonar.server.computation.issue; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.core.rule.RuleDto; | |||
import org.sonar.server.util.cache.MemoryCache; | |||
import javax.annotation.CheckForNull; | |||
public class RuleRepositoryImpl implements RuleRepository { | |||
/** | |||
* Cache of the rules involved during the current analysis | |||
*/ | |||
public class RuleCache extends MemoryCache<RuleKey, RuleDto> { | |||
private final MemoryCache<RuleKey, Rule> cache; | |||
public RuleCache(RuleCacheLoader loader) { | |||
super(loader); | |||
public RuleRepositoryImpl(RuleCacheLoader cacheLoader) { | |||
this.cache = new MemoryCache<>(cacheLoader); | |||
} | |||
@CheckForNull | |||
public String ruleName(RuleKey key) { | |||
RuleDto rule = getNullable(key); | |||
return rule != null ? rule.getName() : null; | |||
@Override | |||
public Rule getByKey(RuleKey key) { | |||
return cache.get(key); | |||
} | |||
} |
@@ -19,28 +19,25 @@ | |||
*/ | |||
package org.sonar.server.computation.issue; | |||
import java.util.Set; | |||
import org.sonar.core.issue.DefaultIssue; | |||
import org.sonar.core.rule.RuleDto; | |||
import org.sonar.server.computation.component.Component; | |||
import static com.google.common.collect.Sets.union; | |||
public class RuleTagsCopier extends IssueVisitor { | |||
private final RuleCache ruleCache; | |||
private final RuleRepository ruleRepository; | |||
public RuleTagsCopier(RuleCache ruleCache) { | |||
this.ruleCache = ruleCache; | |||
public RuleTagsCopier(RuleRepository ruleRepository) { | |||
this.ruleRepository = ruleRepository; | |||
} | |||
@Override | |||
public void onIssue(Component component, DefaultIssue issue) { | |||
if (issue.isNew()) { | |||
// analyzer can provide some tags. They must be merged with rule tags | |||
RuleDto rule = ruleCache.get(issue.ruleKey()); | |||
Set<String> ruleTags = union(rule.getTags(), rule.getSystemTags()); | |||
issue.setTags(union(issue.tags(), ruleTags)); | |||
Rule rule = ruleRepository.getByKey(issue.ruleKey()); | |||
issue.setTags(union(issue.tags(), rule.getTags())); | |||
} | |||
} | |||
} |
@@ -19,35 +19,29 @@ | |||
*/ | |||
package org.sonar.server.computation.issue; | |||
import com.google.common.annotations.VisibleForTesting; | |||
import com.google.common.base.Function; | |||
import com.google.common.base.Joiner; | |||
import com.google.common.collect.Collections2; | |||
import java.util.Collection; | |||
import java.util.List; | |||
import java.util.Map; | |||
import javax.annotation.Nonnull; | |||
import javax.annotation.Nullable; | |||
import org.sonar.api.utils.log.Logger; | |||
import org.sonar.api.utils.log.Loggers; | |||
import org.sonar.server.user.index.UserDoc; | |||
import org.sonar.server.user.index.UserIndex; | |||
import org.sonar.server.util.cache.CacheLoader; | |||
import java.util.Collection; | |||
import java.util.List; | |||
import java.util.Map; | |||
/** | |||
* Loads the association between a SCM account and a SQ user | |||
*/ | |||
public class ScmAccountToUserLoader implements CacheLoader<String, String> { | |||
private final Logger log; | |||
private static final Logger log = Loggers.get(ScmAccountToUserLoader.class); | |||
private final UserIndex index; | |||
public ScmAccountToUserLoader(UserIndex index) { | |||
this(index, Loggers.get(ScmAccountToUserLoader.class)); | |||
} | |||
@VisibleForTesting | |||
ScmAccountToUserLoader(UserIndex index, Logger log) { | |||
this.log = log; | |||
this.index = index; | |||
} | |||
@@ -60,12 +54,7 @@ public class ScmAccountToUserLoader implements CacheLoader<String, String> { | |||
if (!users.isEmpty()) { | |||
// multiple users are associated to the same SCM account, for example | |||
// the same email | |||
Collection<String> logins = Collections2.transform(users, new Function<UserDoc, String>() { | |||
@Override | |||
public String apply(UserDoc input) { | |||
return input.login(); | |||
} | |||
}); | |||
Collection<String> logins = Collections2.transform(users, UserDocToLogin.INSTANCE); | |||
log.warn(String.format("Multiple users share the SCM account '%s': %s", scmAccount, Joiner.on(", ").join(logins))); | |||
} | |||
return null; | |||
@@ -75,4 +64,13 @@ public class ScmAccountToUserLoader implements CacheLoader<String, String> { | |||
public Map<String, String> loadAll(Collection<? extends String> scmAccounts) { | |||
throw new UnsupportedOperationException("Loading by multiple scm accounts is not supported yet"); | |||
} | |||
private enum UserDocToLogin implements Function<UserDoc, String> { | |||
INSTANCE; | |||
@Nullable | |||
@Override | |||
public String apply(@Nonnull UserDoc user) { | |||
return user.login(); | |||
} | |||
} | |||
} |
@@ -26,8 +26,7 @@ import java.util.Collections; | |||
import java.util.List; | |||
import org.sonar.api.issue.Issue; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.utils.Duration; | |||
import org.sonar.api.utils.log.Loggers; | |||
import org.sonar.api.utils.KeyValueFormat; | |||
import org.sonar.batch.protocol.output.BatchReport; | |||
import org.sonar.core.issue.DefaultIssue; | |||
import org.sonar.core.issue.tracking.Input; | |||
@@ -41,12 +40,10 @@ public class TrackerRawInputFactory { | |||
private final TreeRootHolder treeRootHolder; | |||
private final BatchReportReader reportReader; | |||
private final RuleCache ruleCache; | |||
public TrackerRawInputFactory(TreeRootHolder treeRootHolder, BatchReportReader reportReader, RuleCache ruleCache) { | |||
public TrackerRawInputFactory(TreeRootHolder treeRootHolder, BatchReportReader reportReader) { | |||
this.treeRootHolder = treeRootHolder; | |||
this.reportReader = reportReader; | |||
this.ruleCache = ruleCache; | |||
} | |||
public Input<DefaultIssue> create(Component component) { | |||
@@ -80,10 +77,7 @@ public class TrackerRawInputFactory { | |||
LineHashSequence lineHashSeq = getLineHashSequence(); | |||
for (BatchReport.Issue reportIssue : reportIssues) { | |||
DefaultIssue issue = toIssue(lineHashSeq, reportIssue); | |||
if (isValid(issue, lineHashSeq)) { | |||
Loggers.get(getClass()).info("Loaded from report: " + issue); | |||
issues.add(issue); | |||
} | |||
issues.add(issue); | |||
} | |||
} | |||
return issues; | |||
@@ -114,25 +108,11 @@ public class TrackerRawInputFactory { | |||
if (reportIssue.hasEffortToFix()) { | |||
issue.setEffortToFix(reportIssue.getEffortToFix()); | |||
} | |||
if (reportIssue.hasDebtInMinutes()) { | |||
issue.setDebt(Duration.create(reportIssue.getDebtInMinutes())); | |||
} | |||
issue.setTags(Sets.newHashSet(reportIssue.getTagList())); | |||
// TODO issue attributes | |||
if (reportIssue.hasAttributes()) { | |||
issue.setAttributes(KeyValueFormat.parse(reportIssue.getAttributes())); | |||
} | |||
return issue; | |||
} | |||
} | |||
private boolean isValid(DefaultIssue issue, LineHashSequence lineHashSeq) { | |||
// TODO log debug when invalid ? Or throw exception ? | |||
if (ruleCache.getNullable(issue.ruleKey()) == null) { | |||
return false; | |||
} | |||
if (issue.getLine() != null && !lineHashSeq.hasLine(issue.getLine())) { | |||
// FIXME | |||
//return false; | |||
} | |||
return true; | |||
} | |||
} |
@@ -51,7 +51,7 @@ public class PeriodsHolderImpl implements PeriodsHolder { | |||
public void setPeriods(Iterable<Period> periods) { | |||
requireNonNull(periods, "Periods cannot be null"); | |||
checkArgument(Iterables.size(periods) <= MAX_NUMBER_OF_PERIODS, String.format("There can not be more than %d periods", MAX_NUMBER_OF_PERIODS)); | |||
checkState(this.periods == null, "Periods have already been initialized"); | |||
checkState(this.periods == null, "Periods have already been initialized"); | |||
Period[] newPeriods = new Period[MAX_NUMBER_OF_PERIODS]; | |||
for (Period period : from(periods).filter(CheckNotNull.INSTANCE)) { |
@@ -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, |
@@ -61,7 +61,7 @@ public class IntegrateIssuesStep implements ComputationStep { | |||
@Override | |||
public void execute() { | |||
// all the components that had issues before this analysis | |||
final Set<String> unprocessedComponentUuids = Sets.newHashSet(baseIssuesLoader.loadComponentUuids()); | |||
final Set<String> unprocessedComponentUuids = Sets.newHashSet(baseIssuesLoader.loadUuidsOfComponentsWithOpenIssues()); | |||
new DepthTraversalTypeAwareVisitor(Component.Type.FILE, POST_ORDER) { | |||
@Override | |||
@@ -76,8 +76,6 @@ public class IntegrateIssuesStep implements ComputationStep { | |||
private void processIssues(Component component) { | |||
Tracking<DefaultIssue, DefaultIssue> tracking = tracker.track(component); | |||
Loggers.get(getClass()).info("----- tracking ------"); | |||
Loggers.get(getClass()).info("" + tracking); | |||
DiskCache<DefaultIssue>.DiskAppender cacheAppender = issueCache.newAppender(); | |||
try { | |||
issueVisitors.beforeComponent(component, tracking); | |||
@@ -92,44 +90,35 @@ public class IntegrateIssuesStep implements ComputationStep { | |||
private void fillNewOpenIssues(Component component, Tracking<DefaultIssue, DefaultIssue> tracking, DiskCache<DefaultIssue>.DiskAppender cacheAppender) { | |||
Set<DefaultIssue> issues = tracking.getUnmatchedRaws(); | |||
Loggers.get(getClass()).info("----- fillNewOpenIssues on " + component.getKey()); | |||
for (DefaultIssue issue : issues) { | |||
issueLifecycle.initNewOpenIssue(issue); | |||
Loggers.get(getClass()).info("new " + issue); | |||
process(component, issue, cacheAppender); | |||
} | |||
Loggers.get(getClass()).info("----- /fillNewOpenIssues on " + component.getKey()); | |||
} | |||
private void fillExistingOpenIssues(Component component, Tracking<DefaultIssue, DefaultIssue> tracking, DiskCache<DefaultIssue>.DiskAppender cacheAppender) { | |||
Loggers.get(getClass()).info("----- fillExistingOpenIssues on " + component.getKey()); | |||
for (Map.Entry<DefaultIssue, DefaultIssue> entry : tracking.getMatchedRaws().entrySet()) { | |||
DefaultIssue raw = entry.getKey(); | |||
DefaultIssue base = entry.getValue(); | |||
issueLifecycle.mergeExistingOpenIssue(raw, base); | |||
Loggers.get(getClass()).info("merged " + raw); | |||
process(component, raw, cacheAppender); | |||
} | |||
for (Map.Entry<Integer, DefaultIssue> entry : tracking.getOpenManualIssuesByLine().entries()) { | |||
int line = entry.getKey(); | |||
Integer line = entry.getKey(); | |||
DefaultIssue manualIssue = entry.getValue(); | |||
manualIssue.setLine(line == 0 ? null : line); | |||
Loggers.get(getClass()).info("kept manual " + manualIssue); | |||
issueLifecycle.moveOpenManualIssue(manualIssue, line); | |||
process(component, manualIssue, cacheAppender); | |||
} | |||
Loggers.get(getClass()).info("----- /fillExistingOpenIssues on " + component.getKey()); | |||
} | |||
private void closeUnmatchedBaseIssues(Component component, Tracking<DefaultIssue, DefaultIssue> tracking, DiskCache<DefaultIssue>.DiskAppender cacheAppender) { | |||
Loggers.get(getClass()).info("----- closeUnmatchedBaseIssues on " + component.getKey()); | |||
for (DefaultIssue issue : tracking.getUnmatchedBases()) { | |||
Loggers.get(getClass()).info("--- close base " + issue); | |||
// TODO should replace flag "beingClosed" by express call to transition "automaticClose" | |||
issue.setBeingClosed(true); | |||
Loggers.get(getClass()).info("closing " + issue); | |||
// TODO manual issues -> was updater.setResolution(newIssue, Issue.RESOLUTION_REMOVED, changeContext);. Is it a problem ? | |||
process(component, issue, cacheAppender); | |||
} | |||
Loggers.get(getClass()).info("----- /closeUnmatchedBaseIssues on " + component.getKey()); | |||
} | |||
private void process(Component component, DefaultIssue issue, DiskCache<DefaultIssue>.DiskAppender cacheAppender) { | |||
@@ -145,10 +134,9 @@ public class IntegrateIssuesStep implements ComputationStep { | |||
List<DefaultIssue> issues = baseIssuesLoader.loadForComponentUuid(deletedComponentUuid); | |||
for (DefaultIssue issue : issues) { | |||
issue.setBeingClosed(true); | |||
// FIXME should be renamed "setToRemovedStatus" | |||
issue.setOnDisabledRule(true); | |||
// TODO should be renamed | |||
issue.setOnDisabledRule(false); | |||
issueLifecycle.doAutomaticTransition(issue); | |||
// TODO execute visitors ? Component is currently missing. | |||
cacheAppender.append(issue); | |||
} | |||
} |
@@ -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); |
@@ -28,7 +28,7 @@ import org.sonar.server.computation.batch.BatchReportReader; | |||
import org.sonar.server.computation.component.Component; | |||
import org.sonar.server.computation.component.TreeRootHolder; | |||
import org.sonar.server.computation.issue.IssueCache; | |||
import org.sonar.server.computation.issue.RuleCache; | |||
import org.sonar.server.computation.issue.RuleRepository; | |||
import org.sonar.server.issue.notification.IssueChangeNotification; | |||
import org.sonar.server.issue.notification.MyNewIssuesNotification; | |||
import org.sonar.server.issue.notification.NewIssuesNotification; | |||
@@ -49,14 +49,15 @@ public class SendIssueNotificationsStep implements ComputationStep { | |||
static final Set<String> NOTIF_TYPES = ImmutableSet.of(IssueChangeNotification.TYPE, NewIssuesNotification.TYPE, MyNewIssuesNotification.MY_NEW_ISSUES_NOTIF_TYPE); | |||
private final IssueCache issueCache; | |||
private final RuleCache rules; | |||
private final RuleRepository rules; | |||
private final TreeRootHolder treeRootHolder; | |||
private final NotificationService service; | |||
private final BatchReportReader reportReader; | |||
private NewIssuesNotificationFactory newIssuesNotificationFactory; | |||
public SendIssueNotificationsStep(IssueCache issueCache, RuleCache rules, TreeRootHolder treeRootHolder, NotificationService service, | |||
BatchReportReader reportReader, NewIssuesNotificationFactory newIssuesNotificationFactory) { | |||
public SendIssueNotificationsStep(IssueCache issueCache, RuleRepository rules, TreeRootHolder treeRootHolder, | |||
NotificationService service, BatchReportReader reportReader, | |||
NewIssuesNotificationFactory newIssuesNotificationFactory) { | |||
this.issueCache = issueCache; | |||
this.rules = rules; | |||
this.treeRootHolder = treeRootHolder; | |||
@@ -84,7 +85,7 @@ public class SendIssueNotificationsStep implements ComputationStep { | |||
newIssuesStats.add(issue); | |||
} else if (issue.isChanged() && issue.mustSendNotifications()) { | |||
IssueChangeNotification changeNotification = new IssueChangeNotification(); | |||
changeNotification.setRuleName(rules.ruleName(issue.ruleKey())); | |||
changeNotification.setRuleName(rules.getByKey(issue.ruleKey()).getName()); | |||
changeNotification.setIssue(issue); | |||
changeNotification.setProject(project.getKey(), projectName); | |||
service.deliver(changeNotification); |
@@ -50,6 +50,7 @@ import org.sonar.api.utils.System2; | |||
import org.sonar.api.web.UserRole; | |||
import org.sonar.core.component.ComponentDto; | |||
import org.sonar.core.persistence.DbSession; | |||
import org.sonar.core.rule.RuleKeyFunctions; | |||
import org.sonar.server.component.ComponentService; | |||
import org.sonar.server.db.DbClient; | |||
import org.sonar.server.issue.filter.IssueFilterParameters; | |||
@@ -419,12 +420,7 @@ public class IssueQueryService { | |||
@CheckForNull | |||
private static Collection<RuleKey> stringsToRules(@Nullable Collection<String> rules) { | |||
if (rules != null) { | |||
return newArrayList(Iterables.transform(rules, new Function<String, RuleKey>() { | |||
@Override | |||
public RuleKey apply(@Nullable String s) { | |||
return s != null ? RuleKey.parse(s) : null; | |||
} | |||
})); | |||
return newArrayList(Iterables.transform(rules, RuleKeyFunctions.stringToRuleKey())); | |||
} | |||
return null; | |||
} |
@@ -17,129 +17,86 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
///* | |||
// * SonarQube, open source software quality management tool. | |||
// * Copyright (C) 2008-2014 SonarSource | |||
// * mailto:contact AT sonarsource DOT com | |||
// * | |||
// * SonarQube is free software; you can redistribute it and/or | |||
// * modify it under the terms of the GNU Lesser General Public | |||
// * License as published by the Free Software Foundation; either | |||
// * version 3 of the License, or (at your option) any later version. | |||
// * | |||
// * SonarQube is distributed in the hope that it will be useful, | |||
// * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
// * Lesser General Public License for more details. | |||
// * | |||
// * You should have received a copy of the GNU Lesser General Public License | |||
// * along with this program; if not, write to the Free Software Foundation, | |||
// * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
// */ | |||
// | |||
//package org.sonar.server.computation.debt; | |||
// | |||
//import java.util.Arrays; | |||
//import java.util.Collections; | |||
//import org.junit.Rule; | |||
//import org.junit.Test; | |||
//import org.junit.rules.ExpectedException; | |||
// | |||
//import static java.util.Collections.singletonList; | |||
// | |||
//public class DebtModelHolderImplTest { | |||
// | |||
// @Rule | |||
// public ExpectedException thrown = ExpectedException.none(); | |||
// | |||
// private static final Characteristic PORTABILITY = new Characteristic(1, "PORTABILITY", null); | |||
// private static final Characteristic COMPILER_RELATED_PORTABILITY = new Characteristic(2, "COMPILER_RELATED_PORTABILITY", 1); | |||
// private static final Characteristic HARDWARE_RELATED_PORTABILITY = new Characteristic(3, "HARDWARE_RELATED_PORTABILITY", 1); | |||
// | |||
// private static final Characteristic MAINTAINABILITY = new Characteristic(4, "MAINTAINABILITY", null); | |||
// private static final Characteristic READABILITY = new Characteristic(5, "READABILITY", null); | |||
// | |||
// DebtModelHolderImpl sut = new DebtModelHolderImpl(); | |||
// | |||
// @Test | |||
// public void add_characteristics() throws Exception { | |||
// sut.addCharacteristics(PORTABILITY, Arrays.asList(COMPILER_RELATED_PORTABILITY, HARDWARE_RELATED_PORTABILITY)); | |||
// sut.addCharacteristics(MAINTAINABILITY, singletonList(READABILITY)); | |||
// | |||
// assertThat(sut.findRootCharacteristics()).hasSize(2); | |||
// assertThat(sut.findSubCharacteristicsByRootKey("PORTABILITY")).hasSize(2); | |||
// } | |||
// | |||
// @Test | |||
// public void add_characteristics_fail_with_NPE_if_root_characteristic_is_null() throws Exception { | |||
// thrown.expect(NullPointerException.class); | |||
// thrown.expectMessage("rootCharacteristic cannot be null"); | |||
// | |||
// sut.addCharacteristics(null, singletonList(COMPILER_RELATED_PORTABILITY)); | |||
// } | |||
// | |||
// @Test | |||
// public void add_characteristics_fail_with_NPE_if_sub_characteristics_are_null() throws Exception { | |||
// thrown.expect(NullPointerException.class); | |||
// thrown.expectMessage("subCharacteristics cannot be null"); | |||
// | |||
// sut.addCharacteristics(PORTABILITY, null); | |||
// } | |||
// | |||
// @Test | |||
// public void add_characteristics_fail_with_IAE_if_sub_characteristics_are_empty() throws Exception { | |||
// thrown.expect(IllegalArgumentException.class); | |||
// thrown.expectMessage("subCharacteristics cannot be empty"); | |||
// | |||
// sut.addCharacteristics(PORTABILITY, Collections.<Characteristic>emptyList()); | |||
// } | |||
// | |||
// @Test | |||
// public void get_characteristic_by_key() throws Exception { | |||
// sut.addCharacteristics(PORTABILITY, singletonList(COMPILER_RELATED_PORTABILITY)); | |||
// | |||
// assertThat(sut.getCharacteristicByKey("PORTABILITY").get()).isEqualTo(PORTABILITY); | |||
// assertThat(sut.getCharacteristicByKey("COMPILER_RELATED_PORTABILITY").get()).isEqualTo(COMPILER_RELATED_PORTABILITY); | |||
// assertThat(sut.getCharacteristicByKey("UNKNOWN").isPresent()).isFalse(); | |||
// } | |||
// | |||
// @Test | |||
// public void get_characteristic_by_key_throws_ISE_when_not_initialized() throws Exception { | |||
// thrown.expect(IllegalStateException.class); | |||
// thrown.expectMessage("Characteristics have not been initialized yet"); | |||
// | |||
// sut.getCharacteristicByKey("PORTABILITY"); | |||
// } | |||
// | |||
// @Test | |||
// public void get_root_characteristics() throws Exception { | |||
// sut.addCharacteristics(PORTABILITY, Arrays.asList(COMPILER_RELATED_PORTABILITY, READABILITY)); | |||
// sut.addCharacteristics(MAINTAINABILITY, singletonList(READABILITY)); | |||
// | |||
// assertThat(sut.findRootCharacteristics()).hasSize(2); | |||
// } | |||
// | |||
// @Test | |||
// public void get_root_characteristics_throws_ISE_when_not_initialized() throws Exception { | |||
// thrown.expect(IllegalStateException.class); | |||
// thrown.expectMessage("Characteristics have not been initialized yet"); | |||
// | |||
// sut.findRootCharacteristics(); | |||
// } | |||
// | |||
// @Test | |||
// public void get_sub_characteristics_by_root_key() throws Exception { | |||
// sut.addCharacteristics(PORTABILITY, Arrays.asList(COMPILER_RELATED_PORTABILITY, READABILITY)); | |||
// | |||
// assertThat(sut.findSubCharacteristicsByRootKey("PORTABILITY")).hasSize(2); | |||
// assertThat(sut.findSubCharacteristicsByRootKey("UNKNOWN")).isEmpty(); | |||
// } | |||
// | |||
// @Test | |||
// public void get_sub_characteristics_by_root_key_throws_a_ISE_when_not_initialized() throws Exception { | |||
// thrown.expect(IllegalStateException.class); | |||
// thrown.expectMessage("Characteristics have not been initialized yet"); | |||
// | |||
// sut.findSubCharacteristicsByRootKey("PORTABILITY"); | |||
// } | |||
//} | |||
package org.sonar.server.computation.debt; | |||
import java.util.Arrays; | |||
import java.util.Collections; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import static java.util.Collections.singletonList; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
public class DebtModelHolderImplTest { | |||
@Rule | |||
public ExpectedException thrown = ExpectedException.none(); | |||
private static final Characteristic PORTABILITY = new Characteristic(1, "PORTABILITY", null); | |||
private static final Characteristic COMPILER_RELATED_PORTABILITY = new Characteristic(2, "COMPILER_RELATED_PORTABILITY", 1); | |||
private static final Characteristic HARDWARE_RELATED_PORTABILITY = new Characteristic(3, "HARDWARE_RELATED_PORTABILITY", 1); | |||
private static final Characteristic MAINTAINABILITY = new Characteristic(4, "MAINTAINABILITY", null); | |||
private static final Characteristic READABILITY = new Characteristic(5, "READABILITY", null); | |||
DebtModelHolderImpl sut = new DebtModelHolderImpl(); | |||
@Test | |||
public void add_and_get_characteristics() throws Exception { | |||
sut.addCharacteristics(PORTABILITY, Arrays.asList(COMPILER_RELATED_PORTABILITY, HARDWARE_RELATED_PORTABILITY)); | |||
sut.addCharacteristics(MAINTAINABILITY, singletonList(READABILITY)); | |||
assertThat(sut.getRootCharacteristics()).hasSize(2); | |||
assertThat(sut.getCharacteristicById(PORTABILITY.getId()).getKey()).isEqualTo("PORTABILITY"); | |||
assertThat(sut.getCharacteristicById(COMPILER_RELATED_PORTABILITY.getId()).getKey()).isEqualTo("COMPILER_RELATED_PORTABILITY"); | |||
} | |||
@Test | |||
public void add_characteristics_fail_with_NPE_if_root_characteristic_is_null() throws Exception { | |||
thrown.expect(NullPointerException.class); | |||
thrown.expectMessage("rootCharacteristic cannot be null"); | |||
sut.addCharacteristics(null, singletonList(COMPILER_RELATED_PORTABILITY)); | |||
} | |||
@Test | |||
public void add_characteristics_fail_with_NPE_if_sub_characteristics_are_null() throws Exception { | |||
thrown.expect(NullPointerException.class); | |||
thrown.expectMessage("subCharacteristics cannot be null"); | |||
sut.addCharacteristics(PORTABILITY, null); | |||
} | |||
@Test | |||
public void add_characteristics_fail_with_IAE_if_sub_characteristics_are_empty() throws Exception { | |||
thrown.expect(IllegalArgumentException.class); | |||
thrown.expectMessage("subCharacteristics cannot be empty"); | |||
sut.addCharacteristics(PORTABILITY, Collections.<Characteristic>emptyList()); | |||
} | |||
@Test | |||
public void get_root_characteristics() throws Exception { | |||
sut.addCharacteristics(PORTABILITY, Arrays.asList(COMPILER_RELATED_PORTABILITY, READABILITY)); | |||
sut.addCharacteristics(MAINTAINABILITY, singletonList(READABILITY)); | |||
assertThat(sut.getRootCharacteristics()).hasSize(2); | |||
} | |||
@Test | |||
public void getCharacteristicById_throws_ISE_when_not_initialized() throws Exception { | |||
thrown.expect(IllegalStateException.class); | |||
thrown.expectMessage("Characteristics have not been initialized yet"); | |||
sut.getCharacteristicById(1); | |||
} | |||
@Test | |||
public void getRootCharacteristics_throws_ISE_when_not_initialized() throws Exception { | |||
thrown.expect(IllegalStateException.class); | |||
thrown.expectMessage("Characteristics have not been initialized yet"); | |||
sut.getRootCharacteristics(); | |||
} | |||
} |
@@ -0,0 +1,155 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.server.computation.issue; | |||
import com.google.common.base.Optional; | |||
import javax.annotation.CheckForNull; | |||
import org.junit.Test; | |||
import org.sonar.api.measures.CoreMetrics; | |||
import org.sonar.api.utils.Duration; | |||
import org.sonar.core.issue.DefaultIssue; | |||
import org.sonar.core.issue.tracking.Tracking; | |||
import org.sonar.server.computation.component.Component; | |||
import org.sonar.server.computation.component.DumbComponent; | |||
import org.sonar.server.computation.debt.Characteristic; | |||
import org.sonar.server.computation.debt.DebtModelHolderImpl; | |||
import org.sonar.server.computation.debt.MutableDebtModelHolder; | |||
import org.sonar.server.computation.measure.Measure; | |||
import org.sonar.server.computation.measure.MeasureRepositoryRule; | |||
import org.sonar.server.computation.metric.MetricRepositoryRule; | |||
import org.sonar.server.rule.RuleTesting; | |||
import static java.util.Arrays.asList; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Mockito.mock; | |||
import static org.sonar.api.issue.Issue.RESOLUTION_FIXED; | |||
public class DebtAggregatorTest { | |||
/** | |||
* Root characteristic | |||
*/ | |||
public static final int PORTABILITY_ID = 1000; | |||
/** | |||
* Sub-characteristic of {@link #PORTABILITY_ID} | |||
*/ | |||
public static final int PORTABILITY_SOFT_ID = 1001; | |||
/** | |||
* Sub-characteristic of {@link #PORTABILITY_ID} | |||
*/ | |||
public static final int PORTABILITY_HARD_ID = 1002; | |||
/** | |||
* Root characteristic | |||
*/ | |||
public static final int RELIABILITY_ID = 1003; | |||
Component file = DumbComponent.builder(Component.Type.FILE, 1).build(); | |||
Component project = DumbComponent.builder(Component.Type.PROJECT, 2).addChildren(file).build(); | |||
DumbRule rule = new DumbRule(RuleTesting.XOO_X1).setId(100).setSubCharacteristicId(PORTABILITY_SOFT_ID); | |||
@org.junit.Rule | |||
public RuleRepositoryRule ruleRepository = new RuleRepositoryRule().add(rule); | |||
MutableDebtModelHolder debtModelHolder = new DebtModelHolderImpl() | |||
.addCharacteristics(new Characteristic(PORTABILITY_ID, "PORTABILITY", null), | |||
asList(new Characteristic(PORTABILITY_SOFT_ID, "PORTABILITY_HARDWARE", PORTABILITY_ID), new Characteristic(PORTABILITY_HARD_ID, "PORTABILITY_SOFTWARE", PORTABILITY_ID))) | |||
.addCharacteristics(new Characteristic(RELIABILITY_ID, "RELIABILITY", null), | |||
asList(new Characteristic(1004, "DATA_RELIABILITY", RELIABILITY_ID)) | |||
); | |||
@org.junit.Rule | |||
public MetricRepositoryRule metricRepository = new MetricRepositoryRule().add(200, CoreMetrics.TECHNICAL_DEBT); | |||
@org.junit.Rule | |||
public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(); | |||
DebtAggregator underTest = new DebtAggregator(ruleRepository, debtModelHolder, metricRepository, measureRepository); | |||
@Test | |||
public void sum_debt_of_unresolved_issues() throws Exception { | |||
DefaultIssue unresolved1 = new DefaultIssue().setDebt(Duration.create(10)).setRuleKey(rule.getKey()); | |||
DefaultIssue unresolved2 = new DefaultIssue().setDebt(Duration.create(30)).setRuleKey(rule.getKey()); | |||
DefaultIssue unresolvedWithoutDebt = new DefaultIssue().setRuleKey(rule.getKey()); | |||
DefaultIssue resolved = new DefaultIssue().setDebt(Duration.create(50)).setResolution(RESOLUTION_FIXED).setRuleKey(rule.getKey()); | |||
underTest.beforeComponent(file, mock(Tracking.class)); | |||
underTest.onIssue(file, unresolved1); | |||
underTest.onIssue(file, unresolved2); | |||
underTest.onIssue(file, unresolvedWithoutDebt); | |||
underTest.onIssue(file, resolved); | |||
underTest.afterComponent(file); | |||
// total debt | |||
assertThat(debtMeasure(file).get().getLongValue()).isEqualTo(10 + 30); | |||
// debt by rule | |||
assertThat(debtRuleMeasure(file, rule.getId()).get().getLongValue()).isEqualTo(10 + 30); | |||
// debt by characteristic. Root characteristics with zero values are not saved for files. | |||
assertThat(debtCharacteristicMeasure(file, PORTABILITY_ID).get().getLongValue()).isEqualTo(10 + 30); | |||
assertThat(debtCharacteristicMeasure(file, PORTABILITY_SOFT_ID).get().getLongValue()).isEqualTo(10 + 30); | |||
assertThat(debtCharacteristicMeasure(file, PORTABILITY_HARD_ID).isPresent()).isFalse(); | |||
assertThat(debtCharacteristicMeasure(file, RELIABILITY_ID).isPresent()).isFalse(); | |||
} | |||
@Test | |||
public void aggregate_debt_of_children() throws Exception { | |||
DefaultIssue fileIssue = new DefaultIssue().setDebt(Duration.create(10)).setRuleKey(rule.getKey()); | |||
DefaultIssue projectIssue = new DefaultIssue().setDebt(Duration.create(30)).setRuleKey(rule.getKey()); | |||
underTest.beforeComponent(file, mock(Tracking.class)); | |||
underTest.onIssue(file, fileIssue); | |||
underTest.afterComponent(file); | |||
underTest.beforeComponent(project, mock(Tracking.class)); | |||
underTest.onIssue(project, projectIssue); | |||
underTest.afterComponent(project); | |||
// total debt of project | |||
assertThat(debtMeasure(project).get().getLongValue()).isEqualTo(10 + 30); | |||
// debt by rule | |||
assertThat(debtRuleMeasure(project, rule.getId()).get().getLongValue()).isEqualTo(10 + 30); | |||
// debt by characteristic. Root characteristics with zero values are stored for modules and projects. | |||
assertThat(debtCharacteristicMeasure(project, PORTABILITY_ID).get().getLongValue()).isEqualTo(10 + 30); | |||
assertThat(debtCharacteristicMeasure(project, PORTABILITY_SOFT_ID).get().getLongValue()).isEqualTo(10 + 30); | |||
assertThat(debtCharacteristicMeasure(project, PORTABILITY_HARD_ID).isPresent()).isFalse(); | |||
assertThat(debtCharacteristicMeasure(project, RELIABILITY_ID).get().getLongValue()).isZero(); | |||
} | |||
@CheckForNull | |||
private Optional<Measure> debtMeasure(Component component) { | |||
return measureRepository.getRawMeasure(component, metricRepository.getByKey(CoreMetrics.TECHNICAL_DEBT_KEY)); | |||
} | |||
@CheckForNull | |||
private Optional<Measure> debtRuleMeasure(Component component, int ruleId) { | |||
return measureRepository.getRawRuleMeasure(component, metricRepository.getByKey(CoreMetrics.TECHNICAL_DEBT_KEY), ruleId); | |||
} | |||
@CheckForNull | |||
private Optional<Measure> debtCharacteristicMeasure(Component component, int characteristicId) { | |||
return measureRepository.getRawCharacteristicMeasure(component, metricRepository.getByKey(CoreMetrics.TECHNICAL_DEBT_KEY), characteristicId); | |||
} | |||
} |
@@ -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)); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -0,0 +1,118 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.server.computation.issue; | |||
import java.util.HashSet; | |||
import java.util.Set; | |||
import javax.annotation.Nullable; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.rule.RuleStatus; | |||
import org.sonar.api.server.debt.DebtRemediationFunction; | |||
import static java.util.Objects.requireNonNull; | |||
public class DumbRule implements Rule { | |||
private Integer id; | |||
private RuleKey key; | |||
private String name; | |||
private RuleStatus status = RuleStatus.READY; | |||
private boolean isActivated = false; | |||
private Set<String> tags = new HashSet<>(); | |||
private Integer subCharacteristicId; | |||
private DebtRemediationFunction function; | |||
public DumbRule(RuleKey key) { | |||
this.key = key; | |||
} | |||
@Override | |||
public int getId() { | |||
return requireNonNull(id); | |||
} | |||
@Override | |||
public RuleKey getKey() { | |||
return requireNonNull(key); | |||
} | |||
@Override | |||
public String getName() { | |||
return requireNonNull(name); | |||
} | |||
@Override | |||
public RuleStatus getStatus() { | |||
return requireNonNull(status); | |||
} | |||
@Override | |||
public boolean isActivated() { | |||
return isActivated; | |||
} | |||
@Override | |||
public Set<String> getTags() { | |||
return requireNonNull(tags); | |||
} | |||
@Override | |||
public Integer getSubCharacteristicId() { | |||
return subCharacteristicId; | |||
} | |||
@Override | |||
public DebtRemediationFunction getRemediationFunction() { | |||
return function; | |||
} | |||
public DumbRule setId(Integer id) { | |||
this.id = id; | |||
return this; | |||
} | |||
public DumbRule setName(String name) { | |||
this.name = name; | |||
return this; | |||
} | |||
public DumbRule setStatus(RuleStatus status) { | |||
this.status = status; | |||
return this; | |||
} | |||
public DumbRule setIsActivated(boolean isActivated) { | |||
this.isActivated = isActivated; | |||
return this; | |||
} | |||
public DumbRule setSubCharacteristicId(@Nullable Integer subCharacteristicId) { | |||
this.subCharacteristicId = subCharacteristicId; | |||
return this; | |||
} | |||
public DumbRule setFunction(@Nullable DebtRemediationFunction function) { | |||
this.function = function; | |||
return this; | |||
} | |||
public void setTags(Set<String> tags) { | |||
this.tags = tags; | |||
} | |||
} |
@@ -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"); | |||
} | |||
} |
@@ -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) { |
@@ -17,351 +17,98 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
//package org.sonar.server.computation.issue; | |||
// | |||
//import java.util.Date; | |||
//import org.apache.commons.lang.ObjectUtils; | |||
//import org.apache.commons.lang.time.DateUtils; | |||
//import org.junit.Before; | |||
//import org.junit.Test; | |||
//import org.mockito.ArgumentMatcher; | |||
//import org.mockito.Mock; | |||
//import org.sonar.api.CoreProperties; | |||
//import org.sonar.api.batch.DecoratorContext; | |||
//import org.sonar.api.component.ResourcePerspectives; | |||
//import org.sonar.api.config.Settings; | |||
//import org.sonar.api.issue.Issuable; | |||
//import org.sonar.api.issue.Issue; | |||
//import org.sonar.api.measures.CoreMetrics; | |||
//import org.sonar.api.measures.Measure; | |||
//import org.sonar.api.measures.Metric; | |||
//import org.sonar.api.resources.Resource; | |||
//import org.sonar.api.utils.Duration; | |||
//import org.sonar.core.issue.DefaultIssue; | |||
//import org.sonar.core.issue.FieldDiffs; | |||
// | |||
//import static com.google.common.collect.Lists.newArrayList; | |||
//import static org.assertj.core.api.Assertions.assertThat; | |||
//import static org.mockito.Matchers.argThat; | |||
//import static org.mockito.Mockito.mock; | |||
//import static org.mockito.Mockito.never; | |||
//import static org.mockito.Mockito.verify; | |||
//import static org.mockito.Mockito.when; | |||
// | |||
//public class NewDebtAggregatorTest { | |||
// NewDebtDecorator decorator; | |||
// | |||
// @Mock | |||
// TimeMachineConfiguration timeMachineConfiguration; | |||
// | |||
// @Mock | |||
// Resource resource; | |||
// | |||
// @Mock | |||
// Issuable issuable; | |||
// | |||
// @Mock | |||
// DecoratorContext context; | |||
// | |||
// Date rightNow; | |||
// Date elevenDaysAgo; | |||
// Date tenDaysAgo; | |||
// Date nineDaysAgo; | |||
// Date fiveDaysAgo; | |||
// Date fourDaysAgo; | |||
// | |||
// static final int HOURS_IN_DAY = 8; | |||
// | |||
// static final Long ONE_DAY_IN_MINUTES = 1L * HOURS_IN_DAY * 60; | |||
// static final Long TWO_DAYS_IN_MINUTES = 2L * HOURS_IN_DAY * 60; | |||
// static final Long FIVE_DAYS_IN_MINUTES = 5L * HOURS_IN_DAY * 60; | |||
// | |||
// @Before | |||
// public void setup() { | |||
// Settings settings = new Settings(); | |||
// settings.setProperty(CoreProperties.HOURS_IN_DAY, HOURS_IN_DAY); | |||
// | |||
// ResourcePerspectives perspectives = mock(ResourcePerspectives.class); | |||
// when(perspectives.as(Issuable.class, resource)).thenReturn(issuable); | |||
// | |||
// rightNow = new Date(); | |||
// elevenDaysAgo = DateUtils.addDays(rightNow, -11); | |||
// tenDaysAgo = DateUtils.addDays(rightNow, -10); | |||
// nineDaysAgo = DateUtils.addDays(rightNow, -9); | |||
// fiveDaysAgo = DateUtils.addDays(rightNow, -5); | |||
// fourDaysAgo = DateUtils.addDays(rightNow, -4); | |||
// | |||
// when(timeMachineConfiguration.periods()).thenReturn(newArrayList(new Period(1, fiveDaysAgo), new Period(2, tenDaysAgo))); | |||
// | |||
// decorator = new NewDebtDecorator(perspectives, timeMachineConfiguration, new IssueChangelogDebtCalculator()); | |||
// } | |||
// | |||
// @Test | |||
// public void generates_metrics() { | |||
// assertThat(decorator.generatesMetrics()).hasSize(1); | |||
// } | |||
// | |||
// @Test | |||
// public void execute_on_project() { | |||
// assertThat(decorator.shouldExecuteOnProject(null)).isTrue(); | |||
// } | |||
// | |||
// @Test | |||
// public void save_on_one_issue_with_one_new_changelog() { | |||
// Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES)).setChanges( | |||
// newArrayList( | |||
// // changelog created at is null because it has just been created on the current analysis | |||
// new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(null) | |||
// ) | |||
// ); | |||
// when(issuable.issues()).thenReturn(newArrayList(issue)); | |||
// | |||
// decorator.decorate(resource, context); | |||
// | |||
// // remember : period1 is 5daysAgo, period2 is 10daysAgo | |||
// verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 1.0 * ONE_DAY_IN_MINUTES, 1.0 * ONE_DAY_IN_MINUTES))); | |||
// } | |||
// | |||
// @Test | |||
// public void save_on_one_issue_with_changelog() { | |||
// Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges( | |||
// newArrayList( | |||
// new FieldDiffs().setDiff("technicalDebt", TWO_DAYS_IN_MINUTES, FIVE_DAYS_IN_MINUTES).setCreationDate(null), | |||
// new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(fourDaysAgo) | |||
// ) | |||
// ); | |||
// when(issuable.issues()).thenReturn(newArrayList(issue)); | |||
// | |||
// decorator.decorate(resource, context); | |||
// | |||
// // remember : period1 is 5daysAgo, period2 is 10daysAgo | |||
// verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 4.0 * ONE_DAY_IN_MINUTES, 4.0 * ONE_DAY_IN_MINUTES))); | |||
// } | |||
// | |||
// @Test | |||
// public void save_on_one_issue_with_changelog_only_in_the_past() { | |||
// Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(ONE_DAY_IN_MINUTES)).setChanges( | |||
// newArrayList( | |||
// // Change before all periods | |||
// new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(elevenDaysAgo) | |||
// ) | |||
// ); | |||
// when(issuable.issues()).thenReturn(newArrayList(issue)); | |||
// | |||
// decorator.decorate(resource, context); | |||
// | |||
// // remember : period1 is 5daysAgo, period2 is 10daysAgo | |||
// verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 0.0))); | |||
// } | |||
// | |||
// @Test | |||
// public void save_on_one_issue_with_changelog_having_null_value() { | |||
// Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges( | |||
// newArrayList( | |||
// new FieldDiffs().setDiff("technicalDebt", null, FIVE_DAYS_IN_MINUTES).setCreationDate(null), | |||
// new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, null).setCreationDate(fourDaysAgo), | |||
// new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo) | |||
// ) | |||
// ); | |||
// when(issuable.issues()).thenReturn(newArrayList(issue)); | |||
// | |||
// decorator.decorate(resource, context); | |||
// | |||
// // remember : period1 is 5daysAgo, period2 is 10daysAgo | |||
// verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 4.0 * ONE_DAY_IN_MINUTES, 5.0 * ONE_DAY_IN_MINUTES))); | |||
// } | |||
// | |||
// @Test | |||
// public void save_on_one_issue_with_changelog_and_periods_have_no_dates() { | |||
// when(timeMachineConfiguration.periods()).thenReturn(newArrayList(new Period(1, null), new Period(2, null))); | |||
// | |||
// Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges( | |||
// newArrayList( | |||
// new FieldDiffs().setDiff("technicalDebt", null, FIVE_DAYS_IN_MINUTES).setCreationDate(null), | |||
// new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, null).setCreationDate(fourDaysAgo), | |||
// new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo) | |||
// ) | |||
// ); | |||
// when(issuable.issues()).thenReturn(newArrayList(issue)); | |||
// | |||
// decorator.decorate(resource, context); | |||
// | |||
// // remember : period1 is 5daysAgo, period2 is 10daysAgo | |||
// verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 5.0 * ONE_DAY_IN_MINUTES, 5.0 * ONE_DAY_IN_MINUTES))); | |||
// } | |||
// | |||
// @Test | |||
// public void save_on_one_issue_with_changelog_having_not_only_technical_debt_changes() { | |||
// Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges( | |||
// newArrayList( | |||
// new FieldDiffs() | |||
// .setDiff("actionPlan", "1.0", "1.1").setCreationDate(fourDaysAgo) | |||
// .setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(fourDaysAgo) | |||
// ) | |||
// ); | |||
// when(issuable.issues()).thenReturn(newArrayList(issue)); | |||
// | |||
// decorator.decorate(resource, context); | |||
// | |||
// // remember : period1 is 5daysAgo, period2 is 10daysAgo | |||
// verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 4.0 * ONE_DAY_IN_MINUTES, 4.0 * ONE_DAY_IN_MINUTES))); | |||
// } | |||
// | |||
// @Test | |||
// public void save_on_issues_with_changelog() { | |||
// Issue issue1 = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges( | |||
// newArrayList( | |||
// new FieldDiffs().setDiff("technicalDebt", TWO_DAYS_IN_MINUTES, FIVE_DAYS_IN_MINUTES).setCreationDate(rightNow), | |||
// new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(fourDaysAgo), | |||
// new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo) | |||
// ) | |||
// ); | |||
// Issue issue2 = new DefaultIssue().setKey("B").setCreationDate(tenDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES)).setChanges( | |||
// newArrayList( | |||
// new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(rightNow), | |||
// new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo) | |||
// ) | |||
// ); | |||
// when(issuable.issues()).thenReturn(newArrayList(issue1, issue2)); | |||
// | |||
// decorator.decorate(resource, context); | |||
// | |||
// // remember : period1 is 5daysAgo, period2 is 10daysAgo | |||
// verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 5.0 * ONE_DAY_IN_MINUTES, 7.0 * ONE_DAY_IN_MINUTES))); | |||
// } | |||
// | |||
// @Test | |||
// public void save_on_one_issue_without_changelog() { | |||
// when(issuable.issues()).thenReturn(newArrayList( | |||
// (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES))) | |||
// ); | |||
// | |||
// decorator.decorate(resource, context); | |||
// | |||
// // remember : period1 is 5daysAgo, period2 is 10daysAgo | |||
// verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 5.0 * ONE_DAY_IN_MINUTES))); | |||
// } | |||
// | |||
// @Test | |||
// public void save_on_one_issue_without_technical_debt_and_without_changelog() { | |||
// when(issuable.issues()).thenReturn(newArrayList( | |||
// (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(null)) | |||
// ); | |||
// | |||
// decorator.decorate(resource, context); | |||
// | |||
// // remember : period1 is 5daysAgo, period2 is 10daysAgo | |||
// verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 0.0))); | |||
// } | |||
// | |||
// @Test | |||
// public void save_on_one_issue_without_changelog_and_periods_have_no_dates() { | |||
// when(timeMachineConfiguration.periods()).thenReturn(newArrayList(new Period(1, null), new Period(2, null))); | |||
// | |||
// when(issuable.issues()).thenReturn(newArrayList( | |||
// (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES))) | |||
// ); | |||
// | |||
// decorator.decorate(resource, context); | |||
// | |||
// // remember : period1 is null, period2 is null | |||
// verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 5.0 * ONE_DAY_IN_MINUTES, 5.0 * ONE_DAY_IN_MINUTES))); | |||
// } | |||
// | |||
// @Test | |||
// public void save_on_issues_without_changelog() { | |||
// when(issuable.issues()).thenReturn(newArrayList( | |||
// (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)), | |||
// new DefaultIssue().setKey("B").setCreationDate(fiveDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES)) | |||
// )); | |||
// | |||
// decorator.decorate(resource, context); | |||
// | |||
// // remember : period1 is 5daysAgo, period2 is 10daysAgo | |||
// verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 7.0 * ONE_DAY_IN_MINUTES))); | |||
// } | |||
// | |||
// @Test | |||
// public void save_on_issues_with_changelog_and_issues_without_changelog() { | |||
// // issue1 and issue2 have changelog | |||
// Issue issue1 = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)).setChanges( | |||
// newArrayList( | |||
// new FieldDiffs().setDiff("technicalDebt", TWO_DAYS_IN_MINUTES, FIVE_DAYS_IN_MINUTES).setCreationDate(rightNow), | |||
// new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(fourDaysAgo), | |||
// new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo) | |||
// ) | |||
// ); | |||
// Issue issue2 = new DefaultIssue().setKey("B").setCreationDate(tenDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES)).setChanges( | |||
// newArrayList( | |||
// new FieldDiffs().setDiff("technicalDebt", ONE_DAY_IN_MINUTES, TWO_DAYS_IN_MINUTES).setCreationDate(rightNow), | |||
// new FieldDiffs().setDiff("technicalDebt", null, ONE_DAY_IN_MINUTES).setCreationDate(nineDaysAgo) | |||
// ) | |||
// ); | |||
// | |||
// // issue3 and issue4 have no changelog | |||
// Issue issue3 = new DefaultIssue().setKey("C").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)); | |||
// Issue issue4 = new DefaultIssue().setKey("D").setCreationDate(fiveDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES)); | |||
// when(issuable.issues()).thenReturn(newArrayList(issue1, issue2, issue3, issue4)); | |||
// | |||
// decorator.decorate(resource, context); | |||
// | |||
// // remember : period1 is 5daysAgo, period2 is 10daysAgo | |||
// verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 5.0 * ONE_DAY_IN_MINUTES, 14.0 * ONE_DAY_IN_MINUTES))); | |||
// } | |||
// | |||
// @Test | |||
// public void not_save_if_measure_already_computed() { | |||
// when(context.getMeasure(CoreMetrics.NEW_TECHNICAL_DEBT)).thenReturn(new Measure()); | |||
// when(issuable.issues()).thenReturn(newArrayList( | |||
// (Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setDebt(Duration.create(FIVE_DAYS_IN_MINUTES)), | |||
// new DefaultIssue().setKey("B").setCreationDate(fiveDaysAgo).setDebt(Duration.create(TWO_DAYS_IN_MINUTES)) | |||
// )); | |||
// | |||
// decorator.decorate(resource, context); | |||
// | |||
// verify(context, never()).saveMeasure(argThat(new IsMeasure(CoreMetrics.NEW_TECHNICAL_DEBT))); | |||
// } | |||
// | |||
// /** | |||
// * SONAR-5059 | |||
// */ | |||
// @Test | |||
// public void not_return_negative_debt() { | |||
// Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(Duration.create(ONE_DAY_IN_MINUTES)).setChanges( | |||
// newArrayList( | |||
// // changelog created at is null because it has just been created on the current analysis | |||
// new FieldDiffs().setDiff("technicalDebt", TWO_DAYS_IN_MINUTES, ONE_DAY_IN_MINUTES).setCreationDate(null) | |||
// ) | |||
// ); | |||
// when(issuable.issues()).thenReturn(newArrayList(issue)); | |||
// | |||
// decorator.decorate(resource, context); | |||
// | |||
// // remember : period1 is 5daysAgo, period2 is 10daysAgo | |||
// verify(context).saveMeasure(argThat(new IsVariationMeasure(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0, 0.0))); | |||
// } | |||
// | |||
// | |||
// class IsVariationMeasure extends ArgumentMatcher<Measure> { | |||
// Metric metric = null; | |||
// Double var1 = null; | |||
// Double var2 = null; | |||
// | |||
// public IsVariationMeasure(Metric metric, Double var1, Double var2) { | |||
// this.metric = metric; | |||
// this.var1 = var1; | |||
// this.var2 = var2; | |||
// } | |||
// | |||
// public boolean matches(Object o) { | |||
// if (!(o instanceof Measure)) { | |||
// return false; | |||
// } | |||
// Measure m = (Measure) o; | |||
// return ObjectUtils.equals(metric, m.getMetric()) && | |||
// ObjectUtils.equals(var1, m.getVariation1()) && | |||
// ObjectUtils.equals(var2, m.getVariation2()); | |||
// } | |||
// } | |||
// | |||
//} | |||
package org.sonar.server.computation.issue; | |||
import com.google.common.base.Optional; | |||
import org.junit.Test; | |||
import org.mockito.Mockito; | |||
import org.sonar.api.measures.CoreMetrics; | |||
import org.sonar.api.utils.Duration; | |||
import org.sonar.core.issue.DefaultIssue; | |||
import org.sonar.core.issue.tracking.Tracking; | |||
import org.sonar.server.computation.component.Component; | |||
import org.sonar.server.computation.component.DumbComponent; | |||
import org.sonar.server.computation.measure.Measure; | |||
import org.sonar.server.computation.measure.MeasureRepositoryRule; | |||
import org.sonar.server.computation.metric.MetricRepositoryRule; | |||
import org.sonar.server.computation.period.Period; | |||
import org.sonar.server.computation.period.PeriodsHolderRule; | |||
import org.sonar.server.db.DbClient; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Matchers.anyList; | |||
import static org.mockito.Matchers.same; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.verifyNoMoreInteractions; | |||
import static org.mockito.Mockito.verifyZeroInteractions; | |||
import static org.mockito.Mockito.when; | |||
import static org.sonar.api.CoreProperties.TIMEMACHINE_MODE_PREVIOUS_ANALYSIS; | |||
import static org.sonar.api.issue.Issue.RESOLUTION_FIXED; | |||
public class NewDebtAggregatorTest { | |||
private static final Period PERIOD = new Period(1, TIMEMACHINE_MODE_PREVIOUS_ANALYSIS, null, 1_500_000_000L, 1000L); | |||
Component file = DumbComponent.builder(Component.Type.FILE, 1).setUuid("FILE").build(); | |||
Component project = DumbComponent.builder(Component.Type.PROJECT, 2).setUuid("PROJECT").addChildren(file).build(); | |||
NewDebtCalculator calculator = mock(NewDebtCalculator.class); | |||
@org.junit.Rule | |||
public PeriodsHolderRule periodsHolder = new PeriodsHolderRule(); | |||
DbClient dbClient = mock(DbClient.class, Mockito.RETURNS_DEEP_STUBS); | |||
@org.junit.Rule | |||
public MetricRepositoryRule metricRepository = new MetricRepositoryRule().add(CoreMetrics.NEW_TECHNICAL_DEBT); | |||
@org.junit.Rule | |||
public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(); | |||
NewDebtAggregator underTest = new NewDebtAggregator(calculator, periodsHolder, dbClient, metricRepository, measureRepository); | |||
@Test | |||
public void sum_new_debt_of_issues() { | |||
periodsHolder.setPeriods(PERIOD); | |||
DefaultIssue unresolved1 = new DefaultIssue().setDebt(Duration.create(10)); | |||
DefaultIssue unresolved2 = new DefaultIssue().setDebt(Duration.create(30)); | |||
DefaultIssue unresolvedWithoutDebt = new DefaultIssue().setDebt(null); | |||
DefaultIssue resolved = new DefaultIssue().setDebt(Duration.create(50)).setResolution(RESOLUTION_FIXED); | |||
when(calculator.calculate(same(unresolved1), anyList(), same(PERIOD))).thenReturn(4L); | |||
when(calculator.calculate(same(unresolved2), anyList(), same(PERIOD))).thenReturn(3L); | |||
verifyNoMoreInteractions(calculator); | |||
underTest.beforeComponent(file, mock(Tracking.class)); | |||
underTest.onIssue(file, unresolved1); | |||
underTest.onIssue(file, unresolved2); | |||
underTest.onIssue(file, unresolvedWithoutDebt); | |||
underTest.onIssue(file, resolved); | |||
underTest.afterComponent(file); | |||
Measure newDebtMeasure = newDebtMeasure(file).get(); | |||
assertThat(newDebtMeasure.getVariations().getVariation(PERIOD.getIndex())).isEqualTo(3 + 4); | |||
assertThat(newDebtMeasure.getVariations().hasVariation(PERIOD.getIndex() + 1)).isFalse(); | |||
} | |||
private Optional<Measure> newDebtMeasure(Component component) { | |||
return measureRepository.getRawMeasure(component, metricRepository.getByKey(CoreMetrics.NEW_TECHNICAL_DEBT_KEY)); | |||
} | |||
@Test | |||
public void aggregate_new_debt_of_children() { | |||
} | |||
@Test | |||
public void no_measures_if_no_periods() throws Exception { | |||
periodsHolder.setPeriods(); | |||
DefaultIssue unresolved = new DefaultIssue().setDebt(Duration.create(10)); | |||
verifyZeroInteractions(calculator); | |||
underTest.beforeComponent(file, mock(Tracking.class)); | |||
underTest.onIssue(file, unresolved); | |||
underTest.afterComponent(file); | |||
assertThat(newDebtMeasure(file).isPresent()).isFalse(); | |||
} | |||
} |
@@ -17,130 +17,109 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
//package org.sonar.server.computation.issue; | |||
// | |||
//import java.util.Date; | |||
//import org.apache.commons.lang.time.DateUtils; | |||
//import org.junit.Before; | |||
//import org.junit.Test; | |||
//import org.sonar.api.issue.Issue; | |||
//import org.sonar.api.utils.Duration; | |||
//import org.sonar.core.issue.DefaultIssue; | |||
//import org.sonar.core.issue.FieldDiffs; | |||
// | |||
//import static com.google.common.collect.Lists.newArrayList; | |||
//import static org.assertj.core.api.Assertions.assertThat; | |||
// | |||
//public class NewDebtCalculatorTest { | |||
// private static final int HOURS_IN_DAY = 8; | |||
// | |||
// IssueChangelogDebtCalculator issueChangelogDebtCalculator; | |||
// | |||
// Date rightNow = new Date(); | |||
// Date elevenDaysAgo = DateUtils.addDays(rightNow, -11); | |||
// Date tenDaysAgo = DateUtils.addDays(rightNow, -10); | |||
// Date nineDaysAgo = DateUtils.addDays(rightNow, -9); | |||
// Date fiveDaysAgo = DateUtils.addDays(rightNow, -5); | |||
// Date fourDaysAgo = DateUtils.addDays(rightNow, -4); | |||
// | |||
// long oneDay = 1 * HOURS_IN_DAY * 60 * 60L; | |||
// long twoDays = 2 * HOURS_IN_DAY * 60 * 60L; | |||
// long fiveDays = 5 * HOURS_IN_DAY * 60 * 60L; | |||
// | |||
// Duration oneDayDebt = Duration.create(oneDay); | |||
// Duration twoDaysDebt = Duration.create(twoDays); | |||
// Duration fiveDaysDebt = Duration.create(fiveDays); | |||
// | |||
// @Before | |||
// public void setUp() { | |||
// issueChangelogDebtCalculator = new IssueChangelogDebtCalculator(); | |||
// } | |||
// | |||
// @Test | |||
// public void calculate_new_technical_debt_with_one_diff_in_changelog() { | |||
// Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(twoDaysDebt).setChanges( | |||
// newArrayList( | |||
// // changelog created at is null because it has just been created on the current analysis | |||
// new FieldDiffs().setDiff("technicalDebt", oneDay, twoDays).setCreationDate(null) | |||
// ) | |||
// ); | |||
// | |||
// assertThat(issueChangelogDebtCalculator.calculateNewTechnicalDebt(issue, rightNow)).isEqualTo(oneDay); | |||
// assertThat(issueChangelogDebtCalculator.calculateNewTechnicalDebt(issue, fiveDaysAgo)).isEqualTo(oneDay); | |||
// | |||
// assertThat(issueChangelogDebtCalculator.calculateNewTechnicalDebt(issue, elevenDaysAgo)).isEqualTo(twoDays); | |||
// } | |||
// | |||
// @Test | |||
// public void calculate_new_technical_debt_with_many_diffs_in_changelog() { | |||
// Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(fiveDaysDebt).setChanges( | |||
// newArrayList( | |||
// new FieldDiffs().setDiff("technicalDebt", twoDays, fiveDays).setCreationDate(null), | |||
// new FieldDiffs().setDiff("technicalDebt", oneDay, twoDays).setCreationDate(fourDaysAgo) | |||
// ) | |||
// ); | |||
// | |||
// assertThat(issueChangelogDebtCalculator.calculateNewTechnicalDebt(issue, rightNow)).isEqualTo(3 * oneDay); | |||
// assertThat(issueChangelogDebtCalculator.calculateNewTechnicalDebt(issue, fiveDaysAgo)).isEqualTo(4 * oneDay); | |||
// assertThat(issueChangelogDebtCalculator.calculateNewTechnicalDebt(issue, elevenDaysAgo)).isEqualTo(5 * oneDay); | |||
// } | |||
// | |||
// @Test | |||
// public void changelog_can_be_in_wrong_order() { | |||
// Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(fiveDaysDebt).setChanges( | |||
// newArrayList( | |||
// // 3rd | |||
// new FieldDiffs().setDiff("technicalDebt", null, oneDay).setCreationDate(nineDaysAgo), | |||
// // 1st | |||
// new FieldDiffs().setDiff("technicalDebt", twoDays, fiveDays).setCreationDate(rightNow), | |||
// // 2nd | |||
// new FieldDiffs().setDiff("technicalDebt", oneDay, twoDays).setCreationDate(fourDaysAgo) | |||
// ) | |||
// ); | |||
// | |||
// assertThat(issueChangelogDebtCalculator.calculateNewTechnicalDebt(issue, fiveDaysAgo)).isEqualTo(4 * oneDay); | |||
// assertThat(issueChangelogDebtCalculator.calculateNewTechnicalDebt(issue, elevenDaysAgo)).isEqualTo(5 * oneDay); | |||
// } | |||
// | |||
// @Test | |||
// public void calculate_new_technical_debt_with_null_date() { | |||
// Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(twoDaysDebt).setChanges( | |||
// newArrayList( | |||
// new FieldDiffs().setDiff("technicalDebt", oneDay, twoDays).setCreationDate(null) | |||
// ) | |||
// ); | |||
// | |||
// assertThat(issueChangelogDebtCalculator.calculateNewTechnicalDebt(issue, null)).isEqualTo(2 * oneDay); | |||
// } | |||
// | |||
// @Test | |||
// public void calculate_new_technical_debt_when_new_debt_is_null() { | |||
// Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(null).setChanges( | |||
// newArrayList( | |||
// new FieldDiffs().setDiff("technicalDebt", oneDay, null).setCreationDate(null), | |||
// new FieldDiffs().setDiff("technicalDebt", null, oneDay).setCreationDate(nineDaysAgo) | |||
// ) | |||
// ); | |||
// | |||
// assertThat(issueChangelogDebtCalculator.calculateNewTechnicalDebt(issue, rightNow)).isNull(); | |||
// } | |||
// | |||
// @Test | |||
// public void calculate_new_technical_debt_on_issue_without_technical_debt_and_without_changelog() { | |||
// Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo); | |||
// | |||
// assertThat(issueChangelogDebtCalculator.calculateNewTechnicalDebt(issue, rightNow)).isNull(); | |||
// } | |||
// | |||
// @Test | |||
// public void not_return_negative_debt() { | |||
// Issue issue = new DefaultIssue().setKey("A").setCreationDate(tenDaysAgo).setDebt(oneDayDebt).setChanges( | |||
// newArrayList( | |||
// new FieldDiffs().setDiff("technicalDebt", twoDays, oneDay).setCreationDate(null) | |||
// ) | |||
// ); | |||
// | |||
// assertThat(issueChangelogDebtCalculator.calculateNewTechnicalDebt(issue, rightNow)).isNull(); | |||
// } | |||
// | |||
//} | |||
package org.sonar.server.computation.issue; | |||
import java.util.Arrays; | |||
import java.util.Collections; | |||
import java.util.Date; | |||
import java.util.List; | |||
import javax.annotation.Nullable; | |||
import org.junit.Test; | |||
import org.sonar.api.CoreProperties; | |||
import org.sonar.api.utils.Duration; | |||
import org.sonar.core.issue.DefaultIssue; | |||
import org.sonar.core.issue.FieldDiffs; | |||
import org.sonar.core.issue.db.IssueChangeDto; | |||
import org.sonar.server.computation.period.Period; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
public class NewDebtCalculatorTest { | |||
private static final int HOURS_IN_DAY = 8; | |||
private static final Duration ONE_DAY = Duration.create(HOURS_IN_DAY * 60 * 60L); | |||
private static final Duration TWO_DAYS = Duration.create(2 * HOURS_IN_DAY * 60 * 60L); | |||
private static final Duration FOUR_DAYS = Duration.create(4 * HOURS_IN_DAY * 60 * 60L); | |||
private static final Duration FIVE_DAYS = Duration.create(5 * HOURS_IN_DAY * 60 * 60L); | |||
private static final Duration TEN_DAYS = Duration.create(10 * HOURS_IN_DAY * 60 * 60L); | |||
private static final long PERIOD_DATE = 150000000L; | |||
private static final long SNAPSHOT_ID = 1000L; | |||
private static final Period PERIOD = new Period(1, CoreProperties.TIMEMACHINE_MODE_PREVIOUS_VERSION, null, PERIOD_DATE, SNAPSHOT_ID); | |||
DefaultIssue issue = new DefaultIssue(); | |||
NewDebtCalculator underTest = new NewDebtCalculator(); | |||
/** | |||
* New debt is the value of the debt when issue is created during the period | |||
*/ | |||
@Test | |||
public void total_debt_if_issue_created_during_period() { | |||
issue.setDebt(TWO_DAYS).setCreationDate(new Date(PERIOD_DATE + 10000)); | |||
long newDebt = underTest.calculate(issue, Collections.<IssueChangeDto>emptyList(), PERIOD); | |||
assertThat(newDebt).isEqualTo(TWO_DAYS.toMinutes()); | |||
} | |||
@Test | |||
public void new_debt_if_issue_created_before_period() throws Exception { | |||
// creation: 1d | |||
// before period: increased to 2d | |||
// after period: increased to 5d, decreased to 4d then increased to 10d | |||
// -> new debt is 10d - 2d = 8d | |||
issue.setDebt(TEN_DAYS).setCreationDate(new Date(PERIOD_DATE - 10000)); | |||
List<IssueChangeDto> changelog = Arrays.asList( | |||
newDebtChangelog(ONE_DAY.toMinutes(), TWO_DAYS.toMinutes(), PERIOD_DATE - 9000), | |||
newDebtChangelog(TWO_DAYS.toMinutes(), FIVE_DAYS.toMinutes(), PERIOD_DATE + 10000), | |||
newDebtChangelog(FIVE_DAYS.toMinutes(), FOUR_DAYS.toMinutes(), PERIOD_DATE + 20000), | |||
newDebtChangelog(FOUR_DAYS.toMinutes(), TEN_DAYS.toMinutes(), PERIOD_DATE + 30000) | |||
); | |||
long newDebt = underTest.calculate(issue, changelog, PERIOD); | |||
assertThat(newDebt).isEqualTo(TEN_DAYS.toMinutes() - TWO_DAYS.toMinutes()); | |||
} | |||
@Test | |||
public void new_debt_is_positive() throws Exception { | |||
// creation: 1d | |||
// before period: increased to 10d | |||
// after period: decreased to 2d | |||
// -> new debt is 2d - 10d = -8d -> 0d | |||
issue.setDebt(TWO_DAYS).setCreationDate(new Date(PERIOD_DATE - 10000)); | |||
List<IssueChangeDto> changelog = Arrays.asList( | |||
newDebtChangelog(ONE_DAY.toMinutes(), TEN_DAYS.toMinutes(), PERIOD_DATE - 9000), | |||
newDebtChangelog(TEN_DAYS.toMinutes(), TWO_DAYS.toMinutes(), PERIOD_DATE + 30000) | |||
); | |||
long newDebt = underTest.calculate(issue, changelog, PERIOD); | |||
assertThat(newDebt).isEqualTo(0L); | |||
} | |||
@Test | |||
public void guess_initial_debt_when_first_change_is_after_period() throws Exception { | |||
// creation: 1d | |||
// after period: increased to 2d, then to 5d | |||
// -> new debt is 5d - 1d = 4d | |||
issue.setDebt(FIVE_DAYS).setCreationDate(new Date(PERIOD_DATE - 10000)); | |||
List<IssueChangeDto> changelog = Arrays.asList( | |||
newDebtChangelog(ONE_DAY.toMinutes(), TWO_DAYS.toMinutes(), PERIOD_DATE + 20000), | |||
newDebtChangelog(TWO_DAYS.toMinutes(), FIVE_DAYS.toMinutes(), PERIOD_DATE + 30000) | |||
); | |||
long newDebt = underTest.calculate(issue, changelog, PERIOD); | |||
assertThat(newDebt).isEqualTo(FIVE_DAYS.toMinutes() - ONE_DAY.toMinutes()); | |||
} | |||
private static IssueChangeDto newDebtChangelog(long previousValue, long value, @Nullable Long date) { | |||
FieldDiffs diffs = new FieldDiffs().setDiff("technicalDebt", previousValue, value); | |||
if (date != null) { | |||
diffs.setCreationDate(new Date(date)); | |||
} | |||
return new IssueChangeDto().setIssueChangeCreationDate(date).setChangeData(diffs.toString()); | |||
} | |||
} |
@@ -19,26 +19,34 @@ | |||
*/ | |||
package org.sonar.server.computation.issue; | |||
import java.util.Collections; | |||
import org.junit.Before; | |||
import org.junit.ClassRule; | |||
import org.junit.Test; | |||
import org.junit.experimental.categories.Category; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.utils.System2; | |||
import org.sonar.batch.protocol.output.BatchReport; | |||
import org.sonar.core.persistence.DbTester; | |||
import org.sonar.server.computation.batch.BatchReportReaderRule; | |||
import org.sonar.server.db.DbClient; | |||
import org.sonar.server.rule.db.RuleDao; | |||
import org.sonar.test.DbTests; | |||
import java.util.Collections; | |||
import static java.util.Arrays.asList; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.junit.Assert.fail; | |||
import static org.mockito.Mockito.mock; | |||
@Category(DbTests.class) | |||
public class RuleCacheLoaderTest { | |||
@ClassRule | |||
public static DbTester dbTester = new DbTester(); | |||
@org.junit.Rule | |||
public BatchReportReaderRule reportReader = new BatchReportReaderRule(); | |||
@Before | |||
public void setUp() { | |||
dbTester.truncateTables(); | |||
@@ -46,18 +54,31 @@ public class RuleCacheLoaderTest { | |||
@Test | |||
public void load_by_key() { | |||
BatchReport.Metadata metadata = BatchReport.Metadata.newBuilder() | |||
.addAllActiveRuleKey(asList("java:JAV01")).build(); | |||
reportReader.setMetadata(metadata); | |||
dbTester.prepareDbUnit(getClass(), "shared.xml"); | |||
DbClient dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new RuleDao(mock(System2.class))); | |||
RuleCacheLoader loader = new RuleCacheLoader(dbClient); | |||
RuleCacheLoader loader = new RuleCacheLoader(dbClient, reportReader); | |||
Rule javaRule = loader.load(RuleKey.of("java", "JAV01")); | |||
assertThat(javaRule.getName()).isEqualTo("Java One"); | |||
assertThat(javaRule.isActivated()).isTrue(); | |||
assertThat(loader.load(RuleKey.of("squid", "R001")).getName()).isEqualTo("Rule One"); | |||
assertThat(loader.load(RuleKey.of("squid", "MISSING"))).isNull(); | |||
Rule jsRule = loader.load(RuleKey.of("js", "JS01")); | |||
assertThat(jsRule.getName()).isEqualTo("JS One"); | |||
assertThat(jsRule.isActivated()).isFalse(); | |||
assertThat(loader.load(RuleKey.of("java", "MISSING"))).isNull(); | |||
} | |||
@Test | |||
public void load_by_keys_is_not_supported() { | |||
reportReader.setMetadata(BatchReport.Metadata.newBuilder().build()); | |||
DbClient dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new RuleDao(mock(System2.class))); | |||
RuleCacheLoader loader = new RuleCacheLoader(dbClient); | |||
RuleCacheLoader loader = new RuleCacheLoader(dbClient, reportReader); | |||
try { | |||
loader.loadAll(Collections.<RuleKey>emptyList()); | |||
fail(); |
@@ -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); | |||
} | |||
} |
@@ -0,0 +1,59 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.server.computation.issue; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import org.junit.rules.ExternalResource; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.server.exceptions.NotFoundException; | |||
import static java.util.Objects.requireNonNull; | |||
public class RuleRepositoryRule extends ExternalResource implements RuleRepository { | |||
private final Map<RuleKey, Rule> rulesByKey = new HashMap<>(); | |||
@Override | |||
protected void after() { | |||
rulesByKey.clear(); | |||
} | |||
@Override | |||
public Rule getByKey(RuleKey key) { | |||
Rule rule = rulesByKey.get(key); | |||
if (rule == null) { | |||
throw new NotFoundException(); | |||
} | |||
return rule; | |||
} | |||
public DumbRule add(RuleKey key) { | |||
DumbRule rule = new DumbRule(key); | |||
rulesByKey.put(key, rule); | |||
return rule; | |||
} | |||
public RuleRepositoryRule add(DumbRule rule) { | |||
rulesByKey.put(rule.getKey(), rule); | |||
return this; | |||
} | |||
} |
@@ -0,0 +1,72 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.server.computation.issue; | |||
import com.google.common.collect.Sets; | |||
import java.util.Collections; | |||
import org.junit.Test; | |||
import org.sonar.core.issue.DefaultIssue; | |||
import org.sonar.server.computation.component.Component; | |||
import static java.util.Arrays.asList; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Mockito.mock; | |||
import static org.sonar.server.rule.RuleTesting.XOO_X1; | |||
public class RuleTagsCopierTest { | |||
DumbRule rule = new DumbRule(XOO_X1); | |||
@org.junit.Rule | |||
public RuleRepositoryRule ruleRepository = new RuleRepositoryRule().add(rule); | |||
DefaultIssue issue = new DefaultIssue().setRuleKey(rule.getKey()); | |||
RuleTagsCopier underTest = new RuleTagsCopier(ruleRepository); | |||
@Test | |||
public void copy_tags_if_new_rule() throws Exception { | |||
rule.setTags(Sets.newHashSet("bug", "performance")); | |||
issue.setNew(true); | |||
underTest.onIssue(mock(Component.class), issue); | |||
assertThat(issue.tags()).containsExactly("bug", "performance"); | |||
} | |||
@Test | |||
public void do_not_copy_tags_if_existing_rule() throws Exception { | |||
rule.setTags(Sets.newHashSet("bug", "performance")); | |||
issue.setNew(false).setTags(asList("misra")); | |||
underTest.onIssue(mock(Component.class), issue); | |||
assertThat(issue.tags()).containsExactly("misra"); | |||
} | |||
@Test | |||
public void do_not_copy_tags_if_existing_rule_without_tags() throws Exception { | |||
rule.setTags(Sets.newHashSet("bug", "performance")); | |||
issue.setNew(false).setTags(Collections.<String>emptyList()); | |||
underTest.onIssue(mock(Component.class), issue); | |||
assertThat(issue.tags()).isEmpty(); | |||
} | |||
} |
@@ -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 |
@@ -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"); | |||
// } | |||
//} |
@@ -167,11 +167,19 @@ public class MeasureRepositoryRule extends ExternalResource implements MeasureRe | |||
return Optional.fromNullable(rawMeasures.get(new InternalKey(component, metric, rule.getId(), null))); | |||
} | |||
public Optional<Measure> getRawRuleMeasure(Component component, Metric metric, int ruleId) { | |||
return Optional.fromNullable(rawMeasures.get(new InternalKey(component, metric, ruleId, null))); | |||
} | |||
@Override | |||
public Optional<Measure> getRawMeasure(Component component, Metric metric, Characteristic characteristic) { | |||
return Optional.fromNullable(rawMeasures.get(new InternalKey(component, metric, null, characteristic.getId()))); | |||
} | |||
public Optional<Measure> getRawCharacteristicMeasure(Component component, Metric metric, int characteristicId) { | |||
return Optional.fromNullable(rawMeasures.get(new InternalKey(component, metric, null, characteristicId))); | |||
} | |||
@Override | |||
public SetMultimap<String, Measure> getRawMeasures(Component component) { | |||
ImmutableSetMultimap.Builder<String, Measure> builder = ImmutableSetMultimap.builder(); |
@@ -17,90 +17,69 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
///* | |||
// * SonarQube, open source software quality management tool. | |||
// * Copyright (C) 2008-2014 SonarSource | |||
// * mailto:contact AT sonarsource DOT com | |||
// * | |||
// * SonarQube is free software; you can redistribute it and/or | |||
// * modify it under the terms of the GNU Lesser General Public | |||
// * License as published by the Free Software Foundation; either | |||
// * version 3 of the License, or (at your option) any later version. | |||
// * | |||
// * SonarQube is distributed in the hope that it will be useful, | |||
// * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
// * Lesser General Public License for more details. | |||
// * | |||
// * You should have received a copy of the GNU Lesser General Public License | |||
// * along with this program; if not, write to the Free Software Foundation, | |||
// * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
// */ | |||
// | |||
//package org.sonar.server.computation.step; | |||
// | |||
//import java.util.Collection; | |||
//import org.junit.After; | |||
//import org.junit.Before; | |||
//import org.junit.ClassRule; | |||
//import org.junit.Test; | |||
//import org.junit.experimental.categories.Category; | |||
//import org.sonar.core.persistence.DbSession; | |||
//import org.sonar.core.persistence.DbTester; | |||
//import org.sonar.core.technicaldebt.db.CharacteristicDao; | |||
//import org.sonar.server.computation.debt.Characteristic; | |||
//import org.sonar.server.computation.debt.DebtModelHolderImpl; | |||
//import org.sonar.server.computation.debt.MutableDebtModelHolder; | |||
//import org.sonar.server.db.DbClient; | |||
//import org.sonar.test.DbTests; | |||
// | |||
//import static org.assertj.core.api.Assertions.assertThat; | |||
// | |||
//@Category(DbTests.class) | |||
//public class FeedDebtModelStepTest extends BaseStepTest { | |||
// | |||
// @ClassRule | |||
// public static final DbTester dbTester = new DbTester(); | |||
// | |||
// DbClient dbClient; | |||
// | |||
// DbSession dbSession; | |||
// | |||
// MutableDebtModelHolder debtModelHolder = new DebtModelHolderImpl(); | |||
// | |||
// FeedDebtModelStep sut; | |||
// | |||
// @Before | |||
// public void setUp() throws Exception { | |||
// dbTester.truncateTables(); | |||
// dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new CharacteristicDao(dbTester.myBatis())); | |||
// dbSession = dbClient.openSession(false); | |||
// | |||
// sut = new FeedDebtModelStep(dbClient, debtModelHolder); | |||
// } | |||
// | |||
// @After | |||
// public void tearDown() throws Exception { | |||
// dbSession.close(); | |||
// } | |||
// | |||
// @Override | |||
// protected ComputationStep step() { | |||
// return sut; | |||
// } | |||
// | |||
// @Test | |||
// public void feed_characteristics() throws Exception { | |||
// dbTester.prepareDbUnit(getClass(), "shared.xml"); | |||
// | |||
// sut.execute(); | |||
// | |||
// Collection<Characteristic> rootChars = debtModelHolder.findRootCharacteristics(); | |||
// assertThat(rootChars).extracting("id").containsOnly(1); | |||
// assertThat(rootChars).extracting("key").containsOnly("PORTABILITY"); | |||
// | |||
// Collection<Characteristic> subChars = debtModelHolder.findSubCharacteristicsByRootKey("PORTABILITY"); | |||
// assertThat(subChars).extracting("id").containsOnly(2, 3); | |||
// assertThat(subChars).extracting("key").containsOnly("COMPILER_RELATED_PORTABILITY", "HARDWARE_RELATED_PORTABILITY"); | |||
// } | |||
//} | |||
package org.sonar.server.computation.step; | |||
import java.util.Collection; | |||
import org.junit.After; | |||
import org.junit.Before; | |||
import org.junit.ClassRule; | |||
import org.junit.Test; | |||
import org.junit.experimental.categories.Category; | |||
import org.sonar.core.persistence.DbSession; | |||
import org.sonar.core.persistence.DbTester; | |||
import org.sonar.core.technicaldebt.db.CharacteristicDao; | |||
import org.sonar.server.computation.debt.Characteristic; | |||
import org.sonar.server.computation.debt.DebtModelHolderImpl; | |||
import org.sonar.server.computation.debt.MutableDebtModelHolder; | |||
import org.sonar.server.db.DbClient; | |||
import org.sonar.test.DbTests; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
@Category(DbTests.class) | |||
public class FeedDebtModelStepTest extends BaseStepTest { | |||
@ClassRule | |||
public static final DbTester dbTester = new DbTester(); | |||
DbClient dbClient; | |||
DbSession dbSession; | |||
MutableDebtModelHolder debtModelHolder = new DebtModelHolderImpl(); | |||
FeedDebtModelStep sut; | |||
@Before | |||
public void setUp() throws Exception { | |||
dbTester.truncateTables(); | |||
dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new CharacteristicDao(dbTester.myBatis())); | |||
dbSession = dbClient.openSession(false); | |||
sut = new FeedDebtModelStep(dbClient, debtModelHolder); | |||
} | |||
@After | |||
public void tearDown() throws Exception { | |||
dbSession.close(); | |||
} | |||
@Override | |||
protected ComputationStep step() { | |||
return sut; | |||
} | |||
@Test | |||
public void feed_characteristics() throws Exception { | |||
dbTester.prepareDbUnit(getClass(), "shared.xml"); | |||
sut.execute(); | |||
Collection<Characteristic> rootChars = debtModelHolder.getRootCharacteristics(); | |||
assertThat(rootChars).extracting("id").containsOnly(1); | |||
assertThat(rootChars).extracting("key").containsOnly("PORTABILITY"); | |||
Characteristic subChar = debtModelHolder.getCharacteristicById(1); | |||
assertThat(subChar).isNotNull(); | |||
} | |||
} |
@@ -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 |
@@ -29,42 +29,31 @@ import org.junit.ClassRule; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.experimental.categories.Category; | |||
import org.sonar.api.measures.Metric; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.utils.System2; | |||
import org.sonar.api.utils.internal.Uuids; | |||
import org.sonar.batch.protocol.Constants.MeasureValueType; | |||
import org.sonar.batch.protocol.output.BatchReport; | |||
import org.sonar.core.component.ComponentDto; | |||
import org.sonar.core.metric.db.MetricDto; | |||
import org.sonar.core.persistence.DbSession; | |||
import org.sonar.core.persistence.DbTester; | |||
import org.sonar.core.rule.RuleDto; | |||
import org.sonar.server.component.db.ComponentDao; | |||
import org.sonar.server.computation.batch.BatchReportReaderRule; | |||
import org.sonar.server.computation.batch.TreeRootHolderRule; | |||
import org.sonar.server.computation.component.Component; | |||
import org.sonar.server.computation.component.DbIdsRepository; | |||
import org.sonar.server.computation.component.DumbComponent; | |||
import org.sonar.server.computation.issue.RuleCache; | |||
import org.sonar.server.computation.issue.RuleCacheLoader; | |||
import org.sonar.server.computation.measure.MeasureRepository; | |||
import org.sonar.server.computation.measure.MeasureRepositoryImpl; | |||
import org.sonar.server.computation.metric.MetricRepositoryRule; | |||
import org.sonar.server.computation.metric.MetricRepositoryImpl; | |||
import org.sonar.server.db.DbClient; | |||
import org.sonar.server.measure.persistence.MeasureDao; | |||
import org.sonar.server.metric.persistence.MetricDao; | |||
import org.sonar.server.rule.RuleTesting; | |||
import org.sonar.server.rule.db.RuleDao; | |||
import org.sonar.test.DbTests; | |||
import static java.util.Arrays.asList; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.sonar.api.measures.CoreMetrics.DUPLICATIONS_DATA; | |||
import static org.sonar.api.measures.CoreMetrics.DUPLICATIONS_DATA_KEY; | |||
import static org.sonar.api.measures.CoreMetrics.FILE_COMPLEXITY_DISTRIBUTION; | |||
import static org.sonar.api.measures.CoreMetrics.FILE_COMPLEXITY_DISTRIBUTION_KEY; | |||
import static org.sonar.api.measures.CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION; | |||
import static org.sonar.api.measures.CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION_KEY; | |||
@Category(DbTests.class) | |||
public class PersistMeasuresStepTest extends BaseStepTest { | |||
@@ -73,31 +62,23 @@ public class PersistMeasuresStepTest extends BaseStepTest { | |||
private static final String STRING_METRIC_KEY = "string-metric-key"; | |||
private static final String DOUBLE_METRIC_KEY = "double-metric-key"; | |||
private static final String OPTIMIZED_METRIC_KEY = "optimized-metric-key"; | |||
private static final Metric STRING_METRIC = new Metric.Builder(STRING_METRIC_KEY, "String metric", Metric.ValueType.STRING).create(); | |||
private static final Metric DOUBLE_METRIC = new Metric.Builder(DOUBLE_METRIC_KEY, "Double metric", Metric.ValueType.FLOAT).create(); | |||
private static final RuleKey RULE_KEY = RuleKey.of("repo", "rule-key"); | |||
private static final int PROJECT_REF = 1; | |||
private static final int FILE_REF = 2; | |||
@ClassRule | |||
public static DbTester dbTester = new DbTester(); | |||
@Rule | |||
public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule(); | |||
@Rule | |||
public BatchReportReaderRule reportReader = new BatchReportReaderRule(); | |||
@Rule | |||
public MetricRepositoryRule metricRepository = new MetricRepositoryRule(); | |||
DbClient dbClient; | |||
DbSession session; | |||
DbIdsRepository dbIdsRepository = new DbIdsRepository(); | |||
RuleDto rule; | |||
MetricDto stringMetric; | |||
MetricDto doubleMetric; | |||
MetricDto optimizedMetric; | |||
ComponentDto projectDto; | |||
ComponentDto fileDto; | |||
@@ -110,12 +91,18 @@ public class PersistMeasuresStepTest extends BaseStepTest { | |||
dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new MeasureDao(), new ComponentDao(), new MetricDao(), new RuleDao(System2.INSTANCE)); | |||
session = dbClient.openSession(false); | |||
rule = RuleTesting.newDto(RULE_KEY); | |||
dbClient.ruleDao().insert(session, rule); | |||
stringMetric = new MetricDto().setValueType("STRING").setShortName("String metric").setKey(STRING_METRIC_KEY).setEnabled(true); | |||
dbClient.metricDao().insert(session, stringMetric); | |||
doubleMetric = new MetricDto().setValueType("FLOAT").setShortName("Double metric").setKey(DOUBLE_METRIC_KEY).setEnabled(true); | |||
dbClient.metricDao().insert(session, doubleMetric); | |||
optimizedMetric = new MetricDto().setValueType("BOOL").setShortName("Optimized metric").setKey(OPTIMIZED_METRIC_KEY).setEnabled(true).setOptimizedBestValue(true) | |||
.setBestValue(1d); | |||
dbClient.metricDao().insert(session, optimizedMetric); | |||
session.commit(); | |||
RuleCache ruleCache = new RuleCache(new RuleCacheLoader(dbClient)); | |||
MeasureRepository measureRepository = new MeasureRepositoryImpl(dbClient, reportReader, metricRepository, ruleCache); | |||
MetricRepositoryImpl metricRepository = new MetricRepositoryImpl(dbClient); | |||
metricRepository.start(); | |||
MeasureRepository measureRepository = new MeasureRepositoryImpl(dbClient, reportReader, metricRepository); | |||
session.commit(); | |||
sut = new PersistMeasuresStep(dbClient, metricRepository, dbIdsRepository, treeRootHolder, measureRepository); | |||
@@ -140,9 +127,6 @@ public class PersistMeasuresStepTest extends BaseStepTest { | |||
@Test | |||
public void insert_measures_from_report() throws Exception { | |||
metricRepository.add(1, STRING_METRIC); | |||
metricRepository.add(2, DOUBLE_METRIC); | |||
reportReader.putMeasures(PROJECT_REF, Arrays.asList( | |||
BatchReport.Measure.newBuilder() | |||
.setValueType(MeasureValueType.STRING) | |||
@@ -172,14 +156,14 @@ public class PersistMeasuresStepTest extends BaseStepTest { | |||
sut.execute(); | |||
session.commit(); | |||
assertThat(dbTester.countRowsOfTable("project_measures")).isEqualTo(2); | |||
assertThat(dbTester.countRowsOfTable("project_measures")).isEqualTo(FILE_REF); | |||
List<Map<String, Object>> dtos = retrieveDtos(); | |||
Map<String, Object> dto = dtos.get(0); | |||
assertThat(dto.get("snapshotId")).isEqualTo(3L); | |||
assertThat(dto.get("componentId")).isEqualTo(projectDto.getId()); | |||
assertThat(dto.get("metricId")).isEqualTo(1L); | |||
assertThat(dto.get("metricId")).isEqualTo(stringMetric.getId().longValue()); | |||
assertThat(dto.get("ruleId")).isNull(); | |||
assertThat(dto.get("textValue")).isEqualTo("measure-data"); | |||
assertThat(dto.get("severity")).isNull(); | |||
@@ -187,15 +171,9 @@ public class PersistMeasuresStepTest extends BaseStepTest { | |||
dto = dtos.get(PROJECT_REF); | |||
assertThat(dto.get("snapshotId")).isEqualTo(4L); | |||
assertThat(dto.get("componentId")).isEqualTo(fileDto.getId()); | |||
<<<<<<< HEAD | |||
assertThat(dto.get("metricId")).isEqualTo(2L); | |||
assertThat(dto.get("ruleId")).isEqualTo(rule.getId().longValue()); | |||
assertThat(dto.get("metricId")).isEqualTo(doubleMetric.getId().longValue()); | |||
assertThat(dto.get("characteristicId")).isNull(); | |||
assertThat(dto.get("value")).isEqualTo(123.1d); | |||
======= | |||
assertThat(dto.get("metricId")).isEqualTo(doubleMetric.getId().longValue()); | |||
assertThat(dto.get("value")).isEqualTo(123.123d); | |||
>>>>>>> SONAR-6588 integrate issues to Compute Engine | |||
assertThat(dto.get("severity")).isNull(); | |||
} | |||
@@ -207,8 +185,6 @@ public class PersistMeasuresStepTest extends BaseStepTest { | |||
@Test | |||
public void bestValue_measure_of_bestValueOptimized_metrics_are_not_persisted() { | |||
metricRepository.add(1, new Metric.Builder(OPTIMIZED_METRIC_KEY, "Optimized metric", Metric.ValueType.BOOL).setOptimizedBestValue(true).setBestValue(1d).create()); | |||
reportReader.putMeasures(FILE_REF, Arrays.asList( | |||
BatchReport.Measure.newBuilder() | |||
.setValueType(MeasureValueType.BOOLEAN) | |||
@@ -224,9 +200,6 @@ public class PersistMeasuresStepTest extends BaseStepTest { | |||
@Test | |||
public void empty_values_are_not_persisted() { | |||
metricRepository.add(1, STRING_METRIC); | |||
metricRepository.add(2, DOUBLE_METRIC); | |||
reportReader.putMeasures(FILE_REF, Arrays.asList( | |||
BatchReport.Measure.newBuilder() | |||
.setValueType(MeasureValueType.STRING) | |||
@@ -244,86 +217,6 @@ public class PersistMeasuresStepTest extends BaseStepTest { | |||
assertThat(retrieveDtos()).isEmpty(); | |||
} | |||
@Test(expected = IllegalStateException.class) | |||
public void fail_with_ISE_when_trying_to_insert_forbidden_measures() throws Exception { | |||
metricRepository.add(1, DUPLICATIONS_DATA); | |||
reportReader.putMeasures(FILE_REF, Arrays.asList( | |||
BatchReport.Measure.newBuilder() | |||
.setValueType(MeasureValueType.STRING) | |||
.setStringValue("{duplications}") | |||
.setMetricKey(DUPLICATIONS_DATA_KEY) | |||
.build())); | |||
sut.execute(); | |||
} | |||
@Test | |||
public void do_not_insert_file_complexity_distribution_metric_on_files() throws Exception { | |||
metricRepository.add(1, FILE_COMPLEXITY_DISTRIBUTION); | |||
reportReader.putMeasures(PROJECT_REF, Arrays.asList( | |||
BatchReport.Measure.newBuilder() | |||
.setValueType(MeasureValueType.STRING) | |||
.setStringValue("0=1;2=10") | |||
.setMetricKey(FILE_COMPLEXITY_DISTRIBUTION_KEY) | |||
.build())); | |||
// Should not be persisted | |||
reportReader.putMeasures(FILE_REF, Arrays.asList( | |||
BatchReport.Measure.newBuilder() | |||
.setValueType(MeasureValueType.STRING) | |||
.setStringValue("0=1;2=10") | |||
.setMetricKey(FILE_COMPLEXITY_DISTRIBUTION_KEY) | |||
.build())); | |||
sut.execute(); | |||
session.commit(); | |||
assertThat(dbTester.countRowsOfTable("project_measures")).isEqualTo(1); | |||
List<Map<String, Object>> dtos = retrieveDtos(); | |||
Map<String, Object> dto = dtos.get(0); | |||
assertThat(dto.get("snapshotId")).isEqualTo(3L); | |||
assertThat(dto.get("componentId")).isEqualTo(projectDto.getId()); | |||
assertThat(dto.get("textValue")).isEqualTo("0=1;2=10"); | |||
} | |||
@Test | |||
public void do_not_insert_function_complexity_distribution_metric_on_files() throws Exception { | |||
metricRepository.add(1, FUNCTION_COMPLEXITY_DISTRIBUTION); | |||
reportReader.putMeasures(PROJECT_REF, Arrays.asList( | |||
BatchReport.Measure.newBuilder() | |||
.setValueType(MeasureValueType.STRING) | |||
.setStringValue("0=1;2=10") | |||
.setMetricKey(FUNCTION_COMPLEXITY_DISTRIBUTION_KEY) | |||
.build())); | |||
// Should not be persisted | |||
reportReader.putMeasures(FILE_REF, Arrays.asList( | |||
BatchReport.Measure.newBuilder() | |||
.setValueType(MeasureValueType.STRING) | |||
.setStringValue("0=1;2=10") | |||
.setMetricKey(FUNCTION_COMPLEXITY_DISTRIBUTION_KEY) | |||
.build())); | |||
sut.execute(); | |||
session.commit(); | |||
assertThat(dbTester.countRowsOfTable("project_measures")).isEqualTo(1); | |||
List<Map<String, Object>> dtos = retrieveDtos(); | |||
Map<String, Object> dto = dtos.get(0); | |||
assertThat(dto.get("snapshotId")).isEqualTo(3L); | |||
assertThat(dto.get("componentId")).isEqualTo(projectDto.getId()); | |||
assertThat(dto.get("textValue")).isEqualTo("0=1;2=10"); | |||
} | |||
private ComponentDto addComponent(String key) { | |||
ComponentDto componentDto = new ComponentDto().setKey(key).setUuid(Uuids.create()); | |||
dbClient.componentDao().insert(session, componentDto); |
@@ -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()); | |||
@@ -1,11 +1,11 @@ | |||
<dataset> | |||
<rules id="1" name="Rule One" plugin_name="squid" plugin_rule_key="R001" | |||
<rules id="1" name="JS One" plugin_name="js" plugin_rule_key="JS01" | |||
plugin_config_key="[null]" description="[null]" priority="4" | |||
status="READY" | |||
is_template="[false]" template_id="[null]" | |||
tags="[null]" system_tags="[null]"/> | |||
<rules id="2" name="Rule Two" plugin_name="squid" plugin_rule_key="R002" | |||
<rules id="2" name="Java One" plugin_name="java" plugin_rule_key="JAV01" | |||
plugin_config_key="[null]" description="[null]" priority="4" | |||
status="READY" | |||
is_template="[false]" template_id="[null]" |
@@ -4,7 +4,7 @@ | |||
plugin_config_key="[null]" priority="0" is_template="[true]" language="xoo" template_id="[null]" | |||
note_data="[null]" note_user_login="[null]" note_created_at="[null]" note_updated_at="[null]" | |||
characteristic_id="100" default_characteristic_id="101" | |||
remediation_function="LINEAR" default_remediation_function="LINEAR_OFFSET" | |||
remediation_function="LINEAR_OFFSET" default_remediation_function="LINEAR_OFFSET" | |||
remediation_coeff="1h" default_remediation_coeff="5d" | |||
remediation_offset="5min" default_remediation_offset="10h" | |||
effort_to_fix_description="[null]" description_format="MARKDOWN" |
@@ -5764,6 +5764,10 @@ public final class BatchReport { | |||
} | |||
/** | |||
* Protobuf type {@code Measures} | |||
* | |||
* <pre> | |||
* TODO to be removed. It prevents streaming | |||
* </pre> | |||
*/ | |||
public static final class Measures extends | |||
com.google.protobuf.GeneratedMessage implements | |||
@@ -6040,6 +6044,10 @@ public final class BatchReport { | |||
} | |||
/** | |||
* Protobuf type {@code Measures} | |||
* | |||
* <pre> | |||
* TODO to be removed. It prevents streaming | |||
* </pre> | |||
*/ | |||
public static final class Builder extends | |||
com.google.protobuf.GeneratedMessage.Builder<Builder> implements | |||
@@ -6587,23 +6595,6 @@ public final class BatchReport { | |||
*/ | |||
com.google.protobuf.ByteString | |||
getAttributesBytes(); | |||
/** | |||
* <code>optional int64 debt_in_minutes = 9;</code> | |||
* | |||
* <pre> | |||
* TODO should it be moved to compute engine? | |||
* </pre> | |||
*/ | |||
boolean hasDebtInMinutes(); | |||
/** | |||
* <code>optional int64 debt_in_minutes = 9;</code> | |||
* | |||
* <pre> | |||
* TODO should it be moved to compute engine? | |||
* </pre> | |||
*/ | |||
long getDebtInMinutes(); | |||
} | |||
/** | |||
* Protobuf type {@code Issue} | |||
@@ -6711,11 +6702,6 @@ public final class BatchReport { | |||
attributes_ = bs; | |||
break; | |||
} | |||
case 72: { | |||
bitField0_ |= 0x00000080; | |||
debtInMinutes_ = input.readInt64(); | |||
break; | |||
} | |||
} | |||
} | |||
} catch (com.google.protobuf.InvalidProtocolBufferException e) { | |||
@@ -7001,29 +6987,6 @@ public final class BatchReport { | |||
} | |||
} | |||
public static final int DEBT_IN_MINUTES_FIELD_NUMBER = 9; | |||
private long debtInMinutes_; | |||
/** | |||
* <code>optional int64 debt_in_minutes = 9;</code> | |||
* | |||
* <pre> | |||
* TODO should it be moved to compute engine? | |||
* </pre> | |||
*/ | |||
public boolean hasDebtInMinutes() { | |||
return ((bitField0_ & 0x00000080) == 0x00000080); | |||
} | |||
/** | |||
* <code>optional int64 debt_in_minutes = 9;</code> | |||
* | |||
* <pre> | |||
* TODO should it be moved to compute engine? | |||
* </pre> | |||
*/ | |||
public long getDebtInMinutes() { | |||
return debtInMinutes_; | |||
} | |||
private void initFields() { | |||
ruleRepository_ = ""; | |||
ruleKey_ = ""; | |||
@@ -7033,7 +6996,6 @@ public final class BatchReport { | |||
tag_ = com.google.protobuf.LazyStringArrayList.EMPTY; | |||
effortToFix_ = 0D; | |||
attributes_ = ""; | |||
debtInMinutes_ = 0L; | |||
} | |||
private byte memoizedIsInitialized = -1; | |||
public final boolean isInitialized() { | |||
@@ -7072,9 +7034,6 @@ public final class BatchReport { | |||
if (((bitField0_ & 0x00000040) == 0x00000040)) { | |||
output.writeBytes(8, getAttributesBytes()); | |||
} | |||
if (((bitField0_ & 0x00000080) == 0x00000080)) { | |||
output.writeInt64(9, debtInMinutes_); | |||
} | |||
getUnknownFields().writeTo(output); | |||
} | |||
@@ -7121,10 +7080,6 @@ public final class BatchReport { | |||
size += com.google.protobuf.CodedOutputStream | |||
.computeBytesSize(8, getAttributesBytes()); | |||
} | |||
if (((bitField0_ & 0x00000080) == 0x00000080)) { | |||
size += com.google.protobuf.CodedOutputStream | |||
.computeInt64Size(9, debtInMinutes_); | |||
} | |||
size += getUnknownFields().getSerializedSize(); | |||
memoizedSerializedSize = size; | |||
return size; | |||
@@ -7258,8 +7213,6 @@ public final class BatchReport { | |||
bitField0_ = (bitField0_ & ~0x00000040); | |||
attributes_ = ""; | |||
bitField0_ = (bitField0_ & ~0x00000080); | |||
debtInMinutes_ = 0L; | |||
bitField0_ = (bitField0_ & ~0x00000100); | |||
return this; | |||
} | |||
@@ -7321,10 +7274,6 @@ public final class BatchReport { | |||
to_bitField0_ |= 0x00000040; | |||
} | |||
result.attributes_ = attributes_; | |||
if (((from_bitField0_ & 0x00000100) == 0x00000100)) { | |||
to_bitField0_ |= 0x00000080; | |||
} | |||
result.debtInMinutes_ = debtInMinutes_; | |||
result.bitField0_ = to_bitField0_; | |||
onBuilt(); | |||
return result; | |||
@@ -7380,9 +7329,6 @@ public final class BatchReport { | |||
attributes_ = other.attributes_; | |||
onChanged(); | |||
} | |||
if (other.hasDebtInMinutes()) { | |||
setDebtInMinutes(other.getDebtInMinutes()); | |||
} | |||
this.mergeUnknownFields(other.getUnknownFields()); | |||
return this; | |||
} | |||
@@ -7906,54 +7852,6 @@ public final class BatchReport { | |||
return this; | |||
} | |||
private long debtInMinutes_ ; | |||
/** | |||
* <code>optional int64 debt_in_minutes = 9;</code> | |||
* | |||
* <pre> | |||
* TODO should it be moved to compute engine? | |||
* </pre> | |||
*/ | |||
public boolean hasDebtInMinutes() { | |||
return ((bitField0_ & 0x00000100) == 0x00000100); | |||
} | |||
/** | |||
* <code>optional int64 debt_in_minutes = 9;</code> | |||
* | |||
* <pre> | |||
* TODO should it be moved to compute engine? | |||
* </pre> | |||
*/ | |||
public long getDebtInMinutes() { | |||
return debtInMinutes_; | |||
} | |||
/** | |||
* <code>optional int64 debt_in_minutes = 9;</code> | |||
* | |||
* <pre> | |||
* TODO should it be moved to compute engine? | |||
* </pre> | |||
*/ | |||
public Builder setDebtInMinutes(long value) { | |||
bitField0_ |= 0x00000100; | |||
debtInMinutes_ = value; | |||
onChanged(); | |||
return this; | |||
} | |||
/** | |||
* <code>optional int64 debt_in_minutes = 9;</code> | |||
* | |||
* <pre> | |||
* TODO should it be moved to compute engine? | |||
* </pre> | |||
*/ | |||
public Builder clearDebtInMinutes() { | |||
bitField0_ = (bitField0_ & ~0x00000100); | |||
debtInMinutes_ = 0L; | |||
onChanged(); | |||
return this; | |||
} | |||
// @@protoc_insertion_point(builder_scope:Issue) | |||
} | |||
@@ -8004,6 +7902,10 @@ public final class BatchReport { | |||
} | |||
/** | |||
* Protobuf type {@code Issues} | |||
* | |||
* <pre> | |||
* TODO to be removed. It prevents streaming | |||
* </pre> | |||
*/ | |||
public static final class Issues extends | |||
com.google.protobuf.GeneratedMessage implements | |||
@@ -8280,6 +8182,10 @@ public final class BatchReport { | |||
} | |||
/** | |||
* Protobuf type {@code Issues} | |||
* | |||
* <pre> | |||
* TODO to be removed. It prevents streaming | |||
* </pre> | |||
*/ | |||
public static final class Builder extends | |||
com.google.protobuf.GeneratedMessage.Builder<Builder> implements | |||
@@ -19463,42 +19369,41 @@ public final class BatchReport { | |||
"alue_4\030\021 \001(\001\022\031\n\021variation_value_5\030\022 \001(\001\022", | |||
"\021\n\tperson_id\030\024 \001(\005\"<\n\010Measures\022\025\n\rcompon" + | |||
"ent_ref\030\001 \001(\005\022\031\n\007measure\030\002 \003(\0132\010.Measure" + | |||
"\"\273\001\n\005Issue\022\027\n\017rule_repository\030\001 \001(\t\022\020\n\010r" + | |||
"\"\242\001\n\005Issue\022\027\n\017rule_repository\030\001 \001(\t\022\020\n\010r" + | |||
"ule_key\030\002 \001(\t\022\014\n\004line\030\003 \001(\005\022\013\n\003msg\030\004 \001(\t" + | |||
"\022\033\n\010severity\030\005 \001(\0162\t.Severity\022\013\n\003tag\030\006 \003" + | |||
"(\t\022\025\n\reffort_to_fix\030\007 \001(\001\022\022\n\nattributes\030" + | |||
"\010 \001(\t\022\027\n\017debt_in_minutes\030\t \001(\003\"6\n\006Issues" + | |||
"\022\025\n\rcomponent_ref\030\001 \001(\005\022\025\n\005issue\030\002 \003(\0132\006" + | |||
".Issue\"\254\001\n\nChangesets\022\025\n\rcomponent_ref\030\001" + | |||
" \001(\005\022(\n\tchangeset\030\002 \003(\0132\025.Changesets.Cha", | |||
"ngeset\022 \n\024changesetIndexByLine\030\003 \003(\005B\002\020\001" + | |||
"\032;\n\tChangeset\022\020\n\010revision\030\001 \001(\t\022\016\n\006autho" + | |||
"r\030\002 \001(\t\022\014\n\004date\030\003 \001(\003\"R\n\tDuplicate\022\026\n\016ot" + | |||
"her_file_ref\030\001 \001(\005\022\025\n\005range\030\002 \001(\0132\006.Rang" + | |||
"e\022\026\n\016other_file_key\030\003 \001(\t\"M\n\013Duplication" + | |||
"\022\037\n\017origin_position\030\001 \001(\0132\006.Range\022\035\n\tdup" + | |||
"licate\030\002 \003(\0132\n.Duplicate\"H\n\014Duplications" + | |||
"\022\025\n\rcomponent_ref\030\001 \001(\005\022!\n\013duplication\030\002" + | |||
" \003(\0132\014.Duplication\"W\n\005Range\022\022\n\nstart_lin" + | |||
"e\030\001 \001(\005\022\020\n\010end_line\030\002 \001(\005\022\024\n\014start_offse", | |||
"t\030\003 \001(\005\022\022\n\nend_offset\030\004 \001(\005\"~\n\007Symbols\022\020" + | |||
"\n\010file_ref\030\001 \001(\005\022\037\n\006symbol\030\002 \003(\0132\017.Symbo" + | |||
"ls.Symbol\032@\n\006Symbol\022\033\n\013declaration\030\001 \001(\013" + | |||
"2\006.Range\022\031\n\treference\030\002 \003(\0132\006.Range\"\260\001\n\010" + | |||
"Coverage\022\014\n\004line\030\001 \001(\005\022\022\n\nconditions\030\002 \001" + | |||
"(\005\022\017\n\007ut_hits\030\003 \001(\010\022\017\n\007it_hits\030\004 \001(\010\022\035\n\025" + | |||
"ut_covered_conditions\030\005 \001(\005\022\035\n\025it_covere" + | |||
"d_conditions\030\006 \001(\005\022\"\n\032overall_covered_co" + | |||
"nditions\030\007 \001(\005\"L\n\022SyntaxHighlighting\022\025\n\005" + | |||
"range\030\001 \001(\0132\006.Range\022\037\n\004type\030\002 \001(\0162\021.High", | |||
"lightingType\"j\n\004Test\022\014\n\004name\030\001 \001(\t\022\033\n\006st" + | |||
"atus\030\002 \001(\0162\013.TestStatus\022\026\n\016duration_in_m" + | |||
"s\030\003 \001(\003\022\022\n\nstacktrace\030\004 \001(\t\022\013\n\003msg\030\005 \001(\t" + | |||
"\"\221\001\n\016CoverageDetail\022\021\n\ttest_name\030\001 \001(\t\0221" + | |||
"\n\014covered_file\030\002 \003(\0132\033.CoverageDetail.Co" + | |||
"veredFile\0329\n\013CoveredFile\022\020\n\010file_ref\030\001 \001" + | |||
"(\005\022\030\n\014covered_line\030\002 \003(\005B\002\020\001B#\n\037org.sona" + | |||
"r.batch.protocol.outputH\001" | |||
"\010 \001(\t\"6\n\006Issues\022\025\n\rcomponent_ref\030\001 \001(\005\022\025" + | |||
"\n\005issue\030\002 \003(\0132\006.Issue\"\254\001\n\nChangesets\022\025\n\r" + | |||
"component_ref\030\001 \001(\005\022(\n\tchangeset\030\002 \003(\0132\025" + | |||
".Changesets.Changeset\022 \n\024changesetIndexB", | |||
"yLine\030\003 \003(\005B\002\020\001\032;\n\tChangeset\022\020\n\010revision" + | |||
"\030\001 \001(\t\022\016\n\006author\030\002 \001(\t\022\014\n\004date\030\003 \001(\003\"R\n\t" + | |||
"Duplicate\022\026\n\016other_file_ref\030\001 \001(\005\022\025\n\005ran" + | |||
"ge\030\002 \001(\0132\006.Range\022\026\n\016other_file_key\030\003 \001(\t" + | |||
"\"M\n\013Duplication\022\037\n\017origin_position\030\001 \001(\013" + | |||
"2\006.Range\022\035\n\tduplicate\030\002 \003(\0132\n.Duplicate\"" + | |||
"H\n\014Duplications\022\025\n\rcomponent_ref\030\001 \001(\005\022!" + | |||
"\n\013duplication\030\002 \003(\0132\014.Duplication\"W\n\005Ran" + | |||
"ge\022\022\n\nstart_line\030\001 \001(\005\022\020\n\010end_line\030\002 \001(\005" + | |||
"\022\024\n\014start_offset\030\003 \001(\005\022\022\n\nend_offset\030\004 \001", | |||
"(\005\"~\n\007Symbols\022\020\n\010file_ref\030\001 \001(\005\022\037\n\006symbo" + | |||
"l\030\002 \003(\0132\017.Symbols.Symbol\032@\n\006Symbol\022\033\n\013de" + | |||
"claration\030\001 \001(\0132\006.Range\022\031\n\treference\030\002 \003" + | |||
"(\0132\006.Range\"\260\001\n\010Coverage\022\014\n\004line\030\001 \001(\005\022\022\n" + | |||
"\nconditions\030\002 \001(\005\022\017\n\007ut_hits\030\003 \001(\010\022\017\n\007it" + | |||
"_hits\030\004 \001(\010\022\035\n\025ut_covered_conditions\030\005 \001" + | |||
"(\005\022\035\n\025it_covered_conditions\030\006 \001(\005\022\"\n\032ove" + | |||
"rall_covered_conditions\030\007 \001(\005\"L\n\022SyntaxH" + | |||
"ighlighting\022\025\n\005range\030\001 \001(\0132\006.Range\022\037\n\004ty" + | |||
"pe\030\002 \001(\0162\021.HighlightingType\"j\n\004Test\022\014\n\004n", | |||
"ame\030\001 \001(\t\022\033\n\006status\030\002 \001(\0162\013.TestStatus\022\026" + | |||
"\n\016duration_in_ms\030\003 \001(\003\022\022\n\nstacktrace\030\004 \001" + | |||
"(\t\022\013\n\003msg\030\005 \001(\t\"\221\001\n\016CoverageDetail\022\021\n\tte" + | |||
"st_name\030\001 \001(\t\0221\n\014covered_file\030\002 \003(\0132\033.Co" + | |||
"verageDetail.CoveredFile\0329\n\013CoveredFile\022" + | |||
"\020\n\010file_ref\030\001 \001(\005\022\030\n\014covered_line\030\002 \003(\005B" + | |||
"\002\020\001B#\n\037org.sonar.batch.protocol.outputH\001" | |||
}; | |||
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = | |||
new com.google.protobuf.Descriptors.FileDescriptor. InternalDescriptorAssigner() { | |||
@@ -19548,7 +19453,7 @@ public final class BatchReport { | |||
internal_static_Issue_fieldAccessorTable = new | |||
com.google.protobuf.GeneratedMessage.FieldAccessorTable( | |||
internal_static_Issue_descriptor, | |||
new java.lang.String[] { "RuleRepository", "RuleKey", "Line", "Msg", "Severity", "Tag", "EffortToFix", "Attributes", "DebtInMinutes", }); | |||
new java.lang.String[] { "RuleRepository", "RuleKey", "Line", "Msg", "Severity", "Tag", "EffortToFix", "Attributes", }); | |||
internal_static_Issues_descriptor = | |||
getDescriptor().getMessageTypes().get(6); | |||
internal_static_Issues_fieldAccessorTable = new |
@@ -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 { | |||
@@ -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; |
@@ -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; | |||
} | |||
} |
@@ -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() { |
@@ -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(); | |||
} | |||
@@ -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 { | |||
@@ -19,6 +19,8 @@ | |||
*/ | |||
package org.sonar.batch.issue; | |||
import java.util.Calendar; | |||
import java.util.Date; | |||
import org.apache.commons.lang.time.DateUtils; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
@@ -26,10 +28,8 @@ import org.junit.runner.RunWith; | |||
import org.mockito.ArgumentCaptor; | |||
import org.mockito.Mock; | |||
import org.mockito.runners.MockitoJUnitRunner; | |||
import org.sonar.api.batch.debt.DebtRemediationFunction; | |||
import org.sonar.api.batch.rule.internal.ActiveRulesBuilder; | |||
import org.sonar.api.batch.rule.internal.RulesBuilder; | |||
import org.sonar.core.issue.DefaultIssue; | |||
import org.sonar.api.resources.File; | |||
import org.sonar.api.resources.Project; | |||
import org.sonar.api.resources.Resource; | |||
@@ -37,11 +37,8 @@ import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.rule.Severity; | |||
import org.sonar.api.rules.RulePriority; | |||
import org.sonar.api.rules.Violation; | |||
import org.sonar.api.utils.Duration; | |||
import org.sonar.api.utils.MessageException; | |||
import java.util.Calendar; | |||
import java.util.Date; | |||
import org.sonar.core.issue.DefaultIssue; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.junit.Assert.fail; | |||
@@ -249,106 +246,6 @@ public class ModuleIssuesTest { | |||
verifyZeroInteractions(cache); | |||
} | |||
@Test | |||
public void set_debt_with_linear_function() { | |||
ruleBuilder.add(SQUID_RULE_KEY) | |||
.setName(SQUID_RULE_NAME) | |||
.setDebtRemediationFunction(DebtRemediationFunction.createLinear(Duration.create(10L))); | |||
activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate(); | |||
initModuleIssues(); | |||
Date analysisDate = new Date(); | |||
when(project.getAnalysisDate()).thenReturn(analysisDate); | |||
DefaultIssue issue = new DefaultIssue() | |||
.setKey("ABCDE") | |||
.setRuleKey(SQUID_RULE_KEY) | |||
.setSeverity(Severity.CRITICAL) | |||
.setEffortToFix(2d); | |||
when(filters.accept(issue)).thenReturn(true); | |||
moduleIssues.initAndAddIssue(issue); | |||
ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class); | |||
verify(cache).put(argument.capture()); | |||
assertThat(argument.getValue().debt()).isEqualTo(Duration.create(20L)); | |||
} | |||
@Test | |||
public void set_debt_with_linear_with_offset_function() { | |||
ruleBuilder.add(SQUID_RULE_KEY) | |||
.setName(SQUID_RULE_NAME) | |||
.setDebtRemediationFunction(DebtRemediationFunction.createLinearWithOffset(Duration.create(10L), Duration.create(25L))); | |||
activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate(); | |||
initModuleIssues(); | |||
Date analysisDate = new Date(); | |||
when(project.getAnalysisDate()).thenReturn(analysisDate); | |||
DefaultIssue issue = new DefaultIssue() | |||
.setKey("ABCDE") | |||
.setRuleKey(SQUID_RULE_KEY) | |||
.setSeverity(Severity.CRITICAL) | |||
.setEffortToFix(2d); | |||
when(filters.accept(issue)).thenReturn(true); | |||
moduleIssues.initAndAddIssue(issue); | |||
ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class); | |||
verify(cache).put(argument.capture()); | |||
assertThat(argument.getValue().debt()).isEqualTo(Duration.create(45L)); | |||
} | |||
@Test | |||
public void set_debt_with_constant_issue_function() { | |||
ruleBuilder.add(SQUID_RULE_KEY) | |||
.setName(SQUID_RULE_NAME) | |||
.setDebtRemediationFunction(DebtRemediationFunction.createConstantPerIssue(Duration.create(10L))); | |||
activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate(); | |||
initModuleIssues(); | |||
Date analysisDate = new Date(); | |||
when(project.getAnalysisDate()).thenReturn(analysisDate); | |||
DefaultIssue issue = new DefaultIssue() | |||
.setKey("ABCDE") | |||
.setRuleKey(SQUID_RULE_KEY) | |||
.setSeverity(Severity.CRITICAL) | |||
.setEffortToFix(null); | |||
when(filters.accept(issue)).thenReturn(true); | |||
moduleIssues.initAndAddIssue(issue); | |||
ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class); | |||
verify(cache).put(argument.capture()); | |||
assertThat(argument.getValue().debt()).isEqualTo(Duration.create(10L)); | |||
} | |||
@Test | |||
public void fail_to_set_debt_with_constant_issue_function_when_effort_to_fix_is_set() { | |||
ruleBuilder.add(SQUID_RULE_KEY) | |||
.setName(SQUID_RULE_NAME) | |||
.setDebtRemediationFunction(DebtRemediationFunction.createConstantPerIssue(Duration.create(25L))); | |||
activeRulesBuilder.create(SQUID_RULE_KEY).setSeverity(Severity.INFO).activate(); | |||
initModuleIssues(); | |||
DefaultIssue issue = new DefaultIssue() | |||
.setKey("ABCDE") | |||
.setRuleKey(SQUID_RULE_KEY) | |||
.setSeverity(Severity.CRITICAL) | |||
.setEffortToFix(2d); | |||
when(filters.accept(issue)).thenReturn(true); | |||
try { | |||
moduleIssues.initAndAddIssue(issue); | |||
fail(); | |||
} catch (Exception e) { | |||
assertThat(e).isInstanceOf(IllegalArgumentException.class) | |||
.hasMessage("Rule 'squid:AvoidCycle' can not use 'Constant/issue' remediation function because this rule does not have a fixed remediation cost."); | |||
} | |||
} | |||
/** | |||
* Every rules and active rules has to be added in builders before creating ModuleIssues | |||
*/ |
@@ -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"); |
@@ -65,11 +65,11 @@ public class IssueChangeDao implements DaoComponent { | |||
} | |||
} | |||
public List<IssueChangeDto> selectChangelogOfUnresolvedIssuesByComponent(String componentUuid) { | |||
public List<IssueChangeDto> selectChangelogOfNonClosedIssuesByComponent(String componentUuid) { | |||
DbSession session = mybatis.openSession(false); | |||
try { | |||
IssueChangeMapper mapper = session.getMapper(IssueChangeMapper.class); | |||
return mapper.selectChangelogOfUnresolvedIssuesByComponent(componentUuid, IssueChangeDto.TYPE_FIELD_CHANGE); | |||
return mapper.selectChangelogOfNonClosedIssuesByComponent(componentUuid, IssueChangeDto.TYPE_FIELD_CHANGE); | |||
} finally { | |||
MyBatis.closeQuietly(session); |
@@ -41,5 +41,5 @@ public interface IssueChangeMapper { | |||
List<IssueChangeDto> selectByIssuesAndType(@Param("issueKeys") List<String> issueKeys, | |||
@Param("changeType") String changeType); | |||
List<IssueChangeDto> selectChangelogOfUnresolvedIssuesByComponent(@Param("componentUuid") String componentUuid, @Param("changeType") String changeType); | |||
List<IssueChangeDto> selectChangelogOfNonClosedIssuesByComponent(@Param("componentUuid") String componentUuid, @Param("changeType") String changeType); | |||
} |
@@ -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) { |
@@ -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; | |||
@@ -36,22 +36,22 @@ class BlockRecognizer<RAW extends Trackable, BASE extends Trackable> { | |||
* Only the issues associated to a line can be matched here. | |||
*/ | |||
void match(Input<RAW> rawInput, Input<BASE> baseInput, Tracking<RAW, BASE> tracking) { | |||
BlockHashSequence baseHashSequence = baseInput.getBlockHashSequence(); | |||
BlockHashSequence rawHashSequence = rawInput.getBlockHashSequence(); | |||
BlockHashSequence baseHashSequence = baseInput.getBlockHashSequence(); | |||
Multimap<Integer, RAW> rawsByLine = groupByLine(tracking.getUnmatchedRaws()); | |||
Multimap<Integer, BASE> basesByLine = groupByLine(tracking.getUnmatchedBases()); | |||
Map<Integer, HashOccurrence> map = new HashMap<>(); | |||
Multimap<Integer, RAW> rawsByLine = groupByLine(tracking.getUnmatchedRaws(), rawHashSequence); | |||
Multimap<Integer, BASE> basesByLine = groupByLine(tracking.getUnmatchedBases(), baseHashSequence); | |||
Map<Integer, HashOccurrence> occurrencesByHash = new HashMap<>(); | |||
for (Integer line : basesByLine.keySet()) { | |||
int hash = baseHashSequence.getBlockHashForLine(line); | |||
HashOccurrence hashOccurrence = map.get(hash); | |||
HashOccurrence hashOccurrence = occurrencesByHash.get(hash); | |||
if (hashOccurrence == null) { | |||
// first occurrence in base | |||
hashOccurrence = new HashOccurrence(); | |||
hashOccurrence.baseLine = line; | |||
hashOccurrence.baseCount = 1; | |||
map.put(hash, hashOccurrence); | |||
occurrencesByHash.put(hash, hashOccurrence); | |||
} else { | |||
hashOccurrence.baseCount++; | |||
} | |||
@@ -59,14 +59,14 @@ class BlockRecognizer<RAW extends Trackable, BASE extends Trackable> { | |||
for (Integer line : rawsByLine.keySet()) { | |||
int hash = rawHashSequence.getBlockHashForLine(line); | |||
HashOccurrence hashOccurrence = map.get(hash); | |||
HashOccurrence hashOccurrence = occurrencesByHash.get(hash); | |||
if (hashOccurrence != null) { | |||
hashOccurrence.rawLine = line; | |||
hashOccurrence.rawCount++; | |||
} | |||
} | |||
for (HashOccurrence hashOccurrence : map.values()) { | |||
for (HashOccurrence hashOccurrence : occurrencesByHash.values()) { | |||
if (hashOccurrence.baseCount == 1 && hashOccurrence.rawCount == 1) { | |||
// Guaranteed that baseLine has been moved to rawLine, so we can map all issues on baseLine to all issues on rawLine | |||
map(rawsByLine.get(hashOccurrence.rawLine), basesByLine.get(hashOccurrence.baseLine), tracking); | |||
@@ -131,11 +131,11 @@ class BlockRecognizer<RAW extends Trackable, BASE extends Trackable> { | |||
} | |||
} | |||
private static <T extends Trackable> Multimap<Integer, T> groupByLine(Collection<T> trackables) { | |||
private static <T extends Trackable> Multimap<Integer, T> groupByLine(Collection<T> trackables, BlockHashSequence hashSequence) { | |||
Multimap<Integer, T> result = LinkedHashMultimap.create(); | |||
for (T trackable : trackables) { | |||
Integer line = trackable.getLine(); | |||
if (line != null) { | |||
if (hashSequence.hasLine(line)) { | |||
result.put(line, trackable); | |||
} | |||
} |
@@ -25,11 +25,11 @@ import com.google.common.collect.ArrayListMultimap; | |||
import com.google.common.collect.Multimap; | |||
import java.util.ArrayList; | |||
import java.util.Collection; | |||
import java.util.HashSet; | |||
import java.util.Objects; | |||
import javax.annotation.Nonnull; | |||
import org.apache.commons.lang.StringUtils; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.utils.log.Loggers; | |||
import static com.google.common.collect.FluentIterable.from; | |||
@@ -38,6 +38,8 @@ public class Tracker<RAW extends Trackable, BASE extends Trackable> { | |||
public Tracking<RAW, BASE> track(Input<RAW> rawInput, Input<BASE> baseInput) { | |||
Tracking<RAW, BASE> tracking = new Tracking<>(rawInput, baseInput); | |||
relocateManualIssues(rawInput, baseInput, tracking); | |||
// 1. match issues with same rule, same line and same line hash, but not necessarily with same message | |||
match(tracking, LineAndLineHashKeyFactory.INSTANCE); | |||
@@ -54,9 +56,6 @@ public class Tracker<RAW extends Trackable, BASE extends Trackable> { | |||
// See SONAR-2812 | |||
match(tracking, LineHashKeyFactory.INSTANCE); | |||
// TODO what about issues on line 0 ? | |||
relocateManualIssues(rawInput, tracking); | |||
return tracking; | |||
} | |||
@@ -92,23 +91,28 @@ public class Tracker<RAW extends Trackable, BASE extends Trackable> { | |||
tracking.markRawsAsAssociated(trackedRaws); | |||
} | |||
private void relocateManualIssues(Input<RAW> rawInput, Tracking<RAW, BASE> tracking) { | |||
Iterable<BASE> manualIssues = from(tracking.getUnmatchedBases()).filter(IsManual.INSTANCE); | |||
private void relocateManualIssues(Input<RAW> rawInput, Input<BASE> baseInput, Tracking<RAW, BASE> tracking) { | |||
// FIXME copy of Set if required to avoid concurrent modifications (see tracking.associateManualIssueToLine()) | |||
Iterable<BASE> manualIssues = from(new HashSet<>(tracking.getUnmatchedBases())).filter(IsManual.INSTANCE); | |||
for (BASE base : manualIssues) { | |||
if (base.getLine() == null) { | |||
// no need to relocate. Location is unchanged. | |||
tracking.associateManualIssueToLine(base, 0); | |||
tracking.associateManualIssueToLine(base, null); | |||
} else { | |||
String lineHash = base.getLineHash(); | |||
if (!Strings.isNullOrEmpty(lineHash)) { | |||
int[] rawLines = rawInput.getLineHashSequence().getLinesForHash(lineHash); | |||
String baseHash = base.getLineHash(); | |||
if (Strings.isNullOrEmpty(baseHash)) { | |||
baseHash = baseInput.getLineHashSequence().getHashForLine(base.getLine()); | |||
} | |||
if (!Strings.isNullOrEmpty(baseHash)) { | |||
int[] rawLines = rawInput.getLineHashSequence().getLinesForHash(baseHash); | |||
if (rawLines.length == 1) { | |||
tracking.associateManualIssueToLine(base, rawLines[0]); | |||
} else if (rawLines.length == 0 && base.getLine() <= rawInput.getLineHashSequence().length()) { | |||
} else if (rawLines.length == 0 && rawInput.getLineHashSequence().hasLine(base.getLine())) { | |||
// still valid (???). We didn't manage to correctly detect code move, so the | |||
// issue is kept at the same location, even if code changes | |||
tracking.associateManualIssueToLine(base, base.getLine()); | |||
} | |||
// TODO if hash found multiple times, , pick the closest line | |||
} | |||
} | |||
} | |||
@@ -147,13 +151,9 @@ public class Tracker<RAW extends Trackable, BASE extends Trackable> { | |||
} | |||
LineAndLineHashKey that = (LineAndLineHashKey) o; | |||
// start with most discriminant field | |||
if (!Objects.equals(line, that.line)) { | |||
return false; | |||
} | |||
if (!lineHash.equals(that.lineHash)) { | |||
return false; | |||
} | |||
return ruleKey.equals(that.ruleKey); | |||
return Objects.equals(line, that.line) | |||
&& lineHash.equals(that.lineHash) | |||
&& ruleKey.equals(that.ruleKey); | |||
} | |||
@Override | |||
@@ -175,7 +175,8 @@ public class Tracker<RAW extends Trackable, BASE extends Trackable> { | |||
private static class LineHashAndMessageKey implements SearchKey { | |||
private final RuleKey ruleKey; | |||
private final String message, lineHash; | |||
private final String message; | |||
private final String lineHash; | |||
LineHashAndMessageKey(Trackable trackable) { | |||
this.ruleKey = trackable.getRuleKey(); | |||
@@ -190,13 +191,9 @@ public class Tracker<RAW extends Trackable, BASE extends Trackable> { | |||
} | |||
LineHashAndMessageKey that = (LineHashAndMessageKey) o; | |||
// start with most discriminant field | |||
if (!lineHash.equals(that.lineHash)) { | |||
return false; | |||
} | |||
if (!message.equals(that.message)) { | |||
return false; | |||
} | |||
return ruleKey.equals(that.ruleKey); | |||
return lineHash.equals(that.lineHash) | |||
&& message.equals(that.message) | |||
&& ruleKey.equals(that.ruleKey); | |||
} | |||
@Override | |||
@@ -234,13 +231,9 @@ public class Tracker<RAW extends Trackable, BASE extends Trackable> { | |||
} | |||
LineAndMessageKey that = (LineAndMessageKey) o; | |||
// start with most discriminant field | |||
if (!Objects.equals(line, that.line)) { | |||
return false; | |||
} | |||
if (!message.equals(that.message)) { | |||
return false; | |||
} | |||
return ruleKey.equals(that.ruleKey); | |||
return Objects.equals(line, that.line) | |||
&& message.equals(that.message) | |||
&& ruleKey.equals(that.ruleKey); | |||
} | |||
@Override | |||
@@ -276,10 +269,8 @@ public class Tracker<RAW extends Trackable, BASE extends Trackable> { | |||
} | |||
LineAndLineHashKey that = (LineAndLineHashKey) o; | |||
// start with most discriminant field | |||
if (!lineHash.equals(that.lineHash)) { | |||
return false; | |||
} | |||
return ruleKey.equals(that.ruleKey); | |||
return lineHash.equals(that.lineHash) | |||
&& ruleKey.equals(that.ruleKey); | |||
} | |||
@Override |
@@ -28,6 +28,7 @@ import java.util.IdentityHashMap; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
public class Tracking<RAW extends Trackable, BASE extends Trackable> { | |||
@@ -112,7 +113,7 @@ public class Tracking<RAW extends Trackable, BASE extends Trackable> { | |||
return openManualIssues; | |||
} | |||
void associateManualIssueToLine(BASE manualIssue, int line) { | |||
void associateManualIssueToLine(BASE manualIssue, @Nullable Integer line) { | |||
openManualIssues.put(line, manualIssue); | |||
unmatchedBases.remove(manualIssue); | |||
} |
@@ -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()) | |||
@@ -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 { |
@@ -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(); | |||
} | |||
} |
@@ -0,0 +1,49 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.core.rule; | |||
import com.google.common.base.Function; | |||
import javax.annotation.Nonnull; | |||
import org.sonar.api.rule.RuleKey; | |||
public final class RuleKeyFunctions { | |||
private RuleKeyFunctions() { | |||
// only static methods | |||
} | |||
public static Function<String, RuleKey> stringToRuleKey() { | |||
return StringToRuleKey.INSTANCE; | |||
} | |||
/** | |||
* Transforms a string representation of key to {@link RuleKey}. It | |||
* does not accept null string inputs. | |||
*/ | |||
private enum StringToRuleKey implements Function<String, RuleKey> { | |||
INSTANCE; | |||
@Nonnull | |||
@Override | |||
public RuleKey apply(@Nonnull String input) { | |||
return RuleKey.parse(input); | |||
} | |||
} | |||
} |
@@ -58,14 +58,14 @@ | |||
order by created_at asc | |||
</select> | |||
<select id="selectChangelogOfUnresolvedIssuesByComponent" parameterType="map" resultType="IssueChange"> | |||
<select id="selectChangelogOfNonClosedIssuesByComponent" parameterType="map" resultType="IssueChange"> | |||
select | |||
<include refid="issueChangeColumns"/> | |||
from issue_changes c | |||
inner join issues i on i.kee = c.issue_key | |||
where i.component_uuid=#{componentUuid} | |||
and c.change_type=#{changeType} | |||
and i.resolution is null | |||
and i.status <> 'CLOSED' | |||
</select> | |||
</mapper> | |||
@@ -0,0 +1,219 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.core.issue; | |||
import com.google.common.collect.ImmutableMap; | |||
import org.apache.commons.lang.StringUtils; | |||
import org.junit.Test; | |||
import org.sonar.api.issue.Issue; | |||
import org.sonar.api.issue.IssueComment; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.utils.Duration; | |||
import java.text.SimpleDateFormat; | |||
import java.util.List; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.assertj.core.api.Assertions.entry; | |||
import static org.junit.Assert.fail; | |||
import static org.mockito.Mockito.mock; | |||
public class DefaultIssueTest { | |||
DefaultIssue issue = new DefaultIssue(); | |||
@Test | |||
public void test_setters_and_getters() throws Exception { | |||
issue.setKey("ABCD") | |||
.setComponentKey("org.sample.Sample") | |||
.setProjectKey("Sample") | |||
.setRuleKey(RuleKey.of("squid", "S100")) | |||
.setLanguage("xoo") | |||
.setSeverity("MINOR") | |||
.setManualSeverity(true) | |||
.setMessage("a message") | |||
.setLine(7) | |||
.setEffortToFix(1.2d) | |||
.setDebt(Duration.create(28800L)) | |||
.setActionPlanKey("BCDE") | |||
.setStatus(Issue.STATUS_CLOSED) | |||
.setResolution(Issue.RESOLUTION_FIXED) | |||
.setReporter("simon") | |||
.setAssignee("julien") | |||
.setAuthorLogin("steph") | |||
.setChecksum("c7b5db46591806455cf082bb348631e8") | |||
.setNew(true) | |||
.setBeingClosed(true) | |||
.setOnDisabledRule(true) | |||
.setChanged(true) | |||
.setSendNotifications(true) | |||
.setCreationDate(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-19")) | |||
.setUpdateDate(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-20")) | |||
.setCloseDate(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-21")) | |||
.setSelectedAt(1400000000000L); | |||
assertThat(issue.key()).isEqualTo("ABCD"); | |||
assertThat(issue.componentKey()).isEqualTo("org.sample.Sample"); | |||
assertThat(issue.projectKey()).isEqualTo("Sample"); | |||
assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("squid", "S100")); | |||
assertThat(issue.language()).isEqualTo("xoo"); | |||
assertThat(issue.severity()).isEqualTo("MINOR"); | |||
assertThat(issue.manualSeverity()).isTrue(); | |||
assertThat(issue.message()).isEqualTo("a message"); | |||
assertThat(issue.line()).isEqualTo(7); | |||
assertThat(issue.effortToFix()).isEqualTo(1.2d); | |||
assertThat(issue.debt()).isEqualTo(Duration.create(28800L)); | |||
assertThat(issue.actionPlanKey()).isEqualTo("BCDE"); | |||
assertThat(issue.status()).isEqualTo(Issue.STATUS_CLOSED); | |||
assertThat(issue.resolution()).isEqualTo(Issue.RESOLUTION_FIXED); | |||
assertThat(issue.reporter()).isEqualTo("simon"); | |||
assertThat(issue.assignee()).isEqualTo("julien"); | |||
assertThat(issue.authorLogin()).isEqualTo("steph"); | |||
assertThat(issue.checksum()).isEqualTo("c7b5db46591806455cf082bb348631e8"); | |||
assertThat(issue.isNew()).isTrue(); | |||
assertThat(issue.isBeingClosed()).isTrue(); | |||
assertThat(issue.isOnDisabledRule()).isTrue(); | |||
assertThat(issue.isChanged()).isTrue(); | |||
assertThat(issue.mustSendNotifications()).isTrue(); | |||
assertThat(issue.creationDate()).isEqualTo(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-19")); | |||
assertThat(issue.updateDate()).isEqualTo(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-20")); | |||
assertThat(issue.closeDate()).isEqualTo(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-21")); | |||
assertThat(issue.selectedAt()).isEqualTo(1400000000000L); | |||
} | |||
@Test | |||
public void set_empty_dates() { | |||
issue | |||
.setCreationDate(null) | |||
.setUpdateDate(null) | |||
.setCloseDate(null) | |||
.setSelectedAt(null); | |||
assertThat(issue.creationDate()).isNull(); | |||
assertThat(issue.updateDate()).isNull(); | |||
assertThat(issue.closeDate()).isNull(); | |||
assertThat(issue.selectedAt()).isNull(); | |||
} | |||
@Test | |||
public void test_attributes() throws Exception { | |||
assertThat(issue.attribute("foo")).isNull(); | |||
issue.setAttribute("foo", "bar"); | |||
assertThat(issue.attribute("foo")).isEqualTo("bar"); | |||
issue.setAttribute("foo", "newbar"); | |||
assertThat(issue.attribute("foo")).isEqualTo("newbar"); | |||
issue.setAttribute("foo", null); | |||
assertThat(issue.attribute("foo")).isNull(); | |||
} | |||
@Test | |||
public void setAttributes_should_not_clear_existing_values() { | |||
issue.setAttributes(ImmutableMap.of("1", "one")); | |||
assertThat(issue.attribute("1")).isEqualTo("one"); | |||
issue.setAttributes(ImmutableMap.of("2", "two")); | |||
assertThat(issue.attributes()).containsOnly(entry("1", "one"), entry("2", "two")); | |||
issue.setAttributes(null); | |||
assertThat(issue.attributes()).containsOnly(entry("1", "one"), entry("2", "two")); | |||
} | |||
@Test | |||
public void fail_on_empty_status() { | |||
try { | |||
issue.setStatus(""); | |||
fail(); | |||
} catch (IllegalArgumentException e) { | |||
assertThat(e).hasMessage("Status must be set"); | |||
} | |||
} | |||
@Test | |||
public void fail_on_bad_severity() { | |||
try { | |||
issue.setSeverity("FOO"); | |||
fail(); | |||
} catch (IllegalArgumentException e) { | |||
assertThat(e).hasMessage("Not a valid severity: FOO"); | |||
} | |||
} | |||
@Test | |||
public void message_should_be_abbreviated_if_too_long() { | |||
issue.setMessage(StringUtils.repeat("a", 5000)); | |||
assertThat(issue.message()).hasSize(4000); | |||
} | |||
@Test | |||
public void message_should_be_trimmed() { | |||
issue.setMessage(" foo "); | |||
assertThat(issue.message()).isEqualTo("foo"); | |||
} | |||
@Test | |||
public void message_could_be_null() { | |||
issue.setMessage(null); | |||
assertThat(issue.message()).isNull(); | |||
} | |||
@Test | |||
public void test_nullable_fields() throws Exception { | |||
issue.setEffortToFix(null).setSeverity(null).setLine(null); | |||
assertThat(issue.effortToFix()).isNull(); | |||
assertThat(issue.severity()).isNull(); | |||
assertThat(issue.line()).isNull(); | |||
} | |||
@Test | |||
public void test_equals_and_hashCode() throws Exception { | |||
DefaultIssue a1 = new DefaultIssue().setKey("AAA"); | |||
DefaultIssue a2 = new DefaultIssue().setKey("AAA"); | |||
DefaultIssue b = new DefaultIssue().setKey("BBB"); | |||
assertThat(a1).isEqualTo(a1); | |||
assertThat(a1).isEqualTo(a2); | |||
assertThat(a1).isNotEqualTo(b); | |||
assertThat(a1.hashCode()).isEqualTo(a1.hashCode()); | |||
} | |||
@Test | |||
public void comments_should_not_be_modifiable() { | |||
DefaultIssue issue = new DefaultIssue().setKey("AAA"); | |||
List<IssueComment> comments = issue.comments(); | |||
assertThat(comments).isEmpty(); | |||
try { | |||
comments.add(new DefaultIssueComment()); | |||
fail(); | |||
} catch (UnsupportedOperationException e) { | |||
// ok | |||
} catch (Exception e) { | |||
fail("Unexpected exception: " + e); | |||
} | |||
} | |||
@Test | |||
public void all_changes_contain_current_change() { | |||
IssueChangeContext issueChangeContext = mock(IssueChangeContext.class); | |||
DefaultIssue issue = new DefaultIssue().setKey("AAA").setFieldChange(issueChangeContext, "actionPlan", "1.0", "1.1"); | |||
assertThat(issue.changes()).hasSize(1); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -113,11 +113,11 @@ public class IssueChangeDaoTest extends AbstractDaoTestCase { | |||
} | |||
@Test | |||
public void selectChangelogOfUnresolvedIssuesByComponent() { | |||
setupData("selectChangelogOfUnresolvedIssuesByComponent"); | |||
public void selectChangelogOfNonClosedIssuesByComponent() { | |||
setupData("selectChangelogOfNonClosedIssuesByComponent"); | |||
List<IssueChangeDto> dtos = dao.selectChangelogOfUnresolvedIssuesByComponent("FILE_1"); | |||
assertThat(dtos).extracting("id").containsExactly(100L); | |||
List<IssueChangeDto> dtos = dao.selectChangelogOfNonClosedIssuesByComponent("FILE_1"); | |||
assertThat(dtos).extracting("id").containsExactly(100L, 103L); | |||
} | |||
@Test |
@@ -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<Issue, Issue> tracking = tracker.track(rawInput, baseInput); | |||
assertThat(tracking.baseFor(raw)).isNull(); | |||
assertThat(tracking.getUnmatchedBases()).containsOnly(base); | |||
} | |||
@Test | |||
public void line_hash_has_greater_priority_than_line() { | |||
FakeInput baseInput = new FakeInput("H1", "H2", "H3"); | |||
@@ -369,6 +358,65 @@ public class TrackerTest { | |||
assertThat(tracking.getUnmatchedBases()).containsOnly(base2); | |||
} | |||
@Test | |||
public void move_manual_issue_to_line_with_same_hash() { | |||
FakeInput baseInput = new FakeInput("H1", "H2"); | |||
Issue issue = baseInput.createIssueOnLine(1, RULE_MANUAL, "message"); | |||
FakeInput rawInput = new FakeInput("H3", "H4", "H1"); | |||
Tracking<Issue, Issue> tracking = tracker.track(rawInput, baseInput); | |||
assertThat(tracking.getUnmatchedBases()).isEmpty(); | |||
Multimap<Integer, Issue> openManualIssues = tracking.getOpenManualIssuesByLine(); | |||
assertThat(openManualIssues.keySet()).containsOnly(3); | |||
assertThat(Iterables.getOnlyElement(openManualIssues.get(3))).isSameAs(issue); | |||
} | |||
@Test | |||
public void do_not_move_manual_issue_if_line_hash_not_found_in_raw() { | |||
FakeInput baseInput = new FakeInput("H1", "H2"); | |||
Issue issue = baseInput.createIssueOnLine(1, RULE_MANUAL, "message"); | |||
FakeInput rawInput = new FakeInput("H3", "H4", "H5"); | |||
Tracking<Issue, Issue> tracking = tracker.track(rawInput, baseInput); | |||
assertThat(tracking.getUnmatchedBases()).isEmpty(); | |||
Multimap<Integer, Issue> openManualIssues = tracking.getOpenManualIssuesByLine(); | |||
assertThat(openManualIssues.keySet()).containsOnly(1); | |||
assertThat(Iterables.getOnlyElement(openManualIssues.get(1))).isSameAs(issue); | |||
} | |||
@Test | |||
public void do_not_match_manual_issue_if_hash_and_line_do_not_exist() { | |||
// manual issue is on line 3 (hash H3) but this hash does not exist | |||
// anymore nor the line 3. | |||
FakeInput baseInput = new FakeInput("H1", "H2", "H3"); | |||
Issue issue = baseInput.createIssueOnLine(3, RULE_MANUAL, "message"); | |||
FakeInput rawInput = new FakeInput("H4"); | |||
Tracking<Issue, Issue> tracking = tracker.track(rawInput, baseInput); | |||
assertThat(tracking.getUnmatchedBases()).containsOnly(issue); | |||
assertThat(tracking.getOpenManualIssuesByLine().isEmpty()).isTrue(); | |||
} | |||
@Test | |||
@Ignore("not implemented yet") | |||
public void move_to_closest_line_if_manual_issue_matches_multiple_hashes() { | |||
// manual issue is on line 3 (hash H3) but this hash does not exist | |||
// anymore nor the line 3. | |||
FakeInput baseInput = new FakeInput("H1", "H2"); | |||
Issue issue = baseInput.createIssueOnLine(1, RULE_MANUAL, "message"); | |||
FakeInput rawInput = new FakeInput("H1", "H3", "H1"); | |||
Tracking<Issue, Issue> tracking = tracker.track(rawInput, baseInput); | |||
assertThat(tracking.getUnmatchedBases()).isEmpty(); | |||
Multimap<Integer, Issue> openManualIssues = tracking.getOpenManualIssuesByLine(); | |||
assertThat(openManualIssues.keySet()).containsOnly(1); | |||
assertThat(Iterables.getOnlyElement(openManualIssues.get(1))).isSameAs(issue); | |||
} | |||
private static class Issue implements Trackable { | |||
private final RuleKey ruleKey; | |||
private final Integer line; |
@@ -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(); | |||
@@ -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); |
@@ -0,0 +1,46 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.core.rule; | |||
import java.util.Arrays; | |||
import java.util.Collection; | |||
import java.util.List; | |||
import org.junit.Test; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.test.TestUtils; | |||
import static com.google.common.collect.FluentIterable.from; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
public class RuleKeyFunctionsTest { | |||
@Test | |||
public void stringToRuleKey() throws Exception { | |||
Collection<String> strings = Arrays.asList("js:S001", "java:S002"); | |||
List<RuleKey> keys = from(strings).transform(RuleKeyFunctions.stringToRuleKey()).toList(); | |||
assertThat(keys).containsExactly(RuleKey.of("js", "S001"), RuleKey.of("java", "S002")); | |||
} | |||
@Test | |||
public void on_static_methods() throws Exception { | |||
assertThat(TestUtils.hasOnlyPrivateConstructors(RuleKeyFunctions.class)).isTrue(); | |||
} | |||
} |
@@ -1,6 +1,6 @@ | |||
<dataset> | |||
<!-- Unresolved --> | |||
<!-- Unresolved. To be included --> | |||
<issues | |||
id="1" | |||
kee="UNRESOLVED_ON_FILE_1" | |||
@@ -52,7 +52,7 @@ | |||
issue_change_creation_date="[null]" | |||
/> | |||
<!-- Resolved: to be ignored --> | |||
<!-- Resolved but not closed. To be included --> | |||
<issues | |||
id="2" | |||
kee="RESOLVED_ON_FILE_1" | |||
@@ -90,9 +90,47 @@ | |||
issue_change_creation_date="1410213600000" | |||
/> | |||
<!-- Closed. To be excluded --> | |||
<issues | |||
id="3" | |||
kee="CLOSED_ON_FILE_1" | |||
component_uuid="FILE_1" | |||
project_uuid="PROJECT_1" | |||
resolution="FIXED" | |||
status="CLOSED" | |||
rule_id="501" | |||
severity="MAJOR" | |||
manual_severity="[false]" | |||
message="[null]" | |||
line="120" | |||
effort_to_fix="[null]" | |||
checksum="[null]" | |||
reporter="[null]" | |||
assignee="user" | |||
author_login="[null]" | |||
issue_attributes="[null]" | |||
issue_creation_date="1366063200000" | |||
issue_update_date="1366063200000" | |||
issue_close_date="1366063200000" | |||
created_at="1400000000000" | |||
updated_at="[null]" | |||
/> | |||
<issue_changes | |||
id="104" | |||
kee="104" | |||
issue_key="CLOSED_ON_FILE_1" | |||
user_login="arthur" | |||
change_type="diff" | |||
change_data="severity=MAJOR|BLOCKER" | |||
created_at="1410213600000" | |||
updated_at="1410213600000" | |||
issue_change_creation_date="1410213600000" | |||
/> | |||
<!-- Unresolved on other file --> | |||
<issues | |||
id="3" | |||
id="4" | |||
kee="UNRESOLVED_ON_FILE_2" | |||
component_uuid="FILE_2" | |||
project_uuid="PROJECT_1" | |||
@@ -118,8 +156,8 @@ | |||
<!-- diff --> | |||
<issue_changes | |||
id="104" | |||
kee="104" | |||
id="105" | |||
kee="105" | |||
issue_key="UNRESOLVED_ON_FILE_2" | |||
user_login="arthur" | |||
change_type="diff" | |||
@@ -131,8 +169,8 @@ | |||
<!-- comment --> | |||
<issue_changes | |||
id="105" | |||
kee="105" | |||
id="106" | |||
kee="106" | |||
issue_key="UNRESOLVED_ON_FILE_2" | |||
user_login="arthur" | |||
change_type="comment" |
@@ -50,11 +50,19 @@ public interface Rule { | |||
RuleStatus status(); | |||
/** | |||
* Remediation function : can by Linear (with a coefficient), Linear with offset (with a coefficient and an offset) or Constant per issue (with an offset) | |||
* | |||
* @since 4.3 | |||
* @deprecated since 5.2 as any computation of data are moved to server's Compute Engine. Calling this method throws an exception. | |||
*/ | |||
@CheckForNull | |||
@Deprecated | |||
String debtSubCharacteristic(); | |||
/** | |||
* @since 4.3 | |||
* @deprecated since 5.2 as any computation of data are moved to server's Compute Engine. Calling this method throws an exception. | |||
*/ | |||
@CheckForNull | |||
@Deprecated | |||
DebtRemediationFunction debtRemediationFunction(); | |||
} |
@@ -42,7 +42,6 @@ public class DefaultRule implements Rule { | |||
private final String description; | |||
private final String internalKey; | |||
private final RuleStatus status; | |||
private final DebtRemediationFunction debtRemediationFunction; | |||
private final Map<String, RuleParam> params; | |||
DefaultRule(NewRule newRule) { | |||
@@ -53,7 +52,6 @@ public class DefaultRule implements Rule { | |||
this.description = newRule.description; | |||
this.internalKey = newRule.internalKey; | |||
this.status = newRule.status; | |||
this.debtRemediationFunction = newRule.debtRemediationFunction; | |||
ImmutableMap.Builder<String, RuleParam> builder = ImmutableMap.builder(); | |||
for (NewRuleParam newRuleParam : newRule.params.values()) { | |||
@@ -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 |
@@ -19,18 +19,15 @@ | |||
*/ | |||
package org.sonar.api.batch.rule.internal; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import javax.annotation.Nullable; | |||
import org.apache.commons.lang.ObjectUtils; | |||
import org.apache.commons.lang.StringUtils; | |||
import org.sonar.api.batch.debt.DebtRemediationFunction; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.rule.RuleStatus; | |||
import org.sonar.api.rule.Severity; | |||
import javax.annotation.Nullable; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
public class NewRule { | |||
private static final String DEFAULT_SEVERITY = Severity.defaultSeverity(); | |||
@@ -41,7 +38,6 @@ public class NewRule { | |||
String description; | |||
String severity = DEFAULT_SEVERITY; | |||
String internalKey; | |||
DebtRemediationFunction debtRemediationFunction; | |||
RuleStatus status = RuleStatus.defaultStatus(); | |||
Map<String, NewRuleParam> params = new HashMap<>(); | |||
@@ -79,11 +75,6 @@ public class NewRule { | |||
return this; | |||
} | |||
public NewRule setDebtRemediationFunction(@Nullable DebtRemediationFunction f) { | |||
this.debtRemediationFunction = f; | |||
return this; | |||
} | |||
public NewRuleParam addParam(String paramKey) { | |||
if (params.containsKey(paramKey)) { | |||
throw new IllegalStateException(String.format("Parameter '%s' already exists on rule '%s'", paramKey, key)); |
@@ -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)); | |||
} | |||
} |
@@ -20,17 +20,19 @@ | |||
package org.sonar.api.measures; | |||
import com.google.common.annotations.Beta; | |||
import java.io.Serializable; | |||
import java.math.BigDecimal; | |||
import java.math.RoundingMode; | |||
import java.util.Date; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
import org.apache.commons.lang.builder.ReflectionToStringBuilder; | |||
import org.apache.commons.lang.math.NumberUtils; | |||
import org.sonar.api.technicaldebt.batch.Characteristic; | |||
import org.sonar.api.technicaldebt.batch.Requirement; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
import java.io.Serializable; | |||
import java.math.BigDecimal; | |||
import java.math.RoundingMode; | |||
import java.util.Date; | |||
/** | |||
* A class to handle measures. | |||
* | |||
@@ -60,6 +62,8 @@ public class Measure<G extends Serializable> implements Serializable { | |||
protected Double variation4; | |||
protected Double variation5; | |||
protected String url; | |||
protected Characteristic characteristic; | |||
protected Requirement requirement; | |||
protected Integer personId; | |||
protected PersistenceMode persistenceMode = PersistenceMode.FULL; | |||
private boolean fromCore; | |||
@@ -627,13 +631,14 @@ public class Measure<G extends Serializable> implements Serializable { | |||
*/ | |||
@CheckForNull | |||
public final Characteristic getCharacteristic() { | |||
return null; | |||
return characteristic; | |||
} | |||
/** | |||
* @since 4.1 | |||
*/ | |||
public final Measure<G> setCharacteristic(@Nullable Characteristic characteristic) { | |||
this.characteristic = characteristic; | |||
return this; | |||
} | |||
@@ -644,7 +649,7 @@ public class Measure<G extends Serializable> implements Serializable { | |||
@Deprecated | |||
@CheckForNull | |||
public final Requirement getRequirement() { | |||
return null; | |||
return requirement; | |||
} | |||
/** | |||
@@ -653,6 +658,7 @@ public class Measure<G extends Serializable> implements Serializable { | |||
*/ | |||
@Deprecated | |||
public final Measure<G> setRequirement(@Nullable Requirement requirement) { | |||
this.requirement = requirement; | |||
return this; | |||
} | |||
@@ -731,12 +737,16 @@ public class Measure<G extends Serializable> implements Serializable { | |||
if (metricKey != null ? !metricKey.equals(measure.metricKey) : (measure.metricKey != null)) { | |||
return false; | |||
} | |||
if (characteristic != null ? !characteristic.equals(measure.characteristic) : (measure.characteristic != null)) { | |||
return false; | |||
} | |||
return !(personId != null ? !personId.equals(measure.personId) : (measure.personId != null)); | |||
} | |||
@Override | |||
public int hashCode() { | |||
int result = metricKey != null ? metricKey.hashCode() : 0; | |||
result = 31 * result + (characteristic != null ? characteristic.hashCode() : 0); | |||
result = 31 * result + (personId != null ? personId.hashCode() : 0); | |||
return result; | |||
} |
@@ -205,7 +205,7 @@ public final class MeasuresFilters { | |||
} | |||
/** | |||
* @deprecated since 5.2. Useless by design because of Compute Engine | |||
* @deprecated since 5.2. The measures related to rules are computed on server side by Compute Engine. | |||
*/ | |||
@Deprecated | |||
private abstract static class AbstractRuleMeasureFilter<M> extends MetricFilter<M> { |
@@ -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"; |
@@ -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(); |
@@ -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(); | |||
} | |||
@@ -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"); | |||
} | |||
} |
@@ -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(""); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -1,238 +0,0 @@ | |||
/* | |||
* SonarQube, open source software quality management tool. | |||
* Copyright (C) 2008-2014 SonarSource | |||
* mailto:contact AT sonarsource DOT com | |||
* | |||
* SonarQube is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* SonarQube is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
///* | |||
// * SonarQube, open source software quality management tool. | |||
// * Copyright (C) 2008-2014 SonarSource | |||
// * mailto:contact AT sonarsource DOT com | |||
// * | |||
// * SonarQube is free software; you can redistribute it and/or | |||
// * modify it under the terms of the GNU Lesser General Public | |||
// * License as published by the Free Software Foundation; either | |||
// * version 3 of the License, or (at your option) any later version. | |||
// * | |||
// * SonarQube is distributed in the hope that it will be useful, | |||
// * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
// * Lesser General Public License for more details. | |||
// * | |||
// * You should have received a copy of the GNU Lesser General Public License | |||
// * along with this program; if not, write to the Free Software Foundation, | |||
// * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
// */ | |||
//package org.sonar.api.issue.internal; | |||
// | |||
//import com.google.common.collect.ImmutableMap; | |||
//import org.apache.commons.lang.StringUtils; | |||
//import org.junit.Test; | |||
//import org.sonar.api.issue.Issue; | |||
//import org.sonar.api.issue.IssueComment; | |||
//import org.sonar.api.rule.RuleKey; | |||
//import org.sonar.api.utils.Duration; | |||
// | |||
//import java.text.SimpleDateFormat; | |||
//import java.util.List; | |||
// | |||
//import static org.assertj.core.api.Assertions.assertThat; | |||
//import static org.assertj.core.api.Assertions.entry; | |||
//import static org.junit.Assert.fail; | |||
//import static org.mockito.Mockito.mock; | |||
// | |||
//public class DefaultIssueTest { | |||
// | |||
// DefaultIssue issue = new DefaultIssue(); | |||
// | |||
// @Test | |||
// public void test_setters_and_getters() throws Exception { | |||
// issue.setKey("ABCD") | |||
// .setComponentKey("org.sample.Sample") | |||
// .setProjectKey("Sample") | |||
// .setRuleKey(RuleKey.of("squid", "S100")) | |||
// .setLanguage("xoo") | |||
// .setSeverity("MINOR") | |||
// .setManualSeverity(true) | |||
// .setMessage("a message") | |||
// .setLine(7) | |||
// .setEffortToFix(1.2d) | |||
// .setDebt(Duration.create(28800L)) | |||
// .setActionPlanKey("BCDE") | |||
// .setStatus(Issue.STATUS_CLOSED) | |||
// .setResolution(Issue.RESOLUTION_FIXED) | |||
// .setReporter("simon") | |||
// .setAssignee("julien") | |||
// .setAuthorLogin("steph") | |||
// .setChecksum("c7b5db46591806455cf082bb348631e8") | |||
// .setNew(true) | |||
// .setBeingClosed(true) | |||
// .setOnDisabledRule(true) | |||
// .setChanged(true) | |||
// .setSendNotifications(true) | |||
// .setCreationDate(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-19")) | |||
// .setUpdateDate(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-20")) | |||
// .setCloseDate(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-21")) | |||
// .setSelectedAt(1400000000000L); | |||
// | |||
// assertThat(issue.key()).isEqualTo("ABCD"); | |||
// assertThat(issue.componentKey()).isEqualTo("org.sample.Sample"); | |||
// assertThat(issue.projectKey()).isEqualTo("Sample"); | |||
// assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("squid", "S100")); | |||
// assertThat(issue.language()).isEqualTo("xoo"); | |||
// assertThat(issue.severity()).isEqualTo("MINOR"); | |||
// assertThat(issue.manualSeverity()).isTrue(); | |||
// assertThat(issue.message()).isEqualTo("a message"); | |||
// assertThat(issue.line()).isEqualTo(7); | |||
// assertThat(issue.effortToFix()).isEqualTo(1.2d); | |||
// assertThat(issue.debt()).isEqualTo(Duration.create(28800L)); | |||
// assertThat(issue.actionPlanKey()).isEqualTo("BCDE"); | |||
// assertThat(issue.status()).isEqualTo(Issue.STATUS_CLOSED); | |||
// assertThat(issue.resolution()).isEqualTo(Issue.RESOLUTION_FIXED); | |||
// assertThat(issue.reporter()).isEqualTo("simon"); | |||
// assertThat(issue.assignee()).isEqualTo("julien"); | |||
// assertThat(issue.authorLogin()).isEqualTo("steph"); | |||
// assertThat(issue.checksum()).isEqualTo("c7b5db46591806455cf082bb348631e8"); | |||
// assertThat(issue.isNew()).isTrue(); | |||
// assertThat(issue.isBeingClosed()).isTrue(); | |||
// assertThat(issue.isOnDisabledRule()).isTrue(); | |||
// assertThat(issue.isChanged()).isTrue(); | |||
// assertThat(issue.mustSendNotifications()).isTrue(); | |||
// assertThat(issue.creationDate()).isEqualTo(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-19")); | |||
// assertThat(issue.updateDate()).isEqualTo(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-20")); | |||
// assertThat(issue.closeDate()).isEqualTo(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-21")); | |||
// assertThat(issue.selectedAt()).isEqualTo(1400000000000L); | |||
// } | |||
// | |||
// @Test | |||
// public void set_empty_dates() { | |||
// issue | |||
// .setCreationDate(null) | |||
// .setUpdateDate(null) | |||
// .setCloseDate(null) | |||
// .setSelectedAt(null); | |||
// | |||
// assertThat(issue.creationDate()).isNull(); | |||
// assertThat(issue.updateDate()).isNull(); | |||
// assertThat(issue.closeDate()).isNull(); | |||
// assertThat(issue.selectedAt()).isNull(); | |||
// } | |||
// | |||
// @Test | |||
// public void test_attributes() throws Exception { | |||
// assertThat(issue.attribute("foo")).isNull(); | |||
// issue.setAttribute("foo", "bar"); | |||
// assertThat(issue.attribute("foo")).isEqualTo("bar"); | |||
// issue.setAttribute("foo", "newbar"); | |||
// assertThat(issue.attribute("foo")).isEqualTo("newbar"); | |||
// issue.setAttribute("foo", null); | |||
// assertThat(issue.attribute("foo")).isNull(); | |||
// } | |||
// | |||
// @Test | |||
// public void setAttributes_should_not_clear_existing_values() { | |||
// issue.setAttributes(ImmutableMap.of("1", "one")); | |||
// assertThat(issue.attribute("1")).isEqualTo("one"); | |||
// | |||
// issue.setAttributes(ImmutableMap.of("2", "two")); | |||
// assertThat(issue.attributes()).containsOnly(entry("1", "one"), entry("2", "two")); | |||
// | |||
// issue.setAttributes(null); | |||
// assertThat(issue.attributes()).containsOnly(entry("1", "one"), entry("2", "two")); | |||
// } | |||
// | |||
// @Test | |||
// public void fail_on_empty_status() { | |||
// try { | |||
// issue.setStatus(""); | |||
// fail(); | |||
// } catch (IllegalArgumentException e) { | |||
// assertThat(e).hasMessage("Status must be set"); | |||
// } | |||
// } | |||
// | |||
// @Test | |||
// public void fail_on_bad_severity() { | |||
// try { | |||
// issue.setSeverity("FOO"); | |||
// fail(); | |||
// } catch (IllegalArgumentException e) { | |||
// assertThat(e).hasMessage("Not a valid severity: FOO"); | |||
// } | |||
// } | |||
// | |||
// @Test | |||
// public void message_should_be_abbreviated_if_too_long() { | |||
// issue.setMessage(StringUtils.repeat("a", 5000)); | |||
// assertThat(issue.message()).hasSize(4000); | |||
// } | |||
// | |||
// @Test | |||
// public void message_should_be_trimmed() { | |||
// issue.setMessage(" foo "); | |||
// assertThat(issue.message()).isEqualTo("foo"); | |||
// } | |||
// | |||
// @Test | |||
// public void message_could_be_null() { | |||
// issue.setMessage(null); | |||
// assertThat(issue.message()).isNull(); | |||
// } | |||
// | |||
// @Test | |||
// public void test_nullable_fields() throws Exception { | |||
// issue.setEffortToFix(null).setSeverity(null).setLine(null); | |||
// assertThat(issue.effortToFix()).isNull(); | |||
// assertThat(issue.severity()).isNull(); | |||
// assertThat(issue.line()).isNull(); | |||
// } | |||
// | |||
// @Test | |||
// public void test_equals_and_hashCode() throws Exception { | |||
// DefaultIssue a1 = new DefaultIssue().setKey("AAA"); | |||
// DefaultIssue a2 = new DefaultIssue().setKey("AAA"); | |||
// DefaultIssue b = new DefaultIssue().setKey("BBB"); | |||
// assertThat(a1).isEqualTo(a1); | |||
// assertThat(a1).isEqualTo(a2); | |||
// assertThat(a1).isNotEqualTo(b); | |||
// assertThat(a1.hashCode()).isEqualTo(a1.hashCode()); | |||
// } | |||
// | |||
// @Test | |||
// public void comments_should_not_be_modifiable() { | |||
// DefaultIssue issue = new DefaultIssue().setKey("AAA"); | |||
// | |||
// List<IssueComment> comments = issue.comments(); | |||
// assertThat(comments).isEmpty(); | |||
// | |||
// try { | |||
// comments.add(new DefaultIssueComment()); | |||
// fail(); | |||
// } catch (UnsupportedOperationException e) { | |||
// // ok | |||
// } catch (Exception e) { | |||
// fail("Unexpected exception: " + e); | |||
// } | |||
// } | |||
// | |||
// @Test | |||
// public void all_changes_contain_current_change() { | |||
// IssueChangeContext issueChangeContext = mock(IssueChangeContext.class); | |||
// DefaultIssue issue = new DefaultIssue().setKey("AAA").setFieldChange(issueChangeContext, "actionPlan", "1.0", "1.1"); | |||
// | |||
// assertThat(issue.changes()).hasSize(1); | |||
// } | |||
//} |
@@ -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(); | |||
// } | |||
//} |