public static @NotNull LcovCoverageReport readLCOV(@NotNull List<File> lcovFiles) throws IOException {
    LcovCoverageReport report = new LcovCoverageReport();
    for (File lcovFile : lcovFiles) {
      try (BufferedReader reader = new BufferedReader(new FileReader(lcovFile, StandardCharsets.UTF_8))) {
        String currentFileName = null;
        String currentFunction = null;
        List<LcovCoverageReport.LineHits> lineDataList = null;
        String line;
        while ((line = reader.readLine()) != null) {
          if (line.startsWith(SOURCE_FILE_PREFIX)) {
            currentFileName = line.substring(SOURCE_FILE_PREFIX.length());
            currentFunction = null;
            lineDataList = new ArrayList<>();
          }
          else if (line.startsWith(FUNCTION_PREFIX)) {
            Matcher fnMatcher = FN_PATTERN.matcher(line);
            if (fnMatcher.find()) {
              currentFunction = fnMatcher.group(2);
            }
          }
          else if (line.startsWith(LINE_HIT_PREFIX)) {
            if (lineDataList == null) {
              throw new RuntimeException("lineDataList is null!");
            }
            String[] values = line.substring(LINE_HIT_PREFIX.length()).split(",");
            int lineNum = Integer.parseInt(values[0]);
            int hitCount = Integer.parseInt(values[1]);
            LcovCoverageReport.LineHits lineHits = new LcovCoverageReport.LineHits(lineNum, hitCount, currentFunction);
            lineDataList.add(lineHits);
          }
          else if (END_OF_RECORD.equals(line)) {
            if (lineDataList == null) {
              throw new RuntimeException("lineDataList is null!");
            }
            report.mergeFileReport(normalizeFilePath(currentFileName), lineDataList);
            currentFileName = null;
            lineDataList = null;
          }
        }
      }
    }
    return report;
  }
public static @NotNull LcovCoverageReport readLCOV(@NotNull List<File> lcovFiles)