@Test
  public void readChangesets_is_not_cached() {
    writer.writeComponentChangesets(CHANGESETS);

    assertThat(underTest.readChangesets(COMPONENT_REF))
        .isNotSameAs(underTest.readChangesets(COMPONENT_REF));
  }
  @Test
  public void verify_readChangesets_returns_changesets() {
    writer.writeComponentChangesets(CHANGESETS);

    BatchReport.Changesets res = underTest.readChangesets(COMPONENT_REF);
    assertThat(res).isEqualTo(CHANGESETS);
  }
  @Override
  public synchronized void blameResult(InputFile file, List<BlameLine> lines) {
    Preconditions.checkNotNull(file);
    Preconditions.checkNotNull(lines);
    Preconditions.checkArgument(
        allFilesToBlame.contains(file),
        "It was not expected to blame file %s",
        file.relativePath());

    if (lines.size() != file.lines()) {
      LOG.debug(
          "Ignoring blame result since provider returned {} blame lines but file {} has {} lines",
          lines.size(),
          file.relativePath(),
          file.lines());
      return;
    }

    BatchComponent batchComponent = componentCache.get(file);
    Builder scmBuilder = BatchReport.Changesets.newBuilder();
    scmBuilder.setComponentRef(batchComponent.batchId());
    Map<String, Integer> changesetsIdByRevision = new HashMap<>();

    int lineId = 1;
    for (BlameLine line : lines) {
      validateLine(line, lineId, file);
      Integer changesetId = changesetsIdByRevision.get(line.revision());
      if (changesetId == null) {
        addChangeset(scmBuilder, line);
        changesetId = scmBuilder.getChangesetCount() - 1;
        changesetsIdByRevision.put(line.revision(), changesetId);
      }
      scmBuilder.addChangesetIndexByLine(changesetId);
      lineId++;
    }
    writer.writeComponentChangesets(scmBuilder.build());
    allFilesToBlame.remove(file);
    count++;
    progressReport.message(count + "/" + total + " files analyzed");
  }