From: Julien HENRY Date: Thu, 12 May 2016 09:25:03 +0000 (+0200) Subject: SONAR-7464 Optimize NCLOC_DATA and COMMENT_LINES_DATA X-Git-Tag: 5.6-RC1~106 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=03a0d8af47f6fb3acd5156b133ffbfea9d1d1586;p=sonarqube.git SONAR-7464 Optimize NCLOC_DATA and COMMENT_LINES_DATA --- diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/measure/internal/DefaultMeasure.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/measure/internal/DefaultMeasure.java index a6af74db14c..8be35ac3c95 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/measure/internal/DefaultMeasure.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/measure/internal/DefaultMeasure.java @@ -56,7 +56,7 @@ public class DefaultMeasure extends DefaultStorable impl @Override public DefaultMeasure forMetric(Metric metric) { - Preconditions.checkState(metric != null, "Metric already defined"); + Preconditions.checkState(this.metric == null, "Metric already defined"); Preconditions.checkNotNull(metric, "metric should be non null"); this.metric = metric; return this; diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java index cd3b57d20f5..dd975732b28 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java @@ -429,12 +429,13 @@ public final class CoreMetrics { .create(); public static final String FILE_COMPLEXITY_DISTRIBUTION_KEY = "file_complexity_distribution"; - public static final Metric FILE_COMPLEXITY_DISTRIBUTION = new Metric.Builder(FILE_COMPLEXITY_DISTRIBUTION_KEY, "Files Distribution / Complexity", Metric.ValueType.DISTRIB) - .setDescription("Files distribution /complexity") - .setDirection(Metric.DIRECTION_NONE) - .setQualitative(true) - .setDomain(DOMAIN_COMPLEXITY) - .create(); + public static final Metric FILE_COMPLEXITY_DISTRIBUTION = new Metric.Builder(FILE_COMPLEXITY_DISTRIBUTION_KEY, "Files Distribution / Complexity", + Metric.ValueType.DISTRIB) + .setDescription("Files distribution /complexity") + .setDirection(Metric.DIRECTION_NONE) + .setQualitative(true) + .setDomain(DOMAIN_COMPLEXITY) + .create(); // -------------------------------------------------------------------------------------------------------------------- // @@ -2449,6 +2450,7 @@ public final class CoreMetrics { /** * Information about lines of code in file. * Key-value pairs, where key - is a number of line, and value - is an indicator of whether line contains code (1) or not (0). + * If a line number is missing in the map it is equivalent to the default value (0). * * @see org.sonar.api.measures.FileLinesContext * @since 2.14 @@ -2466,6 +2468,7 @@ public final class CoreMetrics { /** * Information about comments in file. * Key-value pairs, where key - is a number of line, and value - is an indicator of whether line contains comment (1) or not (0). + * If a line number is missing in the map it is equivalent to the default value (0). * * @see org.sonar.api.measures.FileLinesContext * @since 2.14 @@ -2483,6 +2486,7 @@ public final class CoreMetrics { /** * Information about executable lines of code in file. * Key-value pairs, where key - is a number of line, and value - is an indicator of whether line contains executable code (1) or not (0). + * If a line number is missing in the map it is equivalent to the default value (0). * * @see org.sonar.api.measures.FileLinesContext * @since 5.5 diff --git a/sonar-scanner-engine/src/main/java/org/sonar/batch/DefaultFileLinesContext.java b/sonar-scanner-engine/src/main/java/org/sonar/batch/DefaultFileLinesContext.java index a4045f18ce0..85931a2be11 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/batch/DefaultFileLinesContext.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/batch/DefaultFileLinesContext.java @@ -21,48 +21,65 @@ package org.sonar.batch; import com.google.common.base.Objects; import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import java.util.Map; -import org.sonar.api.batch.SonarIndex; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.measure.MetricFinder; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.FileLinesContext; import org.sonar.api.measures.Measure; -import org.sonar.api.measures.Metric; -import org.sonar.api.measures.PersistenceMode; -import org.sonar.api.resources.Resource; -import org.sonar.api.resources.ResourceUtils; import org.sonar.api.utils.KeyValueFormat; import org.sonar.api.utils.KeyValueFormat.Converter; +import org.sonar.batch.scan.measure.MeasureCache; + +import static com.google.common.collect.Maps.filterValues; public class DefaultFileLinesContext implements FileLinesContext { - private final SonarIndex index; - private final Resource resource; + private static final Predicate LINES_WITH_NON_ZERO_VALUE = new Predicate() { + @Override + public boolean apply(Object input) { + return !input.equals(0); + } + }; + + private final SensorContext context; + private final InputFile inputFile; + private final MetricFinder metricFinder; + private final MeasureCache measureCache; /** * metric key -> line -> value */ private final Map> map = Maps.newHashMap(); - public DefaultFileLinesContext(SonarIndex index, Resource resource) { - Preconditions.checkNotNull(index); - Preconditions.checkArgument(ResourceUtils.isFile(resource)); - this.index = index; - this.resource = resource; + public DefaultFileLinesContext(SensorContext context, InputFile inputFile, MetricFinder metricFinder, MeasureCache measureCache) { + this.context = context; + this.inputFile = inputFile; + this.metricFinder = metricFinder; + this.measureCache = measureCache; } @Override public void setIntValue(String metricKey, int line, int value) { Preconditions.checkNotNull(metricKey); - Preconditions.checkArgument(line > 0); + checkLineRange(line); setValue(metricKey, line, value); } + private void checkLineRange(int line) { + Preconditions.checkArgument(line > 0, "Line number should be positive for file %s.", inputFile); + Preconditions.checkArgument(line <= inputFile.lines(), "Line %s is out of range for file %s. File has %s lines.", line, inputFile, inputFile.lines()); + } + @Override public Integer getIntValue(String metricKey, int line) { Preconditions.checkNotNull(metricKey); - Preconditions.checkArgument(line > 0); + checkLineRange(line); Map lines = map.get(metricKey); if (lines == null) { @@ -76,7 +93,7 @@ public class DefaultFileLinesContext implements FileLinesContext { @Override public void setStringValue(String metricKey, int line, String value) { Preconditions.checkNotNull(metricKey); - Preconditions.checkArgument(line > 0); + checkLineRange(line); Preconditions.checkNotNull(value); setValue(metricKey, line, value); @@ -85,7 +102,7 @@ public class DefaultFileLinesContext implements FileLinesContext { @Override public String getStringValue(String metricKey, int line) { Preconditions.checkNotNull(metricKey); - Preconditions.checkArgument(line > 0); + checkLineRange(line); Map lines = map.get(metricKey); if (lines == null) { @@ -115,19 +132,27 @@ public class DefaultFileLinesContext implements FileLinesContext { String metricKey = entry.getKey(); Map lines = entry.getValue(); if (shouldSave(lines)) { - String data = KeyValueFormat.format(lines); - Measure measure = new Measure(metricKey) - .setPersistenceMode(PersistenceMode.DATABASE) - .setData(data); - index.addMeasure(resource, measure); + String data = KeyValueFormat.format(optimizeStorage(metricKey, lines)); + context.newMeasure() + .on(inputFile) + .forMetric(metricFinder.findByKey(metricKey)) + .withValue(data) + .save(); entry.setValue(ImmutableMap.copyOf(lines)); } } } + private static Map optimizeStorage(String metricKey, Map lines) { + // SONAR-7464 Don't store 0 because this is default value anyway + if (CoreMetrics.NCLOC_DATA_KEY.equals(metricKey) || CoreMetrics.COMMENT_LINES_DATA_KEY.equals(metricKey) || CoreMetrics.EXECUTABLE_LINES_DATA_KEY.equals(metricKey)) { + return filterValues(lines, LINES_WITH_NON_ZERO_VALUE); + } + return lines; + } + private Map loadData(String metricKey, Converter converter) { - // FIXME no way to load measure only by key - Measure measure = index.getMeasure(resource, new Metric(metricKey)); + Measure measure = measureCache.byMetric(inputFile.key(), metricKey); String data = measure != null ? measure.getData() : null; if (data != null) { return ImmutableMap.copyOf(KeyValueFormat.parse(data, KeyValueFormat.newIntegerConverter(), converter)); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/batch/DefaultFileLinesContextFactory.java b/sonar-scanner-engine/src/main/java/org/sonar/batch/DefaultFileLinesContextFactory.java index 922f101f085..e15c94877c1 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/batch/DefaultFileLinesContextFactory.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/batch/DefaultFileLinesContextFactory.java @@ -19,34 +19,50 @@ */ package org.sonar.batch; +import com.google.common.base.Preconditions; +import org.sonar.api.batch.SensorContext; import org.sonar.api.batch.SonarIndex; import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.measure.MetricFinder; import org.sonar.api.measures.FileLinesContext; import org.sonar.api.measures.FileLinesContextFactory; import org.sonar.api.resources.File; import org.sonar.api.resources.Resource; +import org.sonar.api.resources.ResourceUtils; +import org.sonar.batch.index.BatchComponentCache; +import org.sonar.batch.scan.measure.MeasureCache; public class DefaultFileLinesContextFactory implements FileLinesContextFactory { private final SonarIndex index; + private final SensorContext sensorContext; + private final MetricFinder metricFinder; + private final MeasureCache measureCache; + private final BatchComponentCache scannerComponentCache; - public DefaultFileLinesContextFactory(SonarIndex index) { + public DefaultFileLinesContextFactory(SonarIndex index, SensorContext sensorContext, MetricFinder metricFinder, BatchComponentCache scannerComponentCache, + MeasureCache measureCache) { this.index = index; + this.sensorContext = sensorContext; + this.metricFinder = metricFinder; + this.scannerComponentCache = scannerComponentCache; + this.measureCache = measureCache; } @Override - public FileLinesContext createFor(Resource model) { + public FileLinesContext createFor(Resource resource) { + Preconditions.checkArgument(ResourceUtils.isFile(resource)); // Reload resource in case it use deprecated key - Resource resource = index.getResource(model); - return new DefaultFileLinesContext(index, resource); + File file = (File) index.getResource(resource); + if (file == null) { + throw new IllegalArgumentException("Unable to find resource " + resource + " in index."); + } + return new DefaultFileLinesContext(sensorContext, (InputFile) scannerComponentCache.get(file).inputComponent(), metricFinder, measureCache); } @Override public FileLinesContext createFor(InputFile inputFile) { - File sonarFile = File.create(inputFile.relativePath()); - // Reload resource from index - sonarFile = index.getResource(sonarFile); - return new DefaultFileLinesContext(index, sonarFile); + return new DefaultFileLinesContext(sensorContext, inputFile, metricFinder, measureCache); } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java b/sonar-scanner-engine/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java index daba4b1c6c9..194de4c363f 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java @@ -28,6 +28,7 @@ import org.sonar.api.batch.fs.internal.FileMetadata; import org.sonar.api.batch.rule.CheckFactory; import org.sonar.api.resources.Project; import org.sonar.api.scan.filesystem.FileExclusions; +import org.sonar.batch.DefaultFileLinesContextFactory; import org.sonar.batch.DefaultProjectTree; import org.sonar.batch.bootstrap.BatchExtensionDictionnary; import org.sonar.batch.bootstrap.ExtensionInstaller; @@ -168,7 +169,9 @@ public class ModuleScanContainer extends ComponentContainer { // Perspectives BatchPerspectives.class, HighlightableBuilder.class, - SymbolizableBuilder.class); + SymbolizableBuilder.class, + + DefaultFileLinesContextFactory.class); } private void addExtensions() { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java b/sonar-scanner-engine/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java index d30e41090f5..23c83b36550 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java @@ -29,7 +29,6 @@ import org.sonar.api.resources.ResourceTypes; import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; -import org.sonar.batch.DefaultFileLinesContextFactory; import org.sonar.batch.DefaultProjectTree; import org.sonar.batch.ProjectConfigurator; import org.sonar.batch.analysis.AnalysisProperties; @@ -141,7 +140,6 @@ public class ProjectScanContainer extends ComponentContainer { MetricProvider.class, ProjectConfigurator.class, DefaultIndex.class, - DefaultFileLinesContextFactory.class, Caches.class, BatchComponentCache.class, DefaultIssueCallback.class, diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/DefaultFileLinesContextTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/DefaultFileLinesContextTest.java index eb622b003fa..41f62087eeb 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/batch/DefaultFileLinesContextTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/batch/DefaultFileLinesContextTest.java @@ -20,129 +20,159 @@ package org.sonar.batch; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Matchers; -import org.sonar.api.batch.SonarIndex; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.measure.MetricFinder; +import org.sonar.api.batch.sensor.internal.SensorContextTester; +import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Measure; -import org.sonar.api.measures.Metric; -import org.sonar.api.measures.PersistenceMode; -import org.sonar.api.resources.Directory; -import org.sonar.api.resources.Resource; -import org.sonar.api.resources.Scopes; +import org.sonar.batch.scan.measure.MeasureCache; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.sonar.api.measures.CoreMetrics.COMMENT_LINES_DATA_KEY; +import static org.sonar.api.measures.CoreMetrics.EXECUTABLE_LINES_DATA_KEY; +import static org.sonar.api.measures.CoreMetrics.NCLOC_DATA_KEY; public class DefaultFileLinesContextTest { - private SonarIndex index; - private Resource resource; + private static final String HITS_METRIC_KEY = "hits"; + private static final String AUTHOR_METRIC_KEY = "author"; + private static final String BRANCHES_METRIC_KEY = "branches"; + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + private DefaultFileLinesContext fileLineMeasures; - @Before - public void setUp() { - index = mock(SonarIndex.class); - resource = mock(Resource.class); - when(resource.getScope()).thenReturn(Scopes.FILE); - fileLineMeasures = new DefaultFileLinesContext(index, resource); - } + private SensorContextTester sensorContextTester; + private MeasureCache measureCache; - @Test(expected = IllegalArgumentException.class) - public void shouldNotAllowCreationForDirectory() { - new DefaultFileLinesContext(index, Directory.create("key")); + @Before + public void setUp() throws Exception { + sensorContextTester = SensorContextTester.create(temp.newFolder()); + MetricFinder metricFinder = mock(MetricFinder.class); + org.sonar.api.batch.measure.Metric hitsMetric = mock(org.sonar.api.batch.measure.Metric.class); + when(hitsMetric.valueType()).thenReturn(String.class); + when(hitsMetric.key()).thenReturn(HITS_METRIC_KEY); + when(metricFinder.findByKey(HITS_METRIC_KEY)).thenReturn(hitsMetric); + org.sonar.api.batch.measure.Metric authorMetric = mock(org.sonar.api.batch.measure.Metric.class); + when(authorMetric.valueType()).thenReturn(String.class); + when(authorMetric.key()).thenReturn(AUTHOR_METRIC_KEY); + when(metricFinder.findByKey(AUTHOR_METRIC_KEY)).thenReturn(authorMetric); + org.sonar.api.batch.measure.Metric branchesMetric = mock(org.sonar.api.batch.measure.Metric.class); + when(branchesMetric.valueType()).thenReturn(String.class); + when(branchesMetric.key()).thenReturn(BRANCHES_METRIC_KEY); + when(metricFinder.findByKey(BRANCHES_METRIC_KEY)).thenReturn(branchesMetric); + when(metricFinder.findByKey(CoreMetrics.NCLOC_DATA_KEY)).thenReturn(CoreMetrics.NCLOC_DATA); + when(metricFinder.findByKey(CoreMetrics.EXECUTABLE_LINES_DATA_KEY)).thenReturn(CoreMetrics.EXECUTABLE_LINES_DATA); + when(metricFinder.findByKey(CoreMetrics.COMMENT_LINES_DATA_KEY)).thenReturn(CoreMetrics.COMMENT_LINES_DATA); + measureCache = mock(MeasureCache.class); + fileLineMeasures = new DefaultFileLinesContext(sensorContextTester, new DefaultInputFile("foo", "src/foo.php").initMetadata("Foo\nbar\nbiz"), metricFinder, + measureCache); } @Test public void shouldSave() { - fileLineMeasures.setIntValue("hits", 1, 2); - fileLineMeasures.setIntValue("hits", 3, 4); + fileLineMeasures.setIntValue(HITS_METRIC_KEY, 1, 2); + fileLineMeasures.setIntValue(HITS_METRIC_KEY, 3, 0); fileLineMeasures.save(); - assertThat(fileLineMeasures.toString()).isEqualTo("DefaultFileLinesContext{map={hits={1=2, 3=4}}}"); + assertThat(fileLineMeasures.toString()).isEqualTo("DefaultFileLinesContext{map={hits={1=2, 3=0}}}"); + + assertThat(sensorContextTester.measure("foo:src/foo.php", HITS_METRIC_KEY).value()).isEqualTo("1=2;3=0"); + } + + @Test + public void validateLineGreaterThanZero() { + thrown.expectMessage("Line number should be positive for file [moduleKey=foo, relative=src/foo.php, basedir=null]."); + fileLineMeasures.setIntValue(HITS_METRIC_KEY, 0, 2); + } + + @Test + public void validateLineLowerThanLineCount() { + thrown.expectMessage("Line 4 is out of range for file [moduleKey=foo, relative=src/foo.php, basedir=null]. File has 3 lines"); + fileLineMeasures.setIntValue(HITS_METRIC_KEY, 4, 2); + } + + @Test + public void optimizeValues() { + fileLineMeasures.setIntValue(NCLOC_DATA_KEY, 1, 0); + fileLineMeasures.setIntValue(NCLOC_DATA_KEY, 2, 1); + fileLineMeasures.setIntValue(EXECUTABLE_LINES_DATA_KEY, 1, 0); + fileLineMeasures.setIntValue(EXECUTABLE_LINES_DATA_KEY, 2, 1); + fileLineMeasures.setIntValue(COMMENT_LINES_DATA_KEY, 1, 0); + fileLineMeasures.setIntValue(COMMENT_LINES_DATA_KEY, 2, 1); + fileLineMeasures.save(); - ArgumentCaptor measureCaptor = ArgumentCaptor.forClass(Measure.class); - verify(index).addMeasure(Matchers.eq(resource), measureCaptor.capture()); - Measure measure = measureCaptor.getValue(); - assertThat(measure.getMetricKey(), is("hits")); - assertThat(measure.getPersistenceMode(), is(PersistenceMode.DATABASE)); - assertThat(measure.getData(), is("1=2;3=4")); + assertThat(sensorContextTester.measure("foo:src/foo.php", NCLOC_DATA_KEY).value()).isEqualTo("2=1"); + assertThat(sensorContextTester.measure("foo:src/foo.php", EXECUTABLE_LINES_DATA_KEY).value()).isEqualTo("2=1"); + assertThat(sensorContextTester.measure("foo:src/foo.php", COMMENT_LINES_DATA_KEY).value()).isEqualTo("2=1"); } @Test public void shouldSaveSeveral() { - fileLineMeasures.setIntValue("hits", 1, 2); - fileLineMeasures.setIntValue("hits", 3, 4); - fileLineMeasures.setStringValue("author", 1, "simon"); - fileLineMeasures.setStringValue("author", 3, "evgeny"); + fileLineMeasures.setIntValue(HITS_METRIC_KEY, 1, 2); + fileLineMeasures.setIntValue(HITS_METRIC_KEY, 3, 4); + fileLineMeasures.setStringValue(AUTHOR_METRIC_KEY, 1, "simon"); + fileLineMeasures.setStringValue(AUTHOR_METRIC_KEY, 3, "evgeny"); fileLineMeasures.save(); - fileLineMeasures.setIntValue("branches", 1, 2); - fileLineMeasures.setIntValue("branches", 3, 4); + fileLineMeasures.setIntValue(BRANCHES_METRIC_KEY, 1, 2); + fileLineMeasures.setIntValue(BRANCHES_METRIC_KEY, 3, 4); fileLineMeasures.save(); - verify(index, times(3)).addMeasure(Matchers.eq(resource), Matchers.any(Measure.class)); + assertThat(sensorContextTester.measure("foo:src/foo.php", HITS_METRIC_KEY).value()).isEqualTo("1=2;3=4"); + assertThat(sensorContextTester.measure("foo:src/foo.php", AUTHOR_METRIC_KEY).value()).isEqualTo("1=simon;3=evgeny"); + assertThat(sensorContextTester.measure("foo:src/foo.php", BRANCHES_METRIC_KEY).value()).isEqualTo("1=2;3=4"); } @Test(expected = UnsupportedOperationException.class) public void shouldNotModifyAfterSave() { - fileLineMeasures.setIntValue("hits", 1, 2); - fileLineMeasures.save(); + fileLineMeasures.setIntValue(HITS_METRIC_KEY, 1, 2); fileLineMeasures.save(); - verify(index).addMeasure(Matchers.eq(resource), Matchers.any(Measure.class)); - fileLineMeasures.setIntValue("hits", 1, 2); + fileLineMeasures.setIntValue(HITS_METRIC_KEY, 1, 2); } @Test public void shouldLoadIntValues() { - when(index.getMeasure(Matchers.any(Resource.class), Matchers.any(Metric.class))) - .thenReturn(new Measure("hits").setData("1=2;3=4")); + when(measureCache.byMetric("foo:src/foo.php", HITS_METRIC_KEY)).thenReturn(new Measure(HITS_METRIC_KEY).setData("1=2;3=4")); - assertThat(fileLineMeasures.getIntValue("hits", 1), is(2)); - assertThat(fileLineMeasures.getIntValue("hits", 3), is(4)); - assertThat("no measure on line", fileLineMeasures.getIntValue("hits", 5), nullValue()); + assertThat(fileLineMeasures.getIntValue(HITS_METRIC_KEY, 1), is(2)); + assertThat(fileLineMeasures.getIntValue(HITS_METRIC_KEY, 3), is(4)); + assertThat("no measure on line", fileLineMeasures.getIntValue(HITS_METRIC_KEY, 2), nullValue()); } @Test public void shouldLoadStringValues() { - when(index.getMeasure(Matchers.any(Resource.class), Matchers.any(Metric.class))) - .thenReturn(new Measure("author").setData("1=simon;3=evgeny")); - - assertThat(fileLineMeasures.getStringValue("author", 1), is("simon")); - assertThat(fileLineMeasures.getStringValue("author", 3), is("evgeny")); - assertThat("no measure on line", fileLineMeasures.getStringValue("author", 5), nullValue()); - } - - @Test - public void shouldNotSaveAfterLoad() { - when(index.getMeasure(Matchers.any(Resource.class), Matchers.any(Metric.class))) - .thenReturn(new Measure("author").setData("1=simon;3=evgeny")); - - fileLineMeasures.getStringValue("author", 1); - fileLineMeasures.save(); + when(measureCache.byMetric("foo:src/foo.php", AUTHOR_METRIC_KEY)).thenReturn(new Measure(AUTHOR_METRIC_KEY).setData("1=simon;3=evgeny")); - verify(index, never()).addMeasure(Matchers.eq(resource), Matchers.any(Measure.class)); + assertThat(fileLineMeasures.getStringValue(AUTHOR_METRIC_KEY, 1), is("simon")); + assertThat(fileLineMeasures.getStringValue(AUTHOR_METRIC_KEY, 3), is("evgeny")); + assertThat("no measure on line", fileLineMeasures.getStringValue(AUTHOR_METRIC_KEY, 2), nullValue()); } @Test(expected = UnsupportedOperationException.class) public void shouldNotModifyAfterLoad() { - when(index.getMeasure(Matchers.any(Resource.class), Matchers.any(Metric.class))) - .thenReturn(new Measure("author").setData("1=simon;3=evgeny")); + when(measureCache.byMetric("foo:src/foo.php", AUTHOR_METRIC_KEY)).thenReturn(new Measure(AUTHOR_METRIC_KEY).setData("1=simon;3=evgeny")); - fileLineMeasures.getStringValue("author", 1); - fileLineMeasures.setStringValue("author", 1, "evgeny"); + fileLineMeasures.getStringValue(AUTHOR_METRIC_KEY, 1); + fileLineMeasures.setStringValue(AUTHOR_METRIC_KEY, 1, "evgeny"); } @Test public void shouldNotFailIfNoMeasureInIndex() { - assertThat(fileLineMeasures.getIntValue("hits", 1), nullValue()); - assertThat(fileLineMeasures.getStringValue("author", 1), nullValue()); + assertThat(fileLineMeasures.getIntValue(HITS_METRIC_KEY, 1), nullValue()); + assertThat(fileLineMeasures.getStringValue(AUTHOR_METRIC_KEY, 1), nullValue()); } }