public Data compute(Component file, FileSourceDataWarnings fileSourceDataWarnings) {
    try (CloseableIterator<String> linesIterator = sourceLinesRepository.readLines(file);
      SourceLineReadersFactory.LineReaders lineReaders = sourceLineReadersFactory.getLineReaders(file)) {
      SourceLinesHashRepositoryImpl.LineHashesComputer lineHashesComputer = sourceLinesHash.getLineHashesComputerToPersist(file);
      DbFileSources.Data.Builder fileSourceBuilder = DbFileSources.Data.newBuilder();
      int currentLine = 0;

      while (linesIterator.hasNext()) {
        currentLine++;
        String lineSource = linesIterator.next();
        boolean hasNextLine = linesIterator.hasNext();

        sourceHashComputer.addLine(lineSource, hasNextLine);
        lineHashesComputer.addLine(lineSource);

        DbFileSources.Line.Builder lineBuilder = fileSourceBuilder
          .addLinesBuilder()
          .setSource(lineSource)
          .setLine(currentLine);
        lineReaders.read(lineBuilder, readError -> fileSourceDataWarnings.addWarning(file, readError));
      }

      Changeset latestChangeWithRevision = lineReaders.getLatestChangeWithRevision();
      return new Data(fileSourceBuilder.build(), lineHashesComputer.getResult(), sourceHashComputer.getHash(), latestChangeWithRevision);
    }
  }
@Test
  public void compute_calls_read_for_each_line_and_passe_read_error_to_fileSourceDataWarnings() {
    int lineCount = 1 + new Random().nextInt(10);
    List<String> lines = IntStream.range(0, lineCount).mapToObj(i -> "line" + i).collect(toList());
    when(sourceLinesRepository.readLines(FILE)).thenReturn(CloseableIterator.from(lines.iterator()));
    when(sourceLineReadersFactory.getLineReaders(FILE)).thenReturn(lineReaders);
    when(sourceLinesHashRepository.getLineHashesComputerToPersist(FILE)).thenReturn(lineHashesComputer);
    // mock an implementation that will call the ReadErrorConsumer in order to verify that the provided consumer is
    // doing what we expect: pass readError to fileSourceDataWarnings
    int randomStartPoint = new Random().nextInt(500);
    doAnswer(new Answer() {
      int i = randomStartPoint;

      @Override
      public Object answer(InvocationOnMock invocation) {
        Consumer<LineReader.ReadError> readErrorConsumer = invocation.getArgument(1);
        readErrorConsumer.accept(new LineReader.ReadError(LineReader.Data.SYMBOLS, i++));
        return null;
      }
    }).when(lineReaders).read(any(), any());

    underTest.compute(FILE, fileSourceDataWarnings);

    ArgumentCaptor<DbFileSources.Line.Builder> lineBuilderCaptor = ArgumentCaptor.forClass(DbFileSources.Line.Builder.class);
    verify(lineReaders, times(lineCount)).read(lineBuilderCaptor.capture(), any());
    assertThat(lineBuilderCaptor.getAllValues())
      .extracting(DbFileSources.Line.Builder::getSource)
      .containsOnlyElementsOf(lines);
    assertThat(lineBuilderCaptor.getAllValues())
      .extracting(DbFileSources.Line.Builder::getLine)
      .containsExactly(IntStream.range(1, lineCount + 1).boxed().toArray(Integer[]::new));
    ArgumentCaptor<LineReader.ReadError> readErrorCaptor = ArgumentCaptor.forClass(LineReader.ReadError.class);
    verify(fileSourceDataWarnings, times(lineCount)).addWarning(same(FILE), readErrorCaptor.capture());
    assertThat(readErrorCaptor.getAllValues())
      .extracting(LineReader.ReadError::line)
      .containsExactly(IntStream.range(randomStartPoint, randomStartPoint + lineCount).boxed().toArray(Integer[]::new));
  }