diff options
author | simonbrandhof <simon.brandhof@gmail.com> | 2010-11-29 09:56:20 +0000 |
---|---|---|
committer | simonbrandhof <simon.brandhof@gmail.com> | 2010-11-29 09:56:20 +0000 |
commit | 138f4ebc4f2f7f30339153879c11bb51b33034bd (patch) | |
tree | 09761d941013efd2ac2a7cfab56621bd28bd2bdb /plugins | |
parent | b0b8d34052b9e91b172ee38103c678524dfc93f3 (diff) | |
download | sonarqube-138f4ebc4f2f7f30339153879c11bb51b33034bd.tar.gz sonarqube-138f4ebc4f2f7f30339153879c11bb51b33034bd.zip |
SONAR-249 add the decorator to calculate differential values of measures.
Diffstat (limited to 'plugins')
13 files changed, 663 insertions, 34 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 b18eeff5366..ae915535904 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 @@ -19,7 +19,11 @@ */ package org.sonar.plugins.core; -import org.sonar.api.*; +import com.google.common.collect.Lists; +import org.sonar.api.CoreProperties; +import org.sonar.api.Plugin; +import org.sonar.api.Properties; +import org.sonar.api.Property; import org.sonar.api.checks.NoSonarFilter; import org.sonar.api.resources.Java; import org.sonar.plugins.core.batch.ExcludedResourceFilter; @@ -36,11 +40,13 @@ import org.sonar.plugins.core.metrics.UserManagedMetrics; import org.sonar.plugins.core.security.ApplyProjectRolesDecorator; import org.sonar.plugins.core.sensors.*; import org.sonar.plugins.core.testdetailsviewer.TestsViewerDefinition; +import org.sonar.plugins.core.timemachine.DifferentialValueDecorator; +import org.sonar.plugins.core.timemachine.PeriodLocator; +import org.sonar.plugins.core.timemachine.TendencyDecorator; import org.sonar.plugins.core.ui.pageselector.GwtPageSelector; import org.sonar.plugins.core.violationsviewer.ViolationsViewerDefinition; import org.sonar.plugins.core.widgets.*; -import java.util.ArrayList; import java.util.List; @Properties({ @@ -125,7 +131,7 @@ public class CorePlugin implements Plugin { } public List getExtensions() { - List extensions = new ArrayList(); + List extensions = Lists.newLinkedList(); // languages extensions.add(Java.class); @@ -186,6 +192,9 @@ public class CorePlugin implements Plugin { extensions.add(DirectoriesDecorator.class); extensions.add(FilesDecorator.class); + extensions.add(PeriodLocator.class); + extensions.add(DifferentialValueDecorator.class); + return extensions; } diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CheckAlertThresholds.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CheckAlertThresholds.java index 8e55fa3890d..5b3be2accff 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CheckAlertThresholds.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CheckAlertThresholds.java @@ -19,11 +19,9 @@ */ package org.sonar.plugins.core.sensors; +import com.google.common.collect.Lists; import org.apache.commons.lang.StringUtils; -import org.sonar.api.batch.Decorator; -import org.sonar.api.batch.DecoratorContext; -import org.sonar.api.batch.DependedUpon; -import org.sonar.api.batch.Phase; +import org.sonar.api.batch.*; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Measure; import org.sonar.api.measures.Metric; @@ -33,10 +31,8 @@ import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; import org.sonar.api.resources.ResourceUtils; -import java.util.ArrayList; import java.util.List; -@Phase(name = Phase.Name.POST) public class CheckAlertThresholds implements Decorator { private final RulesProfile profile; @@ -50,6 +46,15 @@ public class CheckAlertThresholds implements Decorator { return CoreMetrics.ALERT_STATUS; } + @DependsUpon + public List<Metric> dependsUponMetrics() { + List<Metric> metrics = Lists.newLinkedList(); + for (Alert alert : profile.getAlerts()) { + metrics.add(alert.getMetric()); + } + return metrics; + } + public boolean shouldExecuteOnProject(Project project) { return profile != null @@ -66,7 +71,7 @@ public class CheckAlertThresholds implements Decorator { private void decorateResource(DecoratorContext context) { Metric.Level globalLevel = Metric.Level.OK; - List<String> labels = new ArrayList<String>(); + List<String> labels = Lists.newArrayList(); for (final Alert alert : profile.getAlerts()) { Measure measure = context.getMeasure(alert.getMetric()); diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/GenerateAlertEvents.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/GenerateAlertEvents.java index f8af4708d3f..62b8882fd2d 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/GenerateAlertEvents.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/GenerateAlertEvents.java @@ -30,7 +30,6 @@ import org.sonar.api.resources.ResourceUtils; import java.util.List; -@Phase(name = Phase.Name.POST) public class GenerateAlertEvents implements Decorator { private final RulesProfile profile; diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/DifferentialValueDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/DifferentialValueDecorator.java new file mode 100644 index 00000000000..57c9c5619ed --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/DifferentialValueDecorator.java @@ -0,0 +1,200 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.core.timemachine; + +import com.google.common.collect.Maps; +import org.apache.commons.configuration.Configuration; +import org.apache.commons.lang.ObjectUtils; +import org.sonar.api.batch.*; +import org.sonar.api.database.DatabaseSession; +import org.sonar.api.database.model.MeasureModel; +import org.sonar.api.database.model.Snapshot; +import org.sonar.api.measures.*; +import org.sonar.api.qualitymodel.Characteristic; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; +import org.sonar.api.rules.Rule; +import org.sonar.api.rules.RulePriority; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +@DependedUpon(DecoratorBarriers.END_OF_TIME_MACHINE) +public class DifferentialValueDecorator implements Decorator { + + private Snapshot[] projectTargetSnapshots; + private Map<Integer, Metric> metricByIds; + private DatabaseSession session; + + public DifferentialValueDecorator(DatabaseSession session, PeriodLocator periodLocator, Configuration configuration, MetricFinder metricFinder) { + this.session = session; + Snapshot snapshot = periodLocator.locate(5); + projectTargetSnapshots = new Snapshot[] {snapshot}; + + this.metricByIds = Maps.newHashMap(); + for (Metric metric : metricFinder.findAll()) { + if (metric.isNumericType()) { + metricByIds.put(metric.getId(), metric); + } + } + } + + /** + * only for unit tests + */ + DifferentialValueDecorator(DatabaseSession session, Snapshot[] projectTargetSnapshots, Metric[] metrics) { + this.session = session; + this.projectTargetSnapshots = projectTargetSnapshots; + this.metricByIds = Maps.newHashMap(); + for (Metric metric : metrics) { + metricByIds.put(metric.getId(), metric); + } + } + + public boolean shouldExecuteOnProject(Project project) { + return true; + } + + @DependsUpon + public Collection<Metric> dependsUponMetrics() { + return metricByIds.values(); + } + + public void decorate(Resource resource, DecoratorContext context) { + for (int index = 0; index < projectTargetSnapshots.length; index++) { + Snapshot projectTargetSnapshot = projectTargetSnapshots[index]; + if (projectTargetSnapshot != null) { + // search past measures + List<MeasureModel> pastMeasures = selectPastMeasures(resource.getId(), projectTargetSnapshot); + + Map<MeasureKey, MeasureModel> pastMeasuresByKey = Maps.newHashMap(); + for (MeasureModel pastMeasure : pastMeasures) { + pastMeasuresByKey.put(new MeasureKey(pastMeasure), pastMeasure); + } + + // for each measure, search equivalent past measure + for (Measure measure : context.getMeasures(MeasuresFilters.all())) { + // compare with past measure + MeasureModel pastMeasure = pastMeasuresByKey.get(new MeasureKey(measure)); + if (calculateDiffValue(measure, pastMeasure, index)) { + context.saveMeasure(measure); + } + } + } + } + } + + private boolean calculateDiffValue(Measure measure, MeasureModel pastMeasure, int index) { + boolean updated = false; + if (pastMeasure != null && pastMeasure.getValue() != null && measure.getValue() != null) { + double diff = (measure.getValue().doubleValue() - pastMeasure.getValue().doubleValue()); + updated = true; + switch (index) { + case 0: + measure.setDiffValue1(diff); + break; + case 1: + measure.setDiffValue2(diff); + break; + case 2: + measure.setDiffValue3(diff); + break; + default: + updated = false; + } + } + return updated; + } + + List<MeasureModel> selectPastMeasures(int resourceId, Snapshot projectTargetSnapshot) { + // improvements : keep query in cache ? select only some columns ? + // TODO support measure on rules and characteristics + String hql = "select m from " + MeasureModel.class.getSimpleName() + " m, " + Snapshot.class.getSimpleName() + " s " + + "where m.snapshotId=s.id and m.metricId in (:metricIds) and m.rule=null and m.rulePriority=null and m.rulesCategoryId=null and m.characteristic=null " + + "and (s.rootId=:rootSnapshotId or s.id=:rootSnapshotId) and s.resourceId=:resourceId and s.status=:status"; + return session.createQuery(hql) + .setParameter("metricIds", metricByIds.keySet()) + .setParameter("rootSnapshotId", ObjectUtils.defaultIfNull(projectTargetSnapshot.getRootId(), projectTargetSnapshot.getId())) + .setParameter("resourceId", resourceId) + .setParameter("status", Snapshot.STATUS_PROCESSED) + .getResultList(); + } + + @Override + public String toString() { + return getClass().toString(); + } + + static class MeasureKey { + Integer metricId; + Rule rule; + Integer categoryId; + RulePriority priority; + Characteristic characteristic; + + MeasureKey(MeasureModel model) { + this.metricId = model.getMetricId(); + rule = model.getRule(); + categoryId = model.getRulesCategoryId(); + priority = model.getRulePriority(); + characteristic = model.getCharacteristic(); + } + + MeasureKey(Measure measure) { + metricId = measure.getMetric().getId(); + characteristic = measure.getCharacteristic(); + // TODO merge RuleMeasure into Measure + if (measure instanceof RuleMeasure) { + RuleMeasure rm = (RuleMeasure)measure; + categoryId = rm.getRuleCategory(); + rule = rm.getRule(); + priority = rm.getRulePriority(); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + MeasureKey that = (MeasureKey) o; + + if (categoryId != null ? !categoryId.equals(that.categoryId) : that.categoryId != null) return false; + if (characteristic != null ? !characteristic.equals(that.characteristic) : that.characteristic != null) + return false; + if (!metricId.equals(that.metricId)) return false; + if (priority != that.priority) return false; + if (rule != null ? !rule.equals(that.rule) : that.rule != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = metricId.hashCode(); + result = 31 * result + (rule != null ? rule.hashCode() : 0); + result = 31 * result + (categoryId != null ? categoryId.hashCode() : 0); + result = 31 * result + (priority != null ? priority.hashCode() : 0); + result = 31 * result + (characteristic != null ? characteristic.hashCode() : 0); + return result; + } + } +} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/PeriodLocator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/PeriodLocator.java new file mode 100644 index 00000000000..35e960023b1 --- /dev/null +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/PeriodLocator.java @@ -0,0 +1,74 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.core.timemachine; + +import org.apache.commons.lang.time.DateUtils; +import org.sonar.api.BatchExtension; +import org.sonar.api.database.DatabaseSession; +import org.sonar.api.database.model.Snapshot; + +import java.util.Date; +import java.util.List; + +// TODO should implement BatchComponent +public final class PeriodLocator implements BatchExtension { + private Snapshot projectSnapshot; // TODO replace by PersistenceManager + private DatabaseSession session; + + public PeriodLocator(Snapshot projectSnapshot, DatabaseSession session) { + this.projectSnapshot = projectSnapshot; + this.session = session; + } + + public Snapshot locate(int days) { + List<Snapshot> snapshots = loadSnapshotsFromDatabase(); + return getNearestToTarget(snapshots, projectSnapshot.getCreatedAt(), days); + } + + List<Snapshot> loadSnapshotsFromDatabase() { + String hql = "from " + Snapshot.class.getSimpleName() + " where resourceId=:resourceId AND status=:status order by createdAt"; + return session.createQuery(hql) + .setParameter("resourceId", projectSnapshot.getResourceId()) + .setParameter("status", Snapshot.STATUS_PROCESSED) + .getResultList(); + } + + static Snapshot getNearestToTarget(List<Snapshot> snapshots, Date currentDate, int distanceInDays) { + Date targetDate = DateUtils.addDays(currentDate, -distanceInDays); + return getNearestToTarget(snapshots, targetDate); + } + + static Snapshot getNearestToTarget(List<Snapshot> snapshots, Date targetDate) { + long bestDistance = Long.MAX_VALUE; + Snapshot nearest = null; + for (Snapshot snapshot : snapshots) { + long distance = distance(snapshot.getCreatedAt(), targetDate); + if (distance<=bestDistance) { + bestDistance = distance; + nearest = snapshot; + } + } + return nearest; + } + + static long distance(Date d1, Date d2) { + return Math.abs(d1.getTime() - d2.getTime()); + } +} diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/TendencyAnalyser.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/TendencyAnalyser.java index 0ffde81e912..16ccf41df5d 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/TendencyAnalyser.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/TendencyAnalyser.java @@ -17,7 +17,7 @@ * License along with Sonar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
-package org.sonar.plugins.core.sensors;
+package org.sonar.plugins.core.timemachine;
import java.util.List;
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/TendencyDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/TendencyDecorator.java index 4a0c9537704..c28c1f52084 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/TendencyDecorator.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/TendencyDecorator.java @@ -17,40 +17,45 @@ * License along with Sonar; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 */ -package org.sonar.plugins.core.sensors; +package org.sonar.plugins.core.timemachine; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; +import com.google.common.collect.Lists; import org.apache.commons.configuration.Configuration; import org.apache.commons.lang.time.DateUtils; import org.sonar.api.CoreProperties; import org.sonar.api.batch.*; import org.sonar.api.measures.Measure; import org.sonar.api.measures.Metric; +import org.sonar.api.measures.MetricFinder; import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; import org.sonar.api.resources.ResourceUtils; -import org.sonar.jpa.dao.MeasuresDao; -import java.util.ArrayList; import java.util.List; -@Phase(name = Phase.Name.POST) +@DependedUpon(DecoratorBarriers.END_OF_TIME_MACHINE) public class TendencyDecorator implements Decorator { public static final String PROP_DAYS_DESCRIPTION = "Number of days the tendency should be calculated on."; - private MeasuresDao measuresDao; private TimeMachine timeMachine; private TimeMachineQuery query; private TendencyAnalyser analyser; private Configuration configuration; + private List<Metric> metrics; - public TendencyDecorator(TimeMachine timeMachine, MeasuresDao measuresDao, Configuration configuration) { + public TendencyDecorator(TimeMachine timeMachine, MetricFinder metricFinder, Configuration configuration) { this.timeMachine = timeMachine; - this.measuresDao = measuresDao; this.analyser = new TendencyAnalyser(); this.configuration = configuration; + this.metrics = Lists.newLinkedList(); + for (Metric metric : metricFinder.findAll()) { + if (metric.isNumericType()) { + metrics.add(metric); + } + } } protected TendencyDecorator(TimeMachine timeMachine, TimeMachineQuery query, TendencyAnalyser analyser, Configuration configuration) { @@ -60,16 +65,14 @@ public class TendencyDecorator implements Decorator { this.configuration = configuration; } + @DependsUpon + public List<Metric> dependsUponMetrics() { + return metrics; + } + protected TimeMachineQuery initQuery(Project project) { int days = project.getConfiguration().getInt(CoreProperties.CORE_TENDENCY_DEPTH_PROPERTY, CoreProperties.CORE_TENDENCY_DEPTH_DEFAULT_VALUE); - List<Metric> metrics = new ArrayList<Metric>(); - for (Metric metric : measuresDao.getMetrics()) { - if (metric.isNumericType()) { - metrics.add(metric); - } - } - query = new TimeMachineQuery(null) // resource is set after .setFrom(DateUtils.addDays(project.getAnalysisDate(), -days)) .setToCurrentAnalysis(true) diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/DifferentialValueDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/DifferentialValueDecoratorTest.java new file mode 100644 index 00000000000..bf4691d01d1 --- /dev/null +++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/DifferentialValueDecoratorTest.java @@ -0,0 +1,78 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.core.timemachine; + +import org.junit.Test; +import org.sonar.api.database.model.MeasureModel; +import org.sonar.api.database.model.Snapshot; +import org.sonar.api.measures.Metric; +import org.sonar.jpa.test.AbstractDbUnitTestCase; + +import java.util.List; + +import static org.hamcrest.CoreMatchers.anyOf; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +public class DifferentialValueDecoratorTest extends AbstractDbUnitTestCase { + + private static final int PROJECT_SNAPSHOT_ID = 1000; + private static final int PROJECT_ID = 1; + private static final int FILE_ID = 3; + + @Test + public void shouldSelectPastResourceMeasures() { + setupData("shared"); + + Metric[] metrics = selectMetrics(); + Snapshot projectSnapshot = getSession().getSingleResult(Snapshot.class, "id", PROJECT_SNAPSHOT_ID); + + DifferentialValueDecorator decorator = new DifferentialValueDecorator(getSession(), new Snapshot[0], metrics); + List<MeasureModel> measures = decorator.selectPastMeasures(FILE_ID, projectSnapshot); + assertThat(measures.size(), is(2)); + + for (MeasureModel measure : measures) { + assertThat(measure.getId(), anyOf(is(5L), is(6L))); + assertThat(measure.getValue(), anyOf(is(5.0), is(60.0))); + } + } + + @Test + public void shouldSelectPastProjectMeasures() { + setupData("shared"); + + Metric[] metrics = selectMetrics(); + Snapshot projectSnapshot = getSession().getSingleResult(Snapshot.class, "id", PROJECT_SNAPSHOT_ID); + + DifferentialValueDecorator decorator = new DifferentialValueDecorator(getSession(), new Snapshot[0], metrics); + List<MeasureModel> measures = decorator.selectPastMeasures(PROJECT_ID, projectSnapshot); + assertThat(measures.size(), is(2)); + + for (MeasureModel measure : measures) { + assertThat(measure.getId(), anyOf(is(1L), is(2L))); + assertThat(measure.getValue(), anyOf(is(60.0), is(80.0))); + } + } + + private Metric[] selectMetrics() { + List<Metric> metrics = getSession().getResults(Metric.class); + return metrics.toArray(new Metric[metrics.size()]); + } +} diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/PeriodLocatorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/PeriodLocatorTest.java new file mode 100644 index 00000000000..d0d99fed25f --- /dev/null +++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/PeriodLocatorTest.java @@ -0,0 +1,99 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.plugins.core.timemachine; + +import com.google.common.collect.Lists; +import org.junit.Test; +import org.sonar.api.database.model.Snapshot; +import org.sonar.jpa.test.AbstractDbUnitTestCase; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.core.IsNull.nullValue; +import static org.junit.Assert.assertThat; +import static org.junit.internal.matchers.IsCollectionContaining.hasItems; + +public class PeriodLocatorTest extends AbstractDbUnitTestCase { + + private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + + @Test + public void shouldLoadSnapshotsFromDatabase() { + setupData("shouldLoadSnapshotsFromDatabase"); + + Snapshot projectSnapshot = getSession().getSingleResult(Snapshot.class, "id", 1009); + PeriodLocator locator = new PeriodLocator(projectSnapshot, getSession()); + List<Snapshot> snapshots = locator.loadSnapshotsFromDatabase(); + List<Integer> snapshotIds = Lists.newLinkedList(); + for (Snapshot snapshot : snapshots) { + snapshotIds.add(snapshot.getId()); + } + assertThat(snapshotIds.size(), is(2)); + assertThat(snapshotIds, hasItems(1000, 1003)); // project snapshots + } + + @Test + public void shouldLocateNearestSnapshotBefore() throws ParseException { + Date current = dateFormat.parse("2010-10-20"); + // distance: 15 => target is 2010-10-05 + + List<Snapshot> snapshots = Arrays.asList( + newSnapshot(1, "2010-09-30"), + newSnapshot(2, "2010-10-03"),// -2 days + newSnapshot(3, "2010-10-08"),// +3 days + newSnapshot(4, "2010-10-12") // + 7 days + ); + assertThat(PeriodLocator.getNearestToTarget(snapshots, current, 15).getId(), is(2)); + } + + @Test + public void shouldLocateNearestSnapshotAfter() throws ParseException { + Date current = dateFormat.parse("2010-10-20"); + // distance: 15 => target is 2010-10-05 + + List<Snapshot> snapshots = Arrays.asList( + newSnapshot(1, "2010-09-30"), + newSnapshot(2, "2010-10-01"),// -4 days + newSnapshot(3, "2010-10-08"),// +3 days + newSnapshot(4, "2010-10-12") // + 7 days + ); + assertThat(PeriodLocator.getNearestToTarget(snapshots, current, 15).getId(), is(3)); + } + + @Test + public void shouldReturnNullIfNoSnapshots() throws ParseException { + Date current = dateFormat.parse("2010-10-20"); + List<Snapshot> snapshots = Collections.emptyList(); + assertThat(PeriodLocator.getNearestToTarget(snapshots, current, 15), nullValue()); + } + + private Snapshot newSnapshot(int id, String date) throws ParseException { + Snapshot snapshot = new Snapshot(); + snapshot.setId(id); + snapshot.setCreatedAt(dateFormat.parse(date)); + return snapshot; + } +} diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/TendencyAnalyserTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/TendencyAnalyserTest.java index 2bab29d3f8b..15acb78854d 100644 --- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/TendencyAnalyserTest.java +++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/TendencyAnalyserTest.java @@ -17,16 +17,17 @@ * License along with Sonar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
-package org.sonar.plugins.core.sensors;
+package org.sonar.plugins.core.timemachine;
-import static org.hamcrest.CoreMatchers.nullValue;
-import static org.junit.Assert.*;
import org.junit.Test;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.*;
+
public class TendencyAnalyserTest {
private TendencyAnalyser analyser = new TendencyAnalyser();
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/TendencyDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/TendencyDecoratorTest.java index 2a2d26a13b1..e37d54ee5a8 100644 --- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/TendencyDecoratorTest.java +++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/TendencyDecoratorTest.java @@ -17,7 +17,7 @@ * License along with Sonar; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 */ -package org.sonar.plugins.core.sensors; +package org.sonar.plugins.core.timemachine; import org.apache.commons.configuration.PropertiesConfiguration; import org.junit.Test; @@ -27,9 +27,9 @@ import org.sonar.api.batch.TimeMachine; import org.sonar.api.batch.TimeMachineQuery; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Measure; +import org.sonar.api.measures.MetricFinder; import org.sonar.api.resources.JavaPackage; import org.sonar.api.resources.Project; -import org.sonar.jpa.dao.MeasuresDao; import java.text.ParseException; import java.text.SimpleDateFormat; @@ -49,10 +49,10 @@ public class TendencyDecoratorTest { when(project.getAnalysisDate()).thenReturn(date("2009-12-25")); when(project.getConfiguration()).thenReturn(new PropertiesConfiguration()); - MeasuresDao dao = mock(MeasuresDao.class); - when(dao.getMetrics()).thenReturn(Arrays.asList(CoreMetrics.LINES, CoreMetrics.COVERAGE, CoreMetrics.COVERAGE_LINE_HITS_DATA, CoreMetrics.PROFILE)); + MetricFinder metricFinder = mock(MetricFinder.class); + when(metricFinder.findAll()).thenReturn(Arrays.asList(CoreMetrics.LINES, CoreMetrics.COVERAGE, CoreMetrics.COVERAGE_LINE_HITS_DATA, CoreMetrics.PROFILE)); - TendencyDecorator decorator = new TendencyDecorator(null, dao, new PropertiesConfiguration()); + TendencyDecorator decorator = new TendencyDecorator(null, metricFinder, new PropertiesConfiguration()); TimeMachineQuery query = decorator.initQuery(project); assertThat(query.getMetrics().size(), is(2)); diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/timemachine/DifferentialValueDecoratorTest/shared.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/timemachine/DifferentialValueDecoratorTest/shared.xml new file mode 100644 index 00000000000..48fe462c4b2 --- /dev/null +++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/timemachine/DifferentialValueDecoratorTest/shared.xml @@ -0,0 +1,85 @@ +<dataset> + + <metrics id="1" name="ncloc" VAL_TYPE="INT" DESCRIPTION="[null]" domain="[null]" short_name="" + enabled="true" worst_value="[null]" optimized_best_value="[null]" best_value="[null]" direction="0" hidden="false"/> + <metrics id="2" name="coverage" VAL_TYPE="INT" DESCRIPTION="[null]" domain="[null]" short_name="" + enabled="true" worst_value="0" optimized_best_value="true" best_value="100" direction="1" hidden="false"/> + + + <rules_categories id="1" name="Efficiency" description="[null]"/> + <rules_categories id="6" name="Usability" description="[null]"/> + + <rules id="30" name="Check Header" rules_category_id="6" plugin_rule_key="com.puppycrawl.tools.checkstyle.checks.header.HeaderCheck" + plugin_config_key="Checker/Treewalker/HeaderCheck" plugin_name="checkstyle" description="[null]" priority="4" enabled="true" + cardinality="SINGLE" parent_id="[null]"/> + + <rules id="31" name="Equals Avoid Null" rules_category_id="6" plugin_rule_key="com.puppycrawl.tools.checkstyle.checks.coding.EqualsAvoidNullCheck" + plugin_config_key="Checker/TreeWalker/EqualsAvoidNull" plugin_name="checkstyle" description="[null]" priority="4" enabled="true" + cardinality="SINGLE" parent_id="[null]"/> + + <!-- project --> + <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="project" name="project" + root_id="[null]" + description="[null]" + enabled="true" language="java" copy_resource_id="[null]"/> + + <!-- package --> + <projects long_name="[null]" id="2" scope="DIR" qualifier="PAC" kee="project:org.foo" name="org.foo" + root_id="1" + description="[null]" + enabled="true" language="java" copy_resource_id="[null]"/> + + <!-- file --> + <projects long_name="org.foo.Bar" id="3" scope="FIL" qualifier="CLA" kee="project:org.foo.Bar" + name="Bar" root_id="[null]" + description="[null]" + enabled="true" language="java" copy_resource_id="[null]"/> + + + <!-- snapshots --> + <snapshots id="1000" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]" + scope="PRJ" qualifier="TRK" created_at="2008-11-01 13:58:00.00" version="[null]" path="" + status="P" islast="false" depth="0" /> + + <snapshots id="1001" project_id="2" parent_snapshot_id="1000" root_project_id="1" root_snapshot_id="1000" + scope="DIR" qualifier="PAC" created_at="2008-11-01 13:58:00.00" version="[null]" path="1000." + status="P" islast="false" depth="1" /> + + <snapshots id="1002" project_id="3" parent_snapshot_id="1001" root_project_id="1" root_snapshot_id="1000" + scope="FIL" qualifier="CLA" created_at="2008-11-01 13:58:00.00" version="[null]" path="1000.1001." + status="P" islast="false" depth="2" /> + + + <!-- project measures --> + <project_measures id="1" VALUE="60" METRIC_ID="1" SNAPSHOT_ID="1000" alert_text="[null]" RULES_CATEGORY_ID="[null]" + RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]" + alert_status="[null]" description="[null]" rule_priority="[null]" characteristic_id="[null]" url="[null]" + diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"/> + + <project_measures id="2" VALUE="80" METRIC_ID="2" SNAPSHOT_ID="1000" alert_text="[null]" RULES_CATEGORY_ID="[null]" + RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]" + alert_status="[null]" description="[null]" rule_priority="[null]" characteristic_id="[null]" url="[null]" + diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"/> + + <!-- package measures --> + <project_measures id="3" VALUE="20" METRIC_ID="1" SNAPSHOT_ID="1001" alert_text="[null]" RULES_CATEGORY_ID="[null]" + RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]" + alert_status="[null]" description="[null]" rule_priority="[null]" characteristic_id="[null]" url="[null]" + diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"/> + + <project_measures id="4" VALUE="70" METRIC_ID="2" SNAPSHOT_ID="1001" alert_text="[null]" RULES_CATEGORY_ID="[null]" + RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]" + alert_status="[null]" description="[null]" rule_priority="[null]" characteristic_id="[null]" url="[null]" + diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"/> + + <!-- file measures --> + <project_measures id="5" VALUE="5" METRIC_ID="1" SNAPSHOT_ID="1002" alert_text="[null]" RULES_CATEGORY_ID="[null]" + RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]" + alert_status="[null]" description="[null]" rule_priority="[null]" characteristic_id="[null]" url="[null]" + diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"/> + + <project_measures id="6" VALUE="60" METRIC_ID="2" SNAPSHOT_ID="1002" alert_text="[null]" RULES_CATEGORY_ID="[null]" + RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]" + alert_status="[null]" description="[null]" rule_priority="[null]" characteristic_id="[null]" url="[null]" + diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"/> +</dataset>
\ No newline at end of file diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/timemachine/PeriodLocatorTest/shouldLoadSnapshotsFromDatabase.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/timemachine/PeriodLocatorTest/shouldLoadSnapshotsFromDatabase.xml new file mode 100644 index 00000000000..6e5b5adfbfc --- /dev/null +++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/timemachine/PeriodLocatorTest/shouldLoadSnapshotsFromDatabase.xml @@ -0,0 +1,76 @@ +<dataset> + + <!-- project --> + <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="project" name="project" + root_id="[null]" + description="[null]" + enabled="true" language="java" copy_resource_id="[null]"/> + + <!-- package --> + <projects long_name="[null]" id="2" scope="DIR" qualifier="PAC" kee="project:org.foo" name="org.foo" + root_id="1" + description="[null]" + enabled="true" language="java" copy_resource_id="[null]"/> + + <!-- file --> + <projects long_name="org.foo.Bar" id="3" scope="FIL" qualifier="CLA" kee="project:org.foo.Bar" + name="Bar" root_id="[null]" + description="[null]" + enabled="true" language="java" copy_resource_id="[null]"/> + + + <!-- first analysis --> + <snapshots id="1000" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]" + scope="PRJ" qualifier="TRK" created_at="2008-11-01 13:58:00.00" version="[null]" path="" + status="P" islast="false" depth="0" /> + + <snapshots id="1001" project_id="2" parent_snapshot_id="1000" root_project_id="1" root_snapshot_id="1000" + scope="DIR" qualifier="PAC" created_at="2008-11-01 13:58:00.00" version="[null]" path="1000." + status="P" islast="false" depth="1" /> + + <snapshots id="1002" project_id="3" parent_snapshot_id="1001" root_project_id="1" root_snapshot_id="1000" + scope="FIL" qualifier="CLA" created_at="2008-11-01 13:58:00.00" version="[null]" path="1000.1001." + status="P" islast="false" depth="2" /> + + + <!-- second analysis --> + <snapshots id="1003" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]" + scope="PRJ" qualifier="TRK" created_at="2008-11-02 13:58:00.00" version="[null]" path="" + status="P" islast="true" depth="0" /> + + <snapshots id="1004" project_id="2" parent_snapshot_id="1003" root_project_id="1" root_snapshot_id="1003" + scope="DIR" qualifier="PAC" created_at="2008-11-02 13:58:00.00" version="[null]" path="1003." + status="P" islast="true" depth="1" /> + + <snapshots id="1005" project_id="3" parent_snapshot_id="1004" root_project_id="1" root_snapshot_id="1003" + scope="FIL" qualifier="CLA" created_at="2008-11-02 13:58:00.00" version="[null]" path="1003.1004." + status="P" islast="true" depth="2" /> + + + <!-- unprocessed analysis (to ignore) --> + <snapshots id="1006" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]" + scope="PRJ" qualifier="TRK" created_at="2008-11-03 13:58:00.00" version="[null]" path="" + status="U" islast="false" depth="0" /> + + <snapshots id="1007" project_id="2" parent_snapshot_id="1006" root_project_id="1" root_snapshot_id="1006" + scope="DIR" qualifier="PAC" created_at="2008-11-03 13:58:00.00" version="[null]" path="1006." + status="U" islast="false" depth="1" /> + + <snapshots id="1008" project_id="3" parent_snapshot_id="1007" root_project_id="1" root_snapshot_id="1006" + scope="FIL" qualifier="CLA" created_at="2008-11-03 13:58:00.00" version="[null]" path="1006.1007." + status="U" islast="false" depth="2" /> + + + <!-- current analysis --> + <snapshots id="1009" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]" + scope="PRJ" qualifier="TRK" created_at="2008-11-04 13:58:00.00" version="[null]" path="" + status="U" islast="false" depth="0" /> + + <snapshots id="1010" project_id="2" parent_snapshot_id="1009" root_project_id="1" root_snapshot_id="1009" + scope="DIR" qualifier="PAC" created_at="2008-11-04 13:58:00.00" version="[null]" path="1009." + status="U" islast="false" depth="1" /> + + <snapshots id="1011" project_id="3" parent_snapshot_id="1010" root_project_id="1" root_snapshot_id="1009" + scope="FIL" qualifier="CLA" created_at="2008-11-04 13:58:00.00" version="[null]" path="1009.1010." + status="U" islast="false" depth="2" /> +</dataset>
\ No newline at end of file |