]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7464 Optimize NCLOC_DATA and COMMENT_LINES_DATA 964/head
authorJulien HENRY <julien.henry@sonarsource.com>
Thu, 12 May 2016 09:25:03 +0000 (11:25 +0200)
committerJulien HENRY <julien.henry@sonarsource.com>
Fri, 13 May 2016 11:46:31 +0000 (13:46 +0200)
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/measure/internal/DefaultMeasure.java
sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java
sonar-scanner-engine/src/main/java/org/sonar/batch/DefaultFileLinesContext.java
sonar-scanner-engine/src/main/java/org/sonar/batch/DefaultFileLinesContextFactory.java
sonar-scanner-engine/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java
sonar-scanner-engine/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
sonar-scanner-engine/src/test/java/org/sonar/batch/DefaultFileLinesContextTest.java

index a6af74db14c9729c07fc9e611dae96081091fc7a..8be35ac3c954a369af7551c08b6b94c5fc56cb7d 100644 (file)
@@ -56,7 +56,7 @@ public class DefaultMeasure<G extends Serializable> extends DefaultStorable impl
 
   @Override
   public DefaultMeasure<G> forMetric(Metric<G> 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;
index cd3b57d20f547b55c4d2c7487700ef7aa12b8b08..dd975732b2895a0d15379c53bedf1ac710825fae 100644 (file)
@@ -429,12 +429,13 @@ public final class CoreMetrics {
       .create();
 
   public static final String FILE_COMPLEXITY_DISTRIBUTION_KEY = "file_complexity_distribution";
-  public static final Metric<String> 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<String> 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
index a4045f18ce048d98e61141b4b90ddff2430c8dd5..85931a2be11c88bee237a6f56aec44cc1c08bda5 100644 (file)
@@ -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<Object> LINES_WITH_NON_ZERO_VALUE = new Predicate<Object>() {
+    @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<String, Map<Integer, Object>> 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<Integer, Object> 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<Integer, Object> 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));
index 922f101f085a44b64ad7e981fa9c8c6dd574c5f9..e15c94877c1fdefb631ffda69cc242734ddf9b0c 100644 (file)
  */
 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);
   }
 
 }
index daba4b1c6c91dfe7a63f3f08a01eff5049118ae7..194de4c363f4a6122bdf98536b9987fafbae488a 100644 (file)
@@ -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() {
index d30e41090f5abd7431c4aaca08372069d863a83a..23c83b365507c04a8efca3cc0ea790cdde74025f 100644 (file)
@@ -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,
index eb622b003fa743f3011ac5bbefdf0f9edc31c05c..41f62087eebe21d3a328d0d3106b7589fe952ac2 100644 (file)
 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<String> hitsMetric = mock(org.sonar.api.batch.measure.Metric.class);
+    when(hitsMetric.valueType()).thenReturn(String.class);
+    when(hitsMetric.key()).thenReturn(HITS_METRIC_KEY);
+    when(metricFinder.<String>findByKey(HITS_METRIC_KEY)).thenReturn(hitsMetric);
+    org.sonar.api.batch.measure.Metric<String> authorMetric = mock(org.sonar.api.batch.measure.Metric.class);
+    when(authorMetric.valueType()).thenReturn(String.class);
+    when(authorMetric.key()).thenReturn(AUTHOR_METRIC_KEY);
+    when(metricFinder.<String>findByKey(AUTHOR_METRIC_KEY)).thenReturn(authorMetric);
+    org.sonar.api.batch.measure.Metric<String> branchesMetric = mock(org.sonar.api.batch.measure.Metric.class);
+    when(branchesMetric.valueType()).thenReturn(String.class);
+    when(branchesMetric.key()).thenReturn(BRANCHES_METRIC_KEY);
+    when(metricFinder.<String>findByKey(BRANCHES_METRIC_KEY)).thenReturn(branchesMetric);
+    when(metricFinder.<String>findByKey(CoreMetrics.NCLOC_DATA_KEY)).thenReturn(CoreMetrics.NCLOC_DATA);
+    when(metricFinder.<String>findByKey(CoreMetrics.EXECUTABLE_LINES_DATA_KEY)).thenReturn(CoreMetrics.EXECUTABLE_LINES_DATA);
+    when(metricFinder.<String>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<Measure> 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());
   }
 
 }