aboutsummaryrefslogtreecommitdiffstats
path: root/plugins/sonar-core-plugin
diff options
context:
space:
mode:
authorJulien Lancelot <julien.lancelot@sonarsource.com>2013-11-08 15:59:29 +0100
committerJulien Lancelot <julien.lancelot@sonarsource.com>2013-11-08 15:59:29 +0100
commitd27d70998b977ccdd57bcfeb41347e58af368b48 (patch)
treec74738765e1ffae6215c08bf6cda34dacb6d680d /plugins/sonar-core-plugin
parent2219ae6127f6e5d6eca9fd5408318ae61141b71a (diff)
downloadsonarqube-d27d70998b977ccdd57bcfeb41347e58af368b48.tar.gz
sonarqube-d27d70998b977ccdd57bcfeb41347e58af368b48.zip
SONAR-4776 Load issues changelog at the begin of each module analysis
Diffstat (limited to 'plugins/sonar-core-plugin')
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java4
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/InitialOpenIssuesSensor.java14
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/InitialOpenIssuesStack.java44
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTrackingDecorator.java11
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/NewTechnicalDebtDecorator.java29
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/InitialOpenIssuesSensorTest.java15
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/InitialOpenIssuesStackTest.java53
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/IssueTrackingDecoratorTest.java22
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/NewTechnicalDebtDecoratorTest.java124
9 files changed, 191 insertions, 125 deletions
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
index 265dcba65df..cd2f6d12bed 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
@@ -26,7 +26,6 @@ import org.sonar.api.config.PropertyDefinition;
import org.sonar.api.resources.Java;
import org.sonar.api.resources.Qualifiers;
import org.sonar.batch.components.PastSnapshotFinder;
-import org.sonar.core.issue.IssueChangelogFinder;
import org.sonar.core.technicaldebt.TechnicalDebtCalculator;
import org.sonar.core.technicaldebt.TechnicalDebtConverter;
import org.sonar.core.timemachine.Periods;
@@ -277,7 +276,6 @@ public final class CorePlugin extends SonarPlugin {
UnresolvedIssuesStatusesWidget.class,
IssueFilterWidget.class,
org.sonar.api.issue.NoSonarFilter.class,
- IssueChangelogFinder.class,
// issue notifications
SendIssueNotificationsPostJob.class,
@@ -396,7 +394,7 @@ public final class CorePlugin extends SonarPlugin {
.type(PropertyType.BOOLEAN)
.category(CoreProperties.CATEGORY_SECURITY)
.build()
- );
+ );
}
}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/InitialOpenIssuesSensor.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/InitialOpenIssuesSensor.java
index cff5ec73080..1a333be0f5a 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/InitialOpenIssuesSensor.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/InitialOpenIssuesSensor.java
@@ -25,6 +25,8 @@ import org.apache.ibatis.session.ResultHandler;
import org.sonar.api.batch.Sensor;
import org.sonar.api.batch.SensorContext;
import org.sonar.api.resources.Project;
+import org.sonar.core.issue.db.IssueChangeDao;
+import org.sonar.core.issue.db.IssueChangeDto;
import org.sonar.core.issue.db.IssueDao;
import org.sonar.core.issue.db.IssueDto;
@@ -38,10 +40,12 @@ public class InitialOpenIssuesSensor implements Sensor {
private final InitialOpenIssuesStack initialOpenIssuesStack;
private final IssueDao issueDao;
+ private final IssueChangeDao issueChangeDao;
- public InitialOpenIssuesSensor(InitialOpenIssuesStack initialOpenIssuesStack, IssueDao issueDao) {
+ public InitialOpenIssuesSensor(InitialOpenIssuesStack initialOpenIssuesStack, IssueDao issueDao, IssueChangeDao issueChangeDao) {
this.initialOpenIssuesStack = initialOpenIssuesStack;
this.issueDao = issueDao;
+ this.issueChangeDao = issueChangeDao;
}
@Override
@@ -63,6 +67,14 @@ public class InitialOpenIssuesSensor implements Sensor {
initialOpenIssuesStack.addIssue(dto);
}
});
+
+ issueChangeDao.selectChangelogOnNonClosedIssuesByModuleAndType(project.getId(), new ResultHandler() {
+ @Override
+ public void handleResult(ResultContext rc) {
+ IssueChangeDto dto = (IssueChangeDto) rc.getResultObject();
+ initialOpenIssuesStack.addChangelog(dto);
+ }
+ });
}
@Override
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/InitialOpenIssuesStack.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/InitialOpenIssuesStack.java
index d3a4c969c27..4646224dd03 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/InitialOpenIssuesStack.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/InitialOpenIssuesStack.java
@@ -20,44 +20,66 @@
package org.sonar.plugins.core.issue;
-import com.google.common.collect.Lists;
+import edu.emory.mathcs.backport.java.util.Collections;
import org.sonar.api.BatchExtension;
import org.sonar.api.batch.InstantiationStrategy;
import org.sonar.batch.index.Cache;
import org.sonar.batch.index.Caches;
+import org.sonar.core.issue.db.IssueChangeDto;
import org.sonar.core.issue.db.IssueDto;
+import java.util.ArrayList;
import java.util.List;
+import static com.google.common.collect.Lists.newArrayList;
+
@InstantiationStrategy(InstantiationStrategy.PER_BATCH)
public class InitialOpenIssuesStack implements BatchExtension {
- private final Cache<String, IssueDto> cache;
+ private final Cache<String, IssueDto> issuesCache;
+ private final Cache<String, ArrayList<IssueChangeDto>> issuesChangelogCache;
public InitialOpenIssuesStack(Caches caches) {
- cache = caches.createCache("last-open-issues");
+ issuesCache = caches.createCache("last-open-issues");
+ issuesChangelogCache = caches.createCache("issues-changelog");
}
public InitialOpenIssuesStack addIssue(IssueDto issueDto) {
- cache.put(issueDto.getComponentKey(), issueDto.getKee(), issueDto);
+ issuesCache.put(issueDto.getComponentKey(), issueDto.getKee(), issueDto);
return this;
}
- public List<IssueDto> selectAndRemove(String componentKey) {
- Iterable<IssueDto> issues = cache.values(componentKey);
- List<IssueDto> result = Lists.newArrayList();
+ public List<IssueDto> selectAndRemoveIssues(String componentKey) {
+ Iterable<IssueDto> issues = issuesCache.values(componentKey);
+ List<IssueDto> result = newArrayList();
for (IssueDto issue : issues) {
result.add(issue);
}
- cache.clear(componentKey);
+ issuesCache.clear(componentKey);
return result;
}
- public Iterable<IssueDto> selectAll() {
- return cache.allValues();
+ public Iterable<IssueDto> selectAllIssues() {
+ return issuesCache.allValues();
+ }
+
+ public InitialOpenIssuesStack addChangelog(IssueChangeDto issueChangeDto) {
+ ArrayList<IssueChangeDto> changeDtos = issuesChangelogCache.get(issueChangeDto.getIssueKey());
+ if (changeDtos == null) {
+ changeDtos = newArrayList();
+ }
+ changeDtos.add(issueChangeDto);
+ issuesChangelogCache.put(issueChangeDto.getIssueKey(), changeDtos);
+ return this;
+ }
+
+ public List<IssueChangeDto> selectChangelog(String issueKey) {
+ List<IssueChangeDto> changeDtos = issuesChangelogCache.get(issueKey);
+ return changeDtos != null ? changeDtos : Collections.emptyList();
}
public void clear() {
- cache.clearAll();
+ issuesCache.clearAll();
+ issuesChangelogCache.clearAll();
}
}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTrackingDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTrackingDecorator.java
index d244dce5501..90603e96ce7 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTrackingDecorator.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/IssueTrackingDecorator.java
@@ -43,6 +43,7 @@ import org.sonar.api.utils.KeyValueFormat;
import org.sonar.batch.issue.IssueCache;
import org.sonar.batch.scan.LastSnapshots;
import org.sonar.core.issue.IssueUpdater;
+import org.sonar.core.issue.db.IssueChangeDto;
import org.sonar.core.issue.db.IssueDto;
import org.sonar.core.issue.workflow.IssueWorkflow;
@@ -110,7 +111,7 @@ public class IssueTrackingDecorator implements Decorator {
// issues = all the issues created by rule engines during this module scan and not excluded by filters
// all the issues that are not closed in db before starting this module scan, including manual issues
- Collection<IssueDto> dbOpenIssues = initialOpenIssues.selectAndRemove(resource.getEffectiveKey());
+ Collection<IssueDto> dbOpenIssues = initialOpenIssues.selectAndRemoveIssues(resource.getEffectiveKey());
SourceHashHolder sourceHashHolder = new SourceHashHolder(index, lastSnapshots, resource);
@@ -160,6 +161,12 @@ public class IssueTrackingDecorator implements Decorator {
issue.setAttributes(KeyValueFormat.parse(ref.getIssueAttributes()));
}
+ // populate existing changelog
+ Collection<IssueChangeDto> issueChangeDtos = initialOpenIssues.selectChangelog(issue.key());
+ for (IssueChangeDto issueChangeDto : issueChangeDtos) {
+ issue.addChange(issueChangeDto.toFieldDiffs());
+ }
+
// fields to update with current values
if (ref.isManualSeverity()) {
issue.setManualSeverity(true);
@@ -188,7 +195,7 @@ public class IssueTrackingDecorator implements Decorator {
}
private void addIssuesOnDeletedComponents(Collection<DefaultIssue> issues) {
- for (IssueDto deadDto : initialOpenIssues.selectAll()) {
+ for (IssueDto deadDto : initialOpenIssues.selectAllIssues()) {
DefaultIssue dead = deadDto.toDefaultIssue();
updateUnmatchedIssue(dead, true);
issues.add(dead);
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/NewTechnicalDebtDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/NewTechnicalDebtDecorator.java
index 7e003efd5c6..3f22b68a80a 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/NewTechnicalDebtDecorator.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/technicaldebt/NewTechnicalDebtDecorator.java
@@ -21,8 +21,6 @@
package org.sonar.plugins.core.technicaldebt;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Multimap;
import org.apache.commons.lang.time.DateUtils;
import org.sonar.api.batch.*;
import org.sonar.api.component.ResourcePerspectives;
@@ -39,7 +37,6 @@ import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
import org.sonar.batch.components.Period;
import org.sonar.batch.components.TimeMachineConfiguration;
-import org.sonar.core.issue.IssueChangelogFinder;
import org.sonar.core.issue.IssueUpdater;
import org.sonar.core.technicaldebt.TechnicalDebtConverter;
@@ -58,14 +55,11 @@ public final class NewTechnicalDebtDecorator implements Decorator {
private final ResourcePerspectives perspectives;
private final TimeMachineConfiguration timeMachineConfiguration;
- private final IssueChangelogFinder changelogFinder;
private final TechnicalDebtConverter technicalDebtConverter;
- public NewTechnicalDebtDecorator(ResourcePerspectives perspectives, TimeMachineConfiguration timeMachineConfiguration, IssueChangelogFinder changelogFinder,
- TechnicalDebtConverter technicalDebtConverter) {
+ public NewTechnicalDebtDecorator(ResourcePerspectives perspectives, TimeMachineConfiguration timeMachineConfiguration, TechnicalDebtConverter technicalDebtConverter) {
this.perspectives = perspectives;
this.timeMachineConfiguration = timeMachineConfiguration;
- this.changelogFinder = changelogFinder;
this.technicalDebtConverter = technicalDebtConverter;
}
@@ -84,16 +78,15 @@ public final class NewTechnicalDebtDecorator implements Decorator {
Issuable issuable = perspectives.as(Issuable.class, resource);
if (issuable != null && shouldSaveNewMetrics(context)) {
List<Issue> issues = newArrayList(issuable.issues());
- Multimap<Issue, FieldDiffs> changelogList = changelogFinder.findByIssues(issues);
- saveMeasures(context, issues, changelogList);
+ saveMeasures(context, issues);
}
}
- private void saveMeasures(DecoratorContext context, Collection<Issue> issues, Multimap<Issue, FieldDiffs> changelogList) {
+ private void saveMeasures(DecoratorContext context, Collection<Issue> issues) {
Measure measure = new Measure(CoreMetrics.NEW_TECHNICAL_DEBT);
for (Period period : timeMachineConfiguration.periods()) {
Date periodDate = period.getDate() != null ? DateUtils.addSeconds(period.getDate(), 1) : null;
- double value = calculateNewTechnicalDebtValue(issues, changelogList, periodDate);
+ double value = calculateNewTechnicalDebtValue(issues, periodDate);
Collection<Measure> children = context.getChildrenMeasures(measure.getMetric());
double sum = MeasureUtils.sumOnVariation(true, period.getIndex(), children) + value;
measure.setVariation(period.getIndex(), sum);
@@ -101,11 +94,11 @@ public final class NewTechnicalDebtDecorator implements Decorator {
context.saveMeasure(measure);
}
- private Double calculateNewTechnicalDebtValue(Collection<Issue> issues, Multimap<Issue, FieldDiffs> changelogList, @Nullable Date periodDate){
+ private Double calculateNewTechnicalDebtValue(Collection<Issue> issues, @Nullable Date periodDate) {
double value = 0;
for (Issue issue : issues) {
WorkDayDuration currentTechnicalDebt = ((DefaultIssue) issue).technicalDebt();
- List<FieldDiffs> technicalDebtChangelog = changesOnField(IssueUpdater.TECHNICAL_DEBT, changelogList.get(issue));
+ List<FieldDiffs> technicalDebtChangelog = changesOnField(IssueUpdater.TECHNICAL_DEBT, ((DefaultIssue) issue).changes());
if (technicalDebtChangelog.isEmpty()) {
if (isAfter(issue.creationDate(), periodDate)) {
value += technicalDebtConverter.toDays(currentTechnicalDebt);
@@ -120,8 +113,14 @@ public final class NewTechnicalDebtDecorator implements Decorator {
private Double calculateNewTechnicalDebtValueFromChangelog(WorkDayDuration currentTechnicalDebt, List<FieldDiffs> technicalDebtChangelog, Date periodDate) {
double currentTechnicalDebtValue = technicalDebtConverter.toDays(currentTechnicalDebt);
- // Changelog have to be sorted from oldest to newest to catch oldest value just before the period date -> By default it's sorted from newest to oldest
- for (FieldDiffs fieldDiffs : Lists.reverse(technicalDebtChangelog)) {
+ // Changelog have to be sorted from oldest to newest to catch oldest value just before the period date
+ Collections.sort(technicalDebtChangelog, new Comparator<FieldDiffs>() {
+ @Override
+ public int compare(FieldDiffs fieldDiffs, FieldDiffs fieldDiffs2) {
+ return fieldDiffs.createdAt().compareTo(fieldDiffs2.createdAt());
+ }
+ });
+ for (FieldDiffs fieldDiffs : technicalDebtChangelog) {
if (isAfter(fieldDiffs.createdAt(), periodDate)) {
WorkDayDuration pastTechnicalDebt = newValue(IssueUpdater.TECHNICAL_DEBT, fieldDiffs);
double pastTechnicalDebtValue = technicalDebtConverter.toDays(pastTechnicalDebt);
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/InitialOpenIssuesSensorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/InitialOpenIssuesSensorTest.java
index 814da65f220..e6c1b2c30d7 100644
--- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/InitialOpenIssuesSensorTest.java
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/InitialOpenIssuesSensorTest.java
@@ -22,8 +22,8 @@ package org.sonar.plugins.core.issue;
import org.apache.ibatis.session.ResultHandler;
import org.junit.Test;
import org.sonar.api.resources.Project;
+import org.sonar.core.issue.db.IssueChangeDao;
import org.sonar.core.issue.db.IssueDao;
-import org.sonar.core.issue.db.IssueDto;
import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Matchers.any;
@@ -35,7 +35,9 @@ public class InitialOpenIssuesSensorTest {
InitialOpenIssuesStack stack = mock(InitialOpenIssuesStack.class);
IssueDao issueDao = mock(IssueDao.class);
- InitialOpenIssuesSensor sensor = new InitialOpenIssuesSensor(stack, issueDao);
+ IssueChangeDao issueChangeDao = mock(IssueChangeDao.class);
+
+ InitialOpenIssuesSensor sensor = new InitialOpenIssuesSensor(stack, issueDao, issueChangeDao);
@Test
public void should_select_module_open_issues() {
@@ -47,6 +49,15 @@ public class InitialOpenIssuesSensorTest {
}
@Test
+ public void should_select_module_open_issues_changelog() {
+ Project project = new Project("key");
+ project.setId(1);
+ sensor.analyse(project, null);
+
+ verify(issueChangeDao).selectChangelogOnNonClosedIssuesByModuleAndType(eq(1), any(ResultHandler.class));
+ }
+
+ @Test
public void test_toString() throws Exception {
assertThat(sensor.toString()).isEqualTo("InitialOpenIssuesSensor");
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/InitialOpenIssuesStackTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/InitialOpenIssuesStackTest.java
index c6fbeaf5cb5..3f9144bd824 100644
--- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/InitialOpenIssuesStackTest.java
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/InitialOpenIssuesStackTest.java
@@ -31,6 +31,7 @@ import org.sonar.batch.bootstrap.BootstrapProperties;
import org.sonar.batch.bootstrap.BootstrapSettings;
import org.sonar.batch.bootstrap.TempFolderProvider;
import org.sonar.batch.index.Caches;
+import org.sonar.core.issue.db.IssueChangeDto;
import org.sonar.core.issue.db.IssueDto;
import java.io.IOException;
@@ -69,48 +70,74 @@ public class InitialOpenIssuesStackTest {
}
@Test
- public void should_get_and_remove() {
+ public void get_and_remove_issues() {
IssueDto issueDto = new IssueDto().setComponentKey_unit_test_only("org.struts.Action").setKee("ISSUE-1");
stack.addIssue(issueDto);
- List<IssueDto> issueDtos = stack.selectAndRemove("org.struts.Action");
+ List<IssueDto> issueDtos = stack.selectAndRemoveIssues("org.struts.Action");
assertThat(issueDtos).hasSize(1);
assertThat(issueDtos.get(0).getKee()).isEqualTo("ISSUE-1");
- assertThat(stack.selectAll()).isEmpty();
+ assertThat(stack.selectAllIssues()).isEmpty();
}
@Test
- public void should_get_and_remove_with_many_issues_on_same_resource() {
+ public void get_and_remove_with_many_issues_on_same_resource() {
stack.addIssue(new IssueDto().setComponentKey_unit_test_only("org.struts.Action").setKee("ISSUE-1"));
stack.addIssue(new IssueDto().setComponentKey_unit_test_only("org.struts.Action").setKee("ISSUE-2"));
- List<IssueDto> issueDtos = stack.selectAndRemove("org.struts.Action");
+ List<IssueDto> issueDtos = stack.selectAndRemoveIssues("org.struts.Action");
assertThat(issueDtos).hasSize(2);
- assertThat(stack.selectAll()).isEmpty();
+ assertThat(stack.selectAllIssues()).isEmpty();
}
@Test
- public void should_do_nothing_if_resource_not_found() {
+ public void get_and_remove_do_nothing_if_resource_not_found() {
stack.addIssue(new IssueDto().setComponentKey_unit_test_only("org.struts.Action").setKee("ISSUE-1"));
- List<IssueDto> issueDtos = stack.selectAndRemove("Other");
+ List<IssueDto> issueDtos = stack.selectAndRemoveIssues("Other");
assertThat(issueDtos).hasSize(0);
- assertThat(stack.selectAll()).hasSize(1);
+ assertThat(stack.selectAllIssues()).hasSize(1);
}
@Test
- public void should_clear() {
+ public void select_changelog() {
+ stack.addChangelog(new IssueChangeDto().setKey("CHANGE-1").setIssueKey("ISSUE-1"));
+ stack.addChangelog(new IssueChangeDto().setKey("CHANGE-2").setIssueKey("ISSUE-1"));
+
+ List<IssueChangeDto> issueChangeDtos = stack.selectChangelog("ISSUE-1");
+ assertThat(issueChangeDtos).hasSize(2);
+ assertThat(issueChangeDtos.get(0).getKey()).isEqualTo("CHANGE-1");
+ assertThat(issueChangeDtos.get(1).getKey()).isEqualTo("CHANGE-2");
+ }
+
+ @Test
+ public void return_empty_changelog() {
+ assertThat(stack.selectChangelog("ISSUE-1")).isEmpty();
+ }
+
+ @Test
+ public void clear_issues() {
stack.addIssue(new IssueDto().setComponentKey_unit_test_only("org.struts.Action").setKee("ISSUE-1"));
- assertThat(stack.selectAll()).hasSize(1);
+ assertThat(stack.selectAllIssues()).hasSize(1);
// issues are not removed
- assertThat(stack.selectAll()).hasSize(1);
+ assertThat(stack.selectAllIssues()).hasSize(1);
+
+ stack.clear();
+ assertThat(stack.selectAllIssues()).hasSize(0);
+ }
+
+ @Test
+ public void clear_issues_changelog() {
+ stack.addChangelog(new IssueChangeDto().setKey("CHANGE-1").setIssueKey("ISSUE-1"));
+
+ assertThat(stack.selectChangelog("ISSUE-1")).hasSize(1);
stack.clear();
- assertThat(stack.selectAll()).hasSize(0);
+ assertThat(stack.selectChangelog("ISSUE-1")).isNull();
}
}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/IssueTrackingDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/IssueTrackingDecoratorTest.java
index ddf259cacd8..9a573b3d62a 100644
--- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/IssueTrackingDecoratorTest.java
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/IssueTrackingDecoratorTest.java
@@ -40,6 +40,7 @@ import org.sonar.api.rules.RuleFinder;
import org.sonar.batch.issue.IssueCache;
import org.sonar.batch.scan.LastSnapshots;
import org.sonar.core.issue.IssueUpdater;
+import org.sonar.core.issue.db.IssueChangeDto;
import org.sonar.core.issue.db.IssueDto;
import org.sonar.core.issue.workflow.IssueWorkflow;
import org.sonar.core.persistence.AbstractDaoTestCase;
@@ -113,7 +114,7 @@ public class IssueTrackingDecoratorTest extends AbstractDaoTestCase {
// INPUT : one issue, no open issues during previous scan, no filtering
when(issueCache.byComponent("struts:Action.java")).thenReturn(Arrays.asList(issue));
List<IssueDto> dbIssues = Collections.emptyList();
- when(initialOpenIssues.selectAndRemove("struts:Action.java")).thenReturn(dbIssues);
+ when(initialOpenIssues.selectAndRemoveIssues("struts:Action.java")).thenReturn(dbIssues);
decorator.doDecorate(file);
@@ -486,7 +487,7 @@ public class IssueTrackingDecoratorTest extends AbstractDaoTestCase {
DefaultIssue openIssue = new DefaultIssue();
when(issueCache.byComponent("struts")).thenReturn(Arrays.asList(openIssue));
IssueDto deadIssue = new IssueDto().setKee("ABCDE").setResolution(null).setStatus("OPEN").setRuleKey_unit_test_only("squid", "AvoidCycle");
- when(initialOpenIssues.selectAll()).thenReturn(Arrays.asList(deadIssue));
+ when(initialOpenIssues.selectAllIssues()).thenReturn(Arrays.asList(deadIssue));
decorator.doDecorate(project);
@@ -537,4 +538,21 @@ public class IssueTrackingDecoratorTest extends AbstractDaoTestCase {
assertThat(issue.severity()).isEqualTo("MAJOR");
verify(updater, never()).setPastSeverity(eq(issue), anyString(), any(IssueChangeContext.class));
}
+
+ @Test
+ public void merge_issue_changelog_with_previous_changelog() throws Exception {
+ when(initialOpenIssues.selectChangelog("ABCDE")).thenReturn(newArrayList(new IssueChangeDto().setIssueKey("ABCD")));
+
+ IssueDto previousIssue = new IssueDto().setKee("ABCDE").setResolution(null).setStatus("OPEN").setRuleKey_unit_test_only("squid", "AvoidCycle")
+ .setLine(10).setMessage("Message").setEffortToFix(1.5).setTechnicalDebt(1L);
+ DefaultIssue issue = new DefaultIssue();
+
+ IssueTrackingResult trackingResult = mock(IssueTrackingResult.class);
+ when(trackingResult.matched()).thenReturn(newArrayList(issue));
+ when(trackingResult.matching(eq(issue))).thenReturn(previousIssue);
+ decorator.mergeMatched(trackingResult);
+
+ assertThat(issue.changes()).hasSize(1);
+ }
+
}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/NewTechnicalDebtDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/NewTechnicalDebtDecoratorTest.java
index 12fe60f2ff3..309dd77b584 100644
--- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/NewTechnicalDebtDecoratorTest.java
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/technicaldebt/NewTechnicalDebtDecoratorTest.java
@@ -43,7 +43,6 @@ import org.sonar.api.resources.Resource;
import org.sonar.api.test.IsMeasure;
import org.sonar.batch.components.Period;
import org.sonar.batch.components.TimeMachineConfiguration;
-import org.sonar.core.issue.IssueChangelogFinder;
import org.sonar.core.technicaldebt.TechnicalDebtConverter;
import java.util.Calendar;
@@ -51,7 +50,6 @@ import java.util.Date;
import static com.google.common.collect.Lists.newArrayList;
import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Matchers.anyCollection;
import static org.mockito.Matchers.argThat;
import static org.mockito.Mockito.*;
@@ -73,9 +71,6 @@ public class NewTechnicalDebtDecoratorTest {
DecoratorContext context;
@Mock
- IssueChangelogFinder changelogFinder;
-
- @Mock
TechnicalDebtConverter technicalDebtConverter;
Date rightNow;
@@ -109,7 +104,7 @@ public class NewTechnicalDebtDecoratorTest {
when(timeMachineConfiguration.periods()).thenReturn(newArrayList(new Period(1, fiveDaysAgo, fiveDaysAgo), new Period(2, tenDaysAgo, tenDaysAgo)));
- decorator = new NewTechnicalDebtDecorator(perspectives, timeMachineConfiguration, changelogFinder, technicalDebtConverter);
+ decorator = new NewTechnicalDebtDecorator(perspectives, timeMachineConfiguration, technicalDebtConverter);
}
@Test
@@ -124,17 +119,16 @@ public class NewTechnicalDebtDecoratorTest {
@Test
public void save_on_one_issue_with_changelog() {
- Issue issue = new DefaultIssue().setKey("A").setCreationDate(fiveDaysAgo).setTechnicalDebt(fiveDaysDebt);
+ Issue issue = new DefaultIssue().setKey("A").setCreationDate(fiveDaysAgo).setTechnicalDebt(fiveDaysDebt).setChanges(
+ newArrayList(
+ new FieldDiffs().setDiff("technicalDebt", twoDaysDebt, fiveDaysDebt).setCreatedAt(rightNow),
+ new FieldDiffs().setDiff("technicalDebt", oneDaysDebt, twoDaysDebt).setCreatedAt(fourDaysAgo),
+ new FieldDiffs().setDiff("technicalDebt", null, oneDaysDebt).setCreatedAt(nineDaysAgo)
+ )
+ );
when(issuable.issues()).thenReturn(newArrayList(issue));
Multimap<Issue, FieldDiffs> changelogList = ArrayListMultimap.create();
- // Changes should be sorted from newest to oldest
- changelogList.putAll(issue, newArrayList(
- new FieldDiffs().setDiff("technicalDebt", twoDaysDebt, fiveDaysDebt).setCreatedAt(rightNow),
- new FieldDiffs().setDiff("technicalDebt", oneDaysDebt, twoDaysDebt).setCreatedAt(fourDaysAgo),
- new FieldDiffs().setDiff("technicalDebt", null, oneDaysDebt).setCreatedAt(nineDaysAgo)
- ));
- when(changelogFinder.findByIssues(anyCollection())).thenReturn(changelogList);
decorator.decorate(resource, context);
@@ -144,18 +138,15 @@ public class NewTechnicalDebtDecoratorTest {
@Test
public void save_on_one_issue_with_changelog_having_null_value() {
- Issue issue = new DefaultIssue().setKey("A").setCreationDate(fiveDaysAgo).setTechnicalDebt(fiveDaysDebt);
+ Issue issue = new DefaultIssue().setKey("A").setCreationDate(fiveDaysAgo).setTechnicalDebt(fiveDaysDebt).setChanges(
+ newArrayList(
+ new FieldDiffs().setDiff("technicalDebt", null, fiveDaysDebt).setCreatedAt(rightNow),
+ new FieldDiffs().setDiff("technicalDebt", oneDaysDebt, null).setCreatedAt(fourDaysAgo),
+ new FieldDiffs().setDiff("technicalDebt", null, oneDaysDebt).setCreatedAt(nineDaysAgo)
+ )
+ );
when(issuable.issues()).thenReturn(newArrayList(issue));
- Multimap<Issue, FieldDiffs> changelogList = ArrayListMultimap.create();
- // Changes should be sorted from newest to oldest
- changelogList.putAll(issue, newArrayList(
- new FieldDiffs().setDiff("technicalDebt", null, fiveDaysDebt).setCreatedAt(rightNow),
- new FieldDiffs().setDiff("technicalDebt", oneDaysDebt, null).setCreatedAt(fourDaysAgo),
- new FieldDiffs().setDiff("technicalDebt", null, oneDaysDebt).setCreatedAt(nineDaysAgo)
- ));
- when(changelogFinder.findByIssues(anyCollection())).thenReturn(changelogList);
-
decorator.decorate(resource, context);
// remember : period1 is 5daysAgo, period2 is 10daysAgo
@@ -166,18 +157,15 @@ public class NewTechnicalDebtDecoratorTest {
public void save_on_one_issue_with_changelog_and_periods_have_no_dates() {
when(timeMachineConfiguration.periods()).thenReturn(newArrayList(new Period(1, null, null), new Period(2, null, null)));
- Issue issue = new DefaultIssue().setKey("A").setCreationDate(fiveDaysAgo).setTechnicalDebt(fiveDaysDebt);
+ Issue issue = new DefaultIssue().setKey("A").setCreationDate(fiveDaysAgo).setTechnicalDebt(fiveDaysDebt).setChanges(
+ newArrayList(
+ new FieldDiffs().setDiff("technicalDebt", null, fiveDaysDebt).setCreatedAt(rightNow),
+ new FieldDiffs().setDiff("technicalDebt", oneDaysDebt, null).setCreatedAt(fourDaysAgo),
+ new FieldDiffs().setDiff("technicalDebt", null, oneDaysDebt).setCreatedAt(nineDaysAgo)
+ )
+ );
when(issuable.issues()).thenReturn(newArrayList(issue));
- Multimap<Issue, FieldDiffs> changelogList = ArrayListMultimap.create();
- // Changes should be sorted from newest to oldest
- changelogList.putAll(issue, newArrayList(
- new FieldDiffs().setDiff("technicalDebt", null, fiveDaysDebt).setCreatedAt(rightNow),
- new FieldDiffs().setDiff("technicalDebt", oneDaysDebt, null).setCreatedAt(fourDaysAgo),
- new FieldDiffs().setDiff("technicalDebt", null, oneDaysDebt).setCreatedAt(nineDaysAgo)
- ));
- when(changelogFinder.findByIssues(anyCollection())).thenReturn(changelogList);
-
decorator.decorate(resource, context);
// remember : period1 is 5daysAgo, period2 is 10daysAgo
@@ -186,23 +174,21 @@ public class NewTechnicalDebtDecoratorTest {
@Test
public void save_on_issues_with_changelog() {
- Issue issue1 = new DefaultIssue().setKey("A").setCreationDate(fiveDaysAgo).setTechnicalDebt(fiveDaysDebt);
- Issue issue2 = new DefaultIssue().setKey("B").setCreationDate(fiveDaysAgo).setTechnicalDebt(twoDaysDebt);
+ Issue issue1 = new DefaultIssue().setKey("A").setCreationDate(fiveDaysAgo).setTechnicalDebt(fiveDaysDebt).setChanges(
+ newArrayList(
+ new FieldDiffs().setDiff("technicalDebt", twoDaysDebt, fiveDaysDebt).setCreatedAt(rightNow),
+ new FieldDiffs().setDiff("technicalDebt", oneDaysDebt, twoDaysDebt).setCreatedAt(fourDaysAgo),
+ new FieldDiffs().setDiff("technicalDebt", null, oneDaysDebt).setCreatedAt(nineDaysAgo)
+ )
+ );
+ Issue issue2 = new DefaultIssue().setKey("B").setCreationDate(fiveDaysAgo).setTechnicalDebt(twoDaysDebt).setChanges(
+ newArrayList(
+ new FieldDiffs().setDiff("technicalDebt", oneDaysDebt, twoDaysDebt).setCreatedAt(rightNow),
+ new FieldDiffs().setDiff("technicalDebt", null, oneDaysDebt).setCreatedAt(nineDaysAgo)
+ )
+ );
when(issuable.issues()).thenReturn(newArrayList(issue1, issue2));
- Multimap<Issue, FieldDiffs> changelogList = ArrayListMultimap.create();
- changelogList.putAll(issue1, newArrayList(
- new FieldDiffs().setDiff("technicalDebt", twoDaysDebt, fiveDaysDebt).setCreatedAt(rightNow),
- new FieldDiffs().setDiff("technicalDebt", oneDaysDebt, twoDaysDebt).setCreatedAt(fourDaysAgo),
- new FieldDiffs().setDiff("technicalDebt", null, oneDaysDebt).setCreatedAt(nineDaysAgo)
- ));
- changelogList.putAll(issue2, newArrayList(
- new FieldDiffs().setDiff("technicalDebt", oneDaysDebt, twoDaysDebt).setCreatedAt(rightNow),
- new FieldDiffs().setDiff("technicalDebt", null, oneDaysDebt).setCreatedAt(nineDaysAgo)
- ));
-
- when(changelogFinder.findByIssues(anyCollection())).thenReturn(changelogList);
-
decorator.decorate(resource, context);
// remember : period1 is 5daysAgo, period2 is 10daysAgo
@@ -215,9 +201,6 @@ public class NewTechnicalDebtDecoratorTest {
(Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setTechnicalDebt(fiveDaysDebt))
);
- Multimap<Issue, FieldDiffs> changelogList = ArrayListMultimap.create();
- when(changelogFinder.findByIssues(anyCollection())).thenReturn(changelogList);
-
decorator.decorate(resource, context);
// remember : period1 is 5daysAgo, period2 is 10daysAgo
@@ -230,9 +213,6 @@ public class NewTechnicalDebtDecoratorTest {
(Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setTechnicalDebt(null))
);
- Multimap<Issue, FieldDiffs> changelogList = ArrayListMultimap.create();
- when(changelogFinder.findByIssues(anyCollection())).thenReturn(changelogList);
-
decorator.decorate(resource, context);
// remember : period1 is 5daysAgo, period2 is 10daysAgo
@@ -247,9 +227,6 @@ public class NewTechnicalDebtDecoratorTest {
(Issue) new DefaultIssue().setKey("A").setCreationDate(nineDaysAgo).setTechnicalDebt(fiveDaysDebt))
);
- Multimap<Issue, FieldDiffs> changelogList = ArrayListMultimap.create();
- when(changelogFinder.findByIssues(anyCollection())).thenReturn(changelogList);
-
decorator.decorate(resource, context);
// remember : period1 is null, period2 is null
@@ -263,9 +240,6 @@ public class NewTechnicalDebtDecoratorTest {
new DefaultIssue().setKey("B").setCreationDate(fiveDaysAgo).setTechnicalDebt(twoDaysDebt)
));
- Multimap<Issue, FieldDiffs> changelogList = ArrayListMultimap.create();
- when(changelogFinder.findByIssues(anyCollection())).thenReturn(changelogList);
-
decorator.decorate(resource, context);
// remember : period1 is 5daysAgo, period2 is 10daysAgo
@@ -274,25 +248,23 @@ public class NewTechnicalDebtDecoratorTest {
@Test
public void save_on_issues_with_changelog_and_issues_without_changelog() {
- Issue issue1 = new DefaultIssue().setKey("A").setCreationDate(fiveDaysAgo).setTechnicalDebt(fiveDaysDebt);
- Issue issue2 = new DefaultIssue().setKey("B").setCreationDate(fiveDaysAgo).setTechnicalDebt(twoDaysDebt);
+ Issue issue1 = new DefaultIssue().setKey("A").setCreationDate(fiveDaysAgo).setTechnicalDebt(fiveDaysDebt).setChanges(
+ newArrayList(
+ new FieldDiffs().setDiff("technicalDebt", twoDaysDebt, fiveDaysDebt).setCreatedAt(rightNow),
+ new FieldDiffs().setDiff("technicalDebt", oneDaysDebt, twoDaysDebt).setCreatedAt(fourDaysAgo),
+ new FieldDiffs().setDiff("technicalDebt", null, oneDaysDebt).setCreatedAt(nineDaysAgo)
+ )
+ );
+ Issue issue2 = new DefaultIssue().setKey("B").setCreationDate(fiveDaysAgo).setTechnicalDebt(twoDaysDebt).setChanges(
+ newArrayList(
+ new FieldDiffs().setDiff("technicalDebt", oneDaysDebt, twoDaysDebt).setCreatedAt(rightNow),
+ new FieldDiffs().setDiff("technicalDebt", null, oneDaysDebt).setCreatedAt(nineDaysAgo)
+ )
+ );
Issue issue3 = new DefaultIssue().setKey("C").setCreationDate(nineDaysAgo).setTechnicalDebt(fiveDaysDebt);
Issue issue4 = new DefaultIssue().setKey("D").setCreationDate(fiveDaysAgo).setTechnicalDebt(twoDaysDebt);
when(issuable.issues()).thenReturn(newArrayList(issue1, issue2, issue3, issue4));
- Multimap<Issue, FieldDiffs> changelogList = ArrayListMultimap.create();
- changelogList.putAll(issue1, newArrayList(
- new FieldDiffs().setDiff("technicalDebt", twoDaysDebt, fiveDaysDebt).setCreatedAt(rightNow),
- new FieldDiffs().setDiff("technicalDebt", oneDaysDebt, twoDaysDebt).setCreatedAt(fourDaysAgo),
- new FieldDiffs().setDiff("technicalDebt", null, oneDaysDebt).setCreatedAt(nineDaysAgo)
- ));
- changelogList.putAll(issue2, newArrayList(
- new FieldDiffs().setDiff("technicalDebt", oneDaysDebt, twoDaysDebt).setCreatedAt(rightNow),
- new FieldDiffs().setDiff("technicalDebt", null, oneDaysDebt).setCreatedAt(nineDaysAgo)
- ));
-
- when(changelogFinder.findByIssues(anyCollection())).thenReturn(changelogList);
-
decorator.decorate(resource, context);
// remember : period1 is 5daysAgo, period2 is 10daysAgo