Skip to content

Commit

Permalink
Add the ability to ignore lines from coverage depending on the comment (
Browse files Browse the repository at this point in the history
#302)

* Add the ability to ignore lines depending on comment

Use // coverage:ignore-line to ignore one line.
Use // coverage:ignore-start and // coverage:ignore-end to ignore range of lines inclusive.
Use // coverage:ignore-file to ignore the whole file.

* Add changelog entry, bump the version to 0.13.10

* remove unused import

* Upate test to have ignore line in a range ignore

* Update readme
  • Loading branch information
pasindud committed Jun 3, 2020
1 parent ed49782 commit 5142786
Show file tree
Hide file tree
Showing 11 changed files with 296 additions and 8 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
## 0.13.10 - 2020-06-03
* Add flag `--check-ignore` that is used to ignore lines from coverage
depending on the comments.

Use // coverage:ignore-line to ignore one line.
Use // coverage:ignore-start and // coverage:ignore-end to ignore range of lines inclusive.
Use // coverage:ignore-file to ignore the whole file.

## 0.13.9 - 2020-03-09

* Don't crash on empty JSON input files.
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,9 @@ format_coverage --packages=app_package/.packages -i coverage.json

where `app_package` is the path to the package whose coverage is being
collected. If `--sdk-root` is set, Dart SDK coverage will also be output.

#### Ignore lines from coverage

- `// coverage:ignore-line` to ignore one line.
- `// coverage:ignore-start` and `// coverage:ignore-end` to ignore range of lines inclusive.
- `// coverage:ignore-file` to ignore the whole file.
16 changes: 15 additions & 1 deletion bin/format_coverage.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class Environment {
bool prettyPrint;
bool lcov;
bool expectMarkers;
bool checkIgnore;
bool verbose;
}

Expand All @@ -39,10 +40,15 @@ Future<Null> main(List<String> arguments) async {
print(' package-root: ${env.pkgRoot}');
print(' package-spec: ${env.packagesPath}');
print(' report-on: ${env.reportOn}');
print(' check-ignore: ${env.checkIgnore}');
}

final clock = Stopwatch()..start();
final hitmap = await parseCoverage(files, env.workers);
final hitmap = await parseCoverage(
files,
env.workers,
checkIgnoredLines: env.checkIgnore,
);

// All workers are done. Process the data.
if (env.verbose) {
Expand Down Expand Up @@ -125,6 +131,13 @@ Environment parseArgs(List<String> arguments) {
help: 'convert coverage data to lcov format');
parser.addFlag('verbose',
abbr: 'v', negatable: false, help: 'verbose output');
parser.addFlag(
'check-ignore',
abbr: 'c',
negatable: false,
help: 'check for coverage ignore comments.'
' Not supported in web coverage.',
);
parser.addFlag('help', abbr: 'h', negatable: false, help: 'show this help');

final args = parser.parse(arguments);
Expand Down Expand Up @@ -214,6 +227,7 @@ Environment parseArgs(List<String> arguments) {
fail('Invalid worker count: $e');
}

env.checkIgnore = args['check-ignore'] as bool;
env.verbose = args['verbose'] as bool;
return env;
}
Expand Down
67 changes: 64 additions & 3 deletions lib/src/hitmap.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,21 @@ import 'dart:async';
import 'dart:convert' show json;
import 'dart:io';

import 'package:coverage/src/resolver.dart';
import 'package:coverage/src/util.dart';

/// Creates a single hitmap from a raw json object. Throws away all entries that
/// are not resolvable.
///
/// `jsonResult` is expected to be a List<Map<String, dynamic>>.
Map<String, Map<int, int>> createHitmap(List<Map<String, dynamic>> jsonResult) {
Future<Map<String, Map<int, int>>> createHitmap(

This comment has been minimized.

Copy link
@felangel

felangel Jun 3, 2020

This was a breaking change can we please revert this?

List<Map<String, dynamic>> jsonResult, {
bool checkIgnoredLines = false,
String packagesPath,
}) async {
final resolver = Resolver(packagesPath: packagesPath);
final loader = Loader();

// Map of source file to map of line to hit count for that line.
final globalHitMap = <String, Map<int, int>>{};

Expand All @@ -26,6 +36,24 @@ Map<String, Map<int, int>> createHitmap(List<Map<String, dynamic>> jsonResult) {
continue;
}

var ignoredLinesList = <List<int>>[];

if (checkIgnoredLines) {
final lines = await loader.load(resolver.resolve(source));
ignoredLinesList = getIgnoredLines(lines);

// Ignore the whole file.
if (ignoredLinesList.length == 1 &&
ignoredLinesList[0][0] == 0 &&
ignoredLinesList[0][1] == lines.length) {
continue;
}
}

// Move to the first ignore range.
final ignoredLines = ignoredLinesList.iterator;
ignoredLines.moveNext();

final sourceHitMap = globalHitMap.putIfAbsent(source, () => <int, int>{});
final hits = e['hits'] as List;
// hits is a flat array of the following format:
Expand All @@ -36,13 +64,17 @@ Map<String, Map<int, int>> createHitmap(List<Map<String, dynamic>> jsonResult) {
final k = hits[i];
if (k is int) {
// Single line.
if (_shouldIgnoreLine(ignoredLines, k)) continue;

addToMap(sourceHitMap, k, hits[i + 1] as int);
} else if (k is String) {
// Linerange. We expand line ranges to actual lines at this point.
final splitPos = k.indexOf('-');
final start = int.parse(k.substring(0, splitPos));
final end = int.parse(k.substring(splitPos + 1));
for (var j = start; j <= end; j++) {
if (_shouldIgnoreLine(ignoredLines, j)) continue;

addToMap(sourceHitMap, j, hits[i + 1] as int);
}
} else {
Expand All @@ -53,6 +85,29 @@ Map<String, Map<int, int>> createHitmap(List<Map<String, dynamic>> jsonResult) {
return globalHitMap;
}

bool _shouldIgnoreLine(Iterator<List<int>> ignoredRanges, int line) {
if (ignoredRanges.current == null || ignoredRanges.current.isEmpty) {
return false;
}

if (line < ignoredRanges.current[0]) return false;

while (ignoredRanges.current != null &&
ignoredRanges.current.isNotEmpty &&
ignoredRanges.current[1] < line) {
ignoredRanges.moveNext();
}

if (ignoredRanges.current != null &&
ignoredRanges.current.isNotEmpty &&
ignoredRanges.current[0] <= line &&
line <= ignoredRanges.current[1]) {
return true;
}

return false;
}

/// Merges [newMap] into [result].
void mergeHitmaps(
Map<String, Map<int, int>> newMap, Map<String, Map<int, int>> result) {
Expand All @@ -73,15 +128,21 @@ void mergeHitmaps(

/// Generates a merged hitmap from a set of coverage JSON files.
Future<Map<String, Map<int, int>>> parseCoverage(
Iterable<File> files, int _) async {
Iterable<File> files,
int _, {
bool checkIgnoredLines = false,
}) async {
final globalHitmap = <String, Map<int, int>>{};
for (var file in files) {
final contents = file.readAsStringSync();
final jsonMap = json.decode(contents) as Map<String, dynamic>;
if (jsonMap.containsKey('coverage')) {
final jsonResult = jsonMap['coverage'] as List;
mergeHitmaps(
createHitmap(jsonResult.cast<Map<String, dynamic>>()),
await createHitmap(
jsonResult.cast<Map<String, dynamic>>(),
checkIgnoredLines: checkIgnoredLines,
),
globalHitmap,
);
}
Expand Down
66 changes: 66 additions & 0 deletions lib/src/util.dart
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,69 @@ int _finish(int hash) {
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}

const muliLineIgnoreStart = '// coverage:ignore-start';
const muliLineIgnoreEnd = '// coverage:ignore-end';
const singleLineIgnore = '// coverage:ignore-line';
const ignoreFile = '// coverage:ignore-file';

/// Return list containing inclusive range of lines to be ignored by coverage.
/// If there is a error in balancing the statements it will ignore nothing,
/// unless `coverage:ignore-file` is found.
/// Return [0, lines.length] if the whole file is ignored.
///
/// ```
/// 1. final str = ''; // coverage:ignore-line
/// 2. final str = '';
/// 3. final str = ''; // coverage:ignore-start
/// 4. final str = '';
/// 5. final str = ''; // coverage:ignore-end
/// ```
///
/// Returns
/// ```
/// [
/// [1,1],
/// [3,5],
/// ]
/// ```
///
List<List<int>> getIgnoredLines(List<String> lines) {
final ignoredLines = <List<int>>[];
if (lines == null) return ignoredLines;

final allLines = [
[0, lines.length]
];

var isError = false;
var i = 0;
while (i < lines.length) {
if (lines[i].contains(ignoreFile)) return allLines;

if (lines[i].contains(muliLineIgnoreEnd)) isError = true;

if (lines[i].contains(singleLineIgnore)) ignoredLines.add([i + 1, i + 1]);

if (lines[i].contains(muliLineIgnoreStart)) {
final start = i;
++i;
while (i < lines.length) {
if (lines[i].contains(ignoreFile)) return allLines;
if (lines[i].contains(muliLineIgnoreStart)) {
isError = true;
break;
}

if (lines[i].contains(muliLineIgnoreEnd)) {
ignoredLines.add([start + 1, i + 1]);
break;
}
++i;
}
}
++i;
}

return isError ? [] : ignoredLines;
}
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: coverage
version: 0.13.9
version: 0.13.10
description: Coverage data manipulation and formatting
homepage: https://github.com/dart-lang/coverage

Expand Down
7 changes: 6 additions & 1 deletion test/collect_coverage_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ void main() {
final resultString = await _getCoverageResult();
final jsonResult = json.decode(resultString) as Map<String, dynamic>;
final coverage = jsonResult['coverage'] as List;
final hitMap = createHitmap(coverage.cast<Map<String, dynamic>>());
final hitMap = await createHitmap(
coverage.cast<Map<String, dynamic>>(),
checkIgnoredLines: true,
);
expect(hitMap, contains(_sampleAppFileUri));

final isolateFile = hitMap[_isolateLibFileUri];
Expand All @@ -70,6 +73,8 @@ void main() {
33: 1,
34: 3,
35: 1,
46: 1,
47: 1,
};
if (Platform.version.startsWith('1.')) {
// Dart VMs prior to 2.0.0-dev.5.0 contain a bug that emits coverage on the
Expand Down
7 changes: 6 additions & 1 deletion test/run_and_collect_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ void main() {
expect(sampleCoverageData['hits'], isNotEmpty);
}

final hitMap = createHitmap(coverage);
final hitMap = await createHitmap(
coverage,
checkIgnoredLines: true,
);
expect(hitMap, contains(_sampleAppFileUri));

final actualHits = hitMap[_isolateLibFileUri];
Expand All @@ -57,6 +60,8 @@ void main() {
33: 1,
34: 3,
35: 1,
46: 1,
47: 1,
};
// Dart VMs prior to 2.0.0-dev.5.0 contain a bug that emits coverage on the
// closing brace of async function blocks.
Expand Down
20 changes: 20 additions & 0 deletions test/test_files/test_app_isolate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,24 @@ void isolateTask(dynamic threeThings) {
final sum = (threeThings[1] + threeThings[2]) as int;
port.send(sum);
});

print('678'); // coverage:ignore-line

// coverage:ignore-start
print('1');
print('2');
print('3');
// coverage:ignore-end

print('4');
print('5');

print('6'); // coverage:ignore-start
print('7');
print('8');
// coverage:ignore-end
print('9'); // coverage:ignore-start
print('10');
print('11'); // coverage:ignore-line
// coverage:ignore-end
}
Loading

0 comments on commit 5142786

Please sign in to comment.