tools icon indicating copy to clipboard operation
tools copied to clipboard

Added a tool to find uncovered files (#529)

Open Victowolf opened this issue 7 months ago • 1 comments

  • Thanks for your contribution! Please replace this text with a description of what this PR is changing or adding and why, list any relevant issues, and review the contribution guidelines below.

  • [x] I’ve reviewed the contributor guide and applied the relevant portions to this PR.
Contribution guidelines:

Note that many Dart repos have a weekly cadence for reviewing PRs - please allow for some latency before initial review feedback.

Overview

This PR adds support for a new --include-uncovered flag in format_coverage.dart, allowing uncovered Dart files (i.e., those with no test coverage) to be included in the LCOV output. Solving the issue (#529)

This is what I did:

  • CLI: Modified format_coverage.dart to parse --include-uncovered flag.
  • Core: Modified formatter.dart to pass the logic and format the LCOV to include files that are not covered by coverage.

Key changes in formatter.dart:

  1. Added two new imports: import 'dart:io'; import 'package:yaml/yaml.dart'; required to scan all files in the directory and compare it with the covered files.
  2. Added new parameter to formatLcov and prettyprint : bool Function(String path)? includeUncovered,
  3. Created three new functions: _findAllDartFiles: to scan all files in the directory. getPackageName: to get package. toPackageUri: to convert the file paths into package uri for checking if the file is included or not.
  4. Added the logic to scan the files , check whether they are covered if no then format the lcov, and wraped this logic within an if statement:
  if (includeUncovered != null) {
    // Step 1: Identify all Dart files
    final allFiles = _findAllDartFiles(reportOn: reportOn);
    print('detected files: $allFiles');

    // Step 2: Identify covered files
    final coveredFiles = Map.fromEntries(entries
        .where((entry) => entry.value.lineHits.values.any((hit) => hit > 0)));

    // check if the file is covered or no.
    final packageName = getPackageName();

    final uncoveredFiles = <String>[];
    for (final file in allFiles) {
      final pkgUri = toPackageUri(file, packageName);
      if (!coveredFiles.containsKey(pkgUri)) {
        uncoveredFiles.add(file);
      }
    }

    print('Uncovered Dart files:');
    for (final file in uncoveredFiles) {
      print(file);
    }

    //formatlcov for including uncovered.
    final uncoveredBuf = StringBuffer();

    for (final file in uncoveredFiles) {
      if (!pathFilter(file)) continue;

      final lines = File(file).readAsLinesSync();
      var displayPath = file;
      if (basePath != null) {
        displayPath = p.relative(file, from: basePath);
      }
      displayPath = displayPath.replaceAll('\\', '/'); // For Windows compatibility

      uncoveredBuf.writeln('SF:$displayPath');

      var lineNumber = 1;
      var realLines = 0;
      for (final line in lines) {
        final trimmed = line.trim();
        if (trimmed.isNotEmpty && !trimmed.startsWith('//')) {
          uncoveredBuf.writeln('DA:$lineNumber,0');
          realLines++;
        }
        lineNumber++;
      }

      uncoveredBuf.writeln('LF:$realLines');
      uncoveredBuf.writeln('LH:0');
      uncoveredBuf.writeln('end_of_record');
    }

    buf.write(uncoveredBuf.toString());
  }

Testing:

CLI cmd: dart run coverage:format_coverage --lcov --in=coverage --out=[lcov.info](http://lcov.info/) --report-on=lib --include-uncovered

example:

project-root/
├── lib/
│ ├── main.dart
│ └── utils.dart
└── test/
└── main_test.dart

Before implementation:

SF:C:\flutter_dev\projects\geminitest\lib\main.dart
DA:6,1
DA:8,2
DA:10,3
DA:12,1
DA:13,1
DA:14,0
DA:16,2
DA:17,4
DA:20,1
DA:21,1
DA:22,0
DA:24,2
DA:25,0
DA:27,2
DA:28,4
DA:31,1
DA:32,1
DA:33,0
DA:35,2
DA:38,1
DA:39,1
DA:40,5
DA:44,1
DA:45,3
DA:46,4
DA:47,1
DA:48,2
DA:49,2
LF:28
LH:24
end_of_record

as utils.dart didn't had any coresponding tests, it was not included in the LCOV.

After implementation:

SF:C:\flutter_dev\projects\geminitest\lib\main.dart
DA:6,1
DA:8,2
DA:10,3
DA:12,1
DA:13,1
DA:14,0
DA:16,2
DA:17,4
DA:20,1
DA:21,1
DA:22,0
DA:24,2
DA:25,0
DA:27,2
DA:28,4
DA:31,1
DA:32,1
DA:33,0
DA:35,2
DA:38,1
DA:39,1
DA:40,5
DA:44,1
DA:45,3
DA:46,4
DA:47,1
DA:48,2
DA:49,2
LF:28
LH:24
end_of_record
SF:lib/utils.dart
DA:5,0
DA:8,0
DA:10,0
DA:13,0
DA:16,0
DA:18,0
DA:21,0
DA:24,0
DA:26,0
DA:27,0
DA:28,0
DA:30,0
DA:31,0
DA:32,0
DA:35,0
DA:36,0
DA:37,0
DA:38,0
DA:39,0
DA:40,0
DA:41,0
DA:43,0
DA:44,0
DA:45,0
DA:46,0
DA:48,0
DA:50,0
DA:51,0
DA:53,0
DA:54,0
DA:55,0
DA:56,0
DA:57,0
DA:59,0
DA:60,0
DA:61,0
DA:62,0
DA:64,0
DA:68,0
DA:69,0
DA:74,0
DA:75,0
DA:76,0
DA:77,0
DA:81,0
DA:82,0
DA:83,0
DA:84,0
DA:85,0
LF:49
LH:0
end_of_record

utils.dart was included in the LCOV with 0 lines hit.


Conclusion:

--include-uncovered is successfully working as expected.

Feedback:

Please let me know if any changes or refinements are needed. Thank you!

Victowolf avatar May 06 '25 07:05 Victowolf

This needs a test. Also, there's a merge conflict in format_coverage.dart that will need to be resolved before the github CI can be run

Ohh yes.. I will resolve it..

Victowolf avatar May 08 '25 04:05 Victowolf

Closing for now due to inactivity - feel free to reopen and revive! :)

mosuem avatar Oct 07 '25 12:10 mosuem