@Nullable
  protected DirCoverageInfo getDirCoverageInfo(
      @NotNull final PsiDirectory directory, @NotNull final CoverageSuitesBundle currentSuite) {
    final VirtualFile dir = directory.getVirtualFile();

    final boolean isInTestContent = TestSourcesFilter.isTestSources(dir, directory.getProject());
    if (!currentSuite.isTrackTestFolders() && isInTestContent) {
      return null;
    }

    final String path = normalizeFilePath(dir.getPath());

    return isInTestContent ? myTestDirCoverageInfos.get(path) : myDirCoverageInfos.get(path);
  }
  @Nullable
  protected DirCoverageInfo collectFolderCoverage(
      @NotNull final VirtualFile dir,
      final @NotNull CoverageDataManager dataManager,
      final Annotator annotator,
      final ProjectData projectInfo,
      boolean trackTestFolders,
      @NotNull final ProjectFileIndex index,
      @NotNull final CoverageEngine coverageEngine,
      Set<VirtualFile> visitedDirs,
      @NotNull final Map<String, String> normalizedFiles2Files) {
    if (!index.isInContent(dir)) {
      return null;
    }

    if (visitedDirs.contains(dir)) {
      return null;
    }

    if (!shouldCollectCoverageInsideLibraryDirs()) {
      if (index.isInLibrarySource(dir) || index.isInLibraryClasses(dir)) {
        return null;
      }
    }
    visitedDirs.add(dir);

    final boolean isInTestSrcContent = TestSourcesFilter.isTestSources(dir, getProject());

    // Don't count coverage for tests folders if track test folders is switched off
    if (!trackTestFolders && isInTestSrcContent) {
      return null;
    }

    final VirtualFile[] children = dataManager.doInReadActionIfProjectOpen(dir::getChildren);
    if (children == null) {
      return null;
    }

    final DirCoverageInfo dirCoverageInfo = new DirCoverageInfo();

    for (VirtualFile fileOrDir : children) {
      if (fileOrDir.isDirectory()) {
        final DirCoverageInfo childCoverageInfo =
            collectFolderCoverage(
                fileOrDir,
                dataManager,
                annotator,
                projectInfo,
                trackTestFolders,
                index,
                coverageEngine,
                visitedDirs,
                normalizedFiles2Files);

        if (childCoverageInfo != null) {
          dirCoverageInfo.totalFilesCount += childCoverageInfo.totalFilesCount;
          dirCoverageInfo.coveredFilesCount += childCoverageInfo.coveredFilesCount;
          dirCoverageInfo.totalLineCount += childCoverageInfo.totalLineCount;
          dirCoverageInfo.coveredLineCount += childCoverageInfo.coveredLineCount;
        }
      } else if (coverageEngine.coverageProjectViewStatisticsApplicableTo(fileOrDir)) {
        // let's count statistics only for ruby-based files

        final FileCoverageInfo fileInfo =
            collectBaseFileCoverage(fileOrDir, annotator, projectInfo, normalizedFiles2Files);

        if (fileInfo != null) {
          dirCoverageInfo.totalLineCount += fileInfo.totalLineCount;
          dirCoverageInfo.totalFilesCount++;

          if (fileInfo.coveredLineCount > 0) {
            dirCoverageInfo.coveredFilesCount++;
            dirCoverageInfo.coveredLineCount += fileInfo.coveredLineCount;
          }
        }
      }
    }

    // TODO - toplevelFilesCoverage - is unused variable!

    // no sense to include directories without ruby files
    if (dirCoverageInfo.totalFilesCount == 0) {
      return null;
    }

    final String dirPath = normalizeFilePath(dir.getPath());
    if (isInTestSrcContent) {
      annotator.annotateTestDirectory(dirPath, dirCoverageInfo);
    } else {
      annotator.annotateSourceDirectory(dirPath, dirCoverageInfo);
    }

    return dirCoverageInfo;
  }