From bd604e40905e54bef42a206a8a638f03783aba3a Mon Sep 17 00:00:00 2001 From: simonbrandhof Date: Thu, 28 Apr 2011 01:22:08 +0200 Subject: [PATCH] SONAR-2202 Calculate variations on quality model measures --- plugins/sonar-core-plugin/derby.log | 91 +++++++++++++++++++ .../core/timemachine/VariationDecorator.java | 84 ++++++++--------- .../timemachine/VariationDecoratorTest.java | 18 ++-- .../batch/components/PastMeasuresLoader.java | 41 +++++---- .../components/PastMeasuresLoaderTest.java | 31 +++++-- 5 files changed, 186 insertions(+), 79 deletions(-) create mode 100644 plugins/sonar-core-plugin/derby.log diff --git a/plugins/sonar-core-plugin/derby.log b/plugins/sonar-core-plugin/derby.log new file mode 100644 index 00000000000..bd143daf3dc --- /dev/null +++ b/plugins/sonar-core-plugin/derby.log @@ -0,0 +1,91 @@ +---------------------------------------------------------------- +Thu Apr 28 00:17:56 CEST 2011: +Booting Derby version The Apache Software Foundation - Apache Derby - 10.7.1.1 - (1040133): instance a816c00e-012f-990a-3e0e-000004e64398 +on database directory memory:/Users/sbrandhof/projects/github/sonar/plugins/sonar-core-plugin/sonar with class loader sun.misc.Launcher$AppClassLoader@1ef6a746 +Loaded from file:/Users/sbrandhof/.m2/repository/org/apache/derby/derby/10.7.1.1/derby-10.7.1.1.jar +java.vendor=Apple Inc. +java.runtime.version=1.6.0_24-b07-334-10M3326 +Database Class Loader started - derby.database.classpath='' +---------------------------------------------------------------- +Thu Apr 28 00:18:01 CEST 2011: +Shutting down instance a816c00e-012f-990a-3e0e-000004e64398 on database directory memory:/Users/sbrandhof/projects/github/sonar/plugins/sonar-core-plugin/sonar with class loader sun.misc.Launcher$AppClassLoader@1ef6a746 +Thu Apr 28 00:18:01 CEST 2011 Thread[main,5,main] Cleanup action starting +java.sql.SQLException: Database 'memory:sonar' not found. + at org.apache.derby.impl.jdbc.SQLExceptionFactory40.getSQLException(Unknown Source) + at org.apache.derby.impl.jdbc.Util.newEmbedSQLException(Unknown Source) + at org.apache.derby.impl.jdbc.Util.newEmbedSQLException(Unknown Source) + at org.apache.derby.impl.jdbc.Util.generateCsSQLException(Unknown Source) + at org.apache.derby.impl.jdbc.EmbedConnection.newSQLException(Unknown Source) + at org.apache.derby.impl.jdbc.EmbedConnection.handleDBNotFound(Unknown Source) + at org.apache.derby.impl.jdbc.EmbedConnection.(Unknown Source) + at org.apache.derby.impl.jdbc.EmbedConnection30.(Unknown Source) + at org.apache.derby.impl.jdbc.EmbedConnection40.(Unknown Source) + at org.apache.derby.jdbc.Driver40.getNewEmbedConnection(Unknown Source) + at org.apache.derby.jdbc.InternalDriver.connect(Unknown Source) + at org.apache.derby.jdbc.EmbeddedDriver.connect(Unknown Source) + at java.sql.DriverManager.getConnection(DriverManager.java:582) + at java.sql.DriverManager.getConnection(DriverManager.java:207) + at org.sonar.test.persistence.DatabaseTestCase.stopDatabase(DatabaseTestCase.java:89) + at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) + at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) + at java.lang.reflect.Method.invoke(Method.java:597) + at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44) + at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) + at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41) + at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:37) + at org.junit.runners.ParentRunner.run(ParentRunner.java:236) + at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:59) + at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.executeTestSet(AbstractDirectoryTestSuite.java:120) + at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.execute(AbstractDirectoryTestSuite.java:145) + at org.apache.maven.surefire.Surefire.run(Surefire.java:104) + at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) + at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) + at java.lang.reflect.Method.invoke(Method.java:597) + at org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(SurefireBooter.java:290) + at org.apache.maven.surefire.booter.SurefireBooter.main(SurefireBooter.java:1017) +Caused by: java.sql.SQLException: Database 'memory:sonar' not found. + at org.apache.derby.impl.jdbc.SQLExceptionFactory.getSQLException(Unknown Source) + at org.apache.derby.impl.jdbc.SQLExceptionFactory40.wrapArgsForTransportAcrossDRDA(Unknown Source) + ... 34 more +============= begin nested exception, level (1) =========== +java.sql.SQLException: Database 'memory:sonar' not found. + at org.apache.derby.impl.jdbc.SQLExceptionFactory.getSQLException(Unknown Source) + at org.apache.derby.impl.jdbc.SQLExceptionFactory40.wrapArgsForTransportAcrossDRDA(Unknown Source) + at org.apache.derby.impl.jdbc.SQLExceptionFactory40.getSQLException(Unknown Source) + at org.apache.derby.impl.jdbc.Util.newEmbedSQLException(Unknown Source) + at org.apache.derby.impl.jdbc.Util.newEmbedSQLException(Unknown Source) + at org.apache.derby.impl.jdbc.Util.generateCsSQLException(Unknown Source) + at org.apache.derby.impl.jdbc.EmbedConnection.newSQLException(Unknown Source) + at org.apache.derby.impl.jdbc.EmbedConnection.handleDBNotFound(Unknown Source) + at org.apache.derby.impl.jdbc.EmbedConnection.(Unknown Source) + at org.apache.derby.impl.jdbc.EmbedConnection30.(Unknown Source) + at org.apache.derby.impl.jdbc.EmbedConnection40.(Unknown Source) + at org.apache.derby.jdbc.Driver40.getNewEmbedConnection(Unknown Source) + at org.apache.derby.jdbc.InternalDriver.connect(Unknown Source) + at org.apache.derby.jdbc.EmbeddedDriver.connect(Unknown Source) + at java.sql.DriverManager.getConnection(DriverManager.java:582) + at java.sql.DriverManager.getConnection(DriverManager.java:207) + at org.sonar.test.persistence.DatabaseTestCase.stopDatabase(DatabaseTestCase.java:89) + at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) + at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) + at java.lang.reflect.Method.invoke(Method.java:597) + at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44) + at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) + at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41) + at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:37) + at org.junit.runners.ParentRunner.run(ParentRunner.java:236) + at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:59) + at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.executeTestSet(AbstractDirectoryTestSuite.java:120) + at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.execute(AbstractDirectoryTestSuite.java:145) + at org.apache.maven.surefire.Surefire.run(Surefire.java:104) + at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) + at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) + at java.lang.reflect.Method.invoke(Method.java:597) + at org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(SurefireBooter.java:290) + at org.apache.maven.surefire.booter.SurefireBooter.main(SurefireBooter.java:1017) +============= end nested exception, level (1) =========== +Cleanup action completed diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/VariationDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/VariationDecorator.java index ac55d86ebf5..920436892a9 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/VariationDecorator.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/VariationDecorator.java @@ -19,16 +19,14 @@ */ package org.sonar.plugins.core.timemachine; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.apache.commons.lang.StringUtils; import org.sonar.api.batch.*; -import org.sonar.api.database.model.MeasureModel; 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.resources.Scopes; -import org.sonar.api.rules.RulePriority; import org.sonar.batch.components.PastMeasuresLoader; import org.sonar.batch.components.PastSnapshot; import org.sonar.batch.components.TimeMachineConfiguration; @@ -41,15 +39,14 @@ import java.util.Map; public class VariationDecorator implements Decorator { private List projectPastSnapshots; - private PastMeasuresLoader pastMeasuresLoader; private MetricFinder metricFinder; + private PastMeasuresLoader pastMeasuresLoader; + public VariationDecorator(PastMeasuresLoader pastMeasuresLoader, MetricFinder metricFinder, TimeMachineConfiguration configuration) { this(pastMeasuresLoader, metricFinder, configuration.getProjectPastSnapshots()); - } - - VariationDecorator(PastMeasuresLoader pastMeasuresLoader, MetricFinder metricFinder, List projectPastSnapshots) { + public VariationDecorator(PastMeasuresLoader pastMeasuresLoader, MetricFinder metricFinder, List projectPastSnapshots) { this.pastMeasuresLoader = pastMeasuresLoader; this.projectPastSnapshots = projectPastSnapshots; this.metricFinder = metricFinder; @@ -78,30 +75,32 @@ public class VariationDecorator implements Decorator { } private void calculateVariation(Resource resource, DecoratorContext context, PastSnapshot pastSnapshot) { - List pastMeasures = pastMeasuresLoader.getPastMeasures(resource, pastSnapshot); + List pastMeasures = pastMeasuresLoader.getPastMeasures(resource, pastSnapshot); compareWithPastMeasures(context, pastSnapshot.getIndex(), pastMeasures); } - void compareWithPastMeasures(DecoratorContext context, int index, List pastMeasures) { - Map pastMeasuresByKey = Maps.newHashMap(); - for (MeasureModel pastMeasure : pastMeasures) { + void compareWithPastMeasures(DecoratorContext context, int index, List pastMeasures) { + Map pastMeasuresByKey = Maps.newHashMap(); + for (Object[] pastMeasure : pastMeasures) { pastMeasuresByKey.put(new MeasureKey(pastMeasure), pastMeasure); } // for each measure, search equivalent past measure - for (Measure measure : context.getMeasures(MeasuresFilters.all())) { + for (Measure measure : context.getMeasures(new CustomMeasureFilter())) { // compare with past measure - Integer metricId = (measure.getMetric().getId()!=null ? measure.getMetric().getId() : metricFinder.findByKey(measure.getMetric().getKey()).getId()); - MeasureModel pastMeasure = pastMeasuresByKey.get(new MeasureKey(measure, metricId)); + Integer metricId = (measure.getMetric().getId() != null ? measure.getMetric().getId() : metricFinder.findByKey(measure.getMetric().getKey()).getId()); + Integer characteristicId = (measure.getCharacteristic() != null ? measure.getCharacteristic().getId() : null); + + Object[] pastMeasure = pastMeasuresByKey.get(new MeasureKey(metricId, characteristicId)); if (updateVariation(measure, pastMeasure, index)) { context.saveMeasure(measure); } } } - boolean updateVariation(Measure measure, MeasureModel pastMeasure, int index) { - if (pastMeasure != null && pastMeasure.getValue() != null && measure.getValue() != null) { - double variation = (measure.getValue().doubleValue() - pastMeasure.getValue().doubleValue()); + boolean updateVariation(Measure measure, Object[] pastMeasure, int index) { + if (pastMeasure != null && PastMeasuresLoader.hasValue(pastMeasure) && measure.getValue() != null) { + double variation = (measure.getValue().doubleValue() - PastMeasuresLoader.getValue(pastMeasure)); measure.setVariation(index, variation); return true; } @@ -113,28 +112,30 @@ public class VariationDecorator implements Decorator { return getClass().getSimpleName(); } - static class MeasureKey { + static class CustomMeasureFilter implements MeasuresFilter> { + public Collection filter(Collection measures) { + List result = Lists.newArrayList(); + for (Measure measure : measures) { + if (!(measure instanceof RuleMeasure)) { + result.add(measure); + } + } + return result; + } + } + + static final class MeasureKey { Integer metricId; - Integer ruleId; - RulePriority priority; - Characteristic characteristic; - - MeasureKey(MeasureModel model) { - metricId = model.getMetricId(); - ruleId = model.getRuleId(); - priority = model.getRulePriority(); - characteristic = model.getCharacteristic(); + Integer characteristicId; + + MeasureKey(Object[] pastFields) { + metricId = PastMeasuresLoader.getMetricId(pastFields); + characteristicId = PastMeasuresLoader.getCharacteristicId(pastFields); } - MeasureKey(Measure measure, Integer metricId) { + MeasureKey(Integer metricId, Integer characteristicId) { this.metricId = metricId; - this.characteristic = measure.getCharacteristic(); - // TODO merge RuleMeasure into Measure - if (measure instanceof RuleMeasure) { - RuleMeasure rm = (RuleMeasure) measure; - this.ruleId = (rm.getRule() == null ? null : rm.getRule().getId()); - this.priority = rm.getRulePriority(); - } + this.characteristicId = characteristicId; } @Override @@ -145,29 +146,20 @@ public class VariationDecorator implements Decorator { if (o == null || getClass() != o.getClass()) { return false; } - MeasureKey that = (MeasureKey) o; - if (characteristic != null ? !characteristic.equals(that.characteristic) : that.characteristic != null) { + if (characteristicId != null ? !characteristicId.equals(that.characteristicId) : that.characteristicId != null) { return false; } if (!metricId.equals(that.metricId)) { return false; } - if (priority != that.priority) { - return false; - } - if (ruleId != null ? !ruleId.equals(that.ruleId) : that.ruleId != null) { - return false; - } return true; } @Override public int hashCode() { int result = metricId.hashCode(); - result = 31 * result + (ruleId != null ? ruleId.hashCode() : 0); - result = 31 * result + (priority != null ? priority.hashCode() : 0); - result = 31 * result + (characteristic != null ? characteristic.hashCode() : 0); + result = 31 * result + (characteristicId != null ? characteristicId.hashCode() : 0); return result; } } diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/VariationDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/VariationDecoratorTest.java index 242f6f94393..ad4fd12459e 100644 --- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/VariationDecoratorTest.java +++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/VariationDecoratorTest.java @@ -43,8 +43,11 @@ import static org.mockito.Mockito.*; public class VariationDecoratorTest extends AbstractDbUnitTestCase { - public static final Metric NCLOC = new Metric("ncloc").setId(12); - public static final Metric COVERAGE = new Metric("coverage").setId(16); + public static final int NCLOC_ID = 12; + public static final Metric NCLOC = new Metric("ncloc").setId(NCLOC_ID); + + public static final int COVERAGE_ID = 16; + public static final Metric COVERAGE = new Metric("coverage").setId(COVERAGE_ID); @Test public void shouldNotCalculateVariationsOnFiles() { @@ -67,12 +70,12 @@ public class VariationDecoratorTest extends AbstractDbUnitTestCase { // first past analysis when(pastMeasuresLoader.getPastMeasures(javaPackage, pastSnapshot1)).thenReturn(Arrays.asList( - newMeasureModel(NCLOC, 180.0), - newMeasureModel(COVERAGE, 75.0))); + new Object[]{NCLOC_ID, null, 180.0}, + new Object[]{COVERAGE_ID, null, 75.0})); // second past analysis - when(pastMeasuresLoader.getPastMeasures(javaPackage, pastSnapshot3)).thenReturn(Arrays.asList( - newMeasureModel(NCLOC, 240.0))); + when(pastMeasuresLoader.getPastMeasures(javaPackage, pastSnapshot3)).thenReturn(Arrays.asList( + new Object[]{NCLOC_ID, null, 240.0})); // current analysis DecoratorContext context = mock(DecoratorContext.class); @@ -80,7 +83,8 @@ public class VariationDecoratorTest extends AbstractDbUnitTestCase { Measure currentCoverage = newMeasure(COVERAGE, 80.0); when(context.getMeasures(Matchers.anyObject())).thenReturn(Arrays.asList(currentNcloc, currentCoverage)); - VariationDecorator decorator = new VariationDecorator(pastMeasuresLoader, mock(MetricFinder.class), Arrays.asList(pastSnapshot1, pastSnapshot3)); + VariationDecorator decorator = new VariationDecorator(pastMeasuresLoader, mock(MetricFinder.class), + Arrays.asList(pastSnapshot1, pastSnapshot3)); decorator.decorate(javaPackage, context); // context updated for each variation : 2 times for ncloc and 1 time for coverage diff --git a/sonar-batch/src/main/java/org/sonar/batch/components/PastMeasuresLoader.java b/sonar-batch/src/main/java/org/sonar/batch/components/PastMeasuresLoader.java index 19efcadd46d..95e5dff7bee 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/components/PastMeasuresLoader.java +++ b/sonar-batch/src/main/java/org/sonar/batch/components/PastMeasuresLoader.java @@ -23,7 +23,6 @@ import com.google.common.collect.Maps; import org.apache.commons.lang.ObjectUtils; import org.sonar.api.BatchExtension; 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.Metric; import org.sonar.api.measures.MetricFinder; @@ -57,24 +56,18 @@ public class PastMeasuresLoader implements BatchExtension { return metricByIds.values(); } - public List getPastMeasures(Resource resource, PastSnapshot projectPastSnapshot) { - return getPastMeasures(resource, projectPastSnapshot.getProjectSnapshot()); - } - - public List getPastMeasures(Resource resource, Snapshot projectSnapshot) { - if (isPersisted(resource) && projectSnapshot!=null) { - return getPastMeasures(resource.getId(), projectSnapshot); + public List getPastMeasures(Resource resource, PastSnapshot projectPastSnapshot) { + if (isPersisted(resource) && projectPastSnapshot != null && projectPastSnapshot.getProjectSnapshot()!=null) { + return getPastMeasures(resource.getId(), projectPastSnapshot.getProjectSnapshot()); } return Collections.emptyList(); } - public List getPastMeasures(int resourceId, Snapshot projectSnapshot) { - // TODO improvement : select only some columns - // TODO support measure on 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.ruleId=null and m.rulePriority=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) + public List getPastMeasures(int resourceId, Snapshot projectSnapshot) { + String sql = "select m.metric_id, m.characteristic_id, m.value from project_measures m, snapshots s" + + " where m.snapshot_id=s.id and m.metric_id in (:metricIds) and m.rule_id is null and m.rule_priority is null " + + " and (s.root_snapshot_id=:rootSnapshotId or s.id=:rootSnapshotId) and s.project_id=:resourceId and s.status=:status"; + return session.createNativeQuery(sql) .setParameter("metricIds", metricByIds.keySet()) .setParameter("rootSnapshotId", ObjectUtils.defaultIfNull(projectSnapshot.getRootId(), projectSnapshot.getId())) .setParameter("resourceId", resourceId) @@ -83,6 +76,22 @@ public class PastMeasuresLoader implements BatchExtension { } private boolean isPersisted(Resource resource) { - return resource.getId()!=null; + return resource.getId() != null; + } + + public static Integer getMetricId(Object[] row) { + return (Integer) row[0]; + } + + public static Integer getCharacteristicId(Object[] row) { + return (Integer) row[1]; + } + + public static boolean hasValue(Object[] row) { + return row[2] != null; + } + + public static double getValue(Object[] row) { + return ((Number) row[2]).doubleValue(); } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/components/PastMeasuresLoaderTest.java b/sonar-batch/src/test/java/org/sonar/batch/components/PastMeasuresLoaderTest.java index 2b2137eb288..74e19b28ddf 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/components/PastMeasuresLoaderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/components/PastMeasuresLoaderTest.java @@ -32,6 +32,7 @@ import java.util.List; import static org.hamcrest.CoreMatchers.anyOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.nullValue; import static org.junit.internal.matchers.IsCollectionContaining.hasItems; public class PastMeasuresLoaderTest extends AbstractDbUnitTestCase { @@ -48,13 +49,18 @@ public class PastMeasuresLoaderTest extends AbstractDbUnitTestCase { Snapshot projectSnapshot = getSession().getSingleResult(Snapshot.class, "id", PROJECT_SNAPSHOT_ID); PastMeasuresLoader loader = new PastMeasuresLoader(getSession(), metrics); - List measures = loader.getPastMeasures(FILE_ID, projectSnapshot); + List measures = loader.getPastMeasures(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))); - } + Object[] pastMeasure = measures.get(0); + assertThat(PastMeasuresLoader.getMetricId(pastMeasure), is(1)); + assertThat(PastMeasuresLoader.getCharacteristicId(pastMeasure), nullValue()); + assertThat(PastMeasuresLoader.getValue(pastMeasure), is(5.0)); + + pastMeasure = measures.get(1); + assertThat(PastMeasuresLoader.getMetricId(pastMeasure), is(2)); + assertThat(PastMeasuresLoader.getCharacteristicId(pastMeasure), nullValue()); + assertThat(PastMeasuresLoader.getValue(pastMeasure), is(60.0)); } @Test @@ -65,13 +71,18 @@ public class PastMeasuresLoaderTest extends AbstractDbUnitTestCase { Snapshot projectSnapshot = getSession().getSingleResult(Snapshot.class, "id", PROJECT_SNAPSHOT_ID); PastMeasuresLoader loader = new PastMeasuresLoader(getSession(), metrics); - List measures = loader.getPastMeasures(PROJECT_ID, projectSnapshot); + List measures = loader.getPastMeasures(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))); - } + Object[] pastMeasure = measures.get(0); + assertThat(PastMeasuresLoader.getMetricId(pastMeasure), is(1)); + assertThat(PastMeasuresLoader.getCharacteristicId(pastMeasure), nullValue()); + assertThat(PastMeasuresLoader.getValue(pastMeasure), is(60.0)); + + pastMeasure = measures.get(1); + assertThat(PastMeasuresLoader.getMetricId(pastMeasure), is(2)); + assertThat(PastMeasuresLoader.getCharacteristicId(pastMeasure), nullValue()); + assertThat(PastMeasuresLoader.getValue(pastMeasure), is(80.0)); } @Test -- 2.39.5