Skip to content

Commit 5b8a079

Browse files
committedJun 18, 2020
Initial commit.
0 parents  commit 5b8a079

19 files changed

+523
-0
lines changed
 

‎.gitignore

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Miscellaneous
2+
*.class
3+
*.log
4+
*.pyc
5+
*.swp
6+
.DS_Store
7+
.atom/
8+
.buildlog/
9+
.history
10+
.svn/
11+
12+
# IntelliJ related
13+
*.iml
14+
*.ipr
15+
*.iws
16+
.idea/
17+
18+
# The .vscode folder contains launch configuration and tasks you configure in
19+
# VS Code which you may wish to be included in version control, so this line
20+
# is commented out by default.
21+
#.vscode/
22+
23+
# Flutter/Dart/Pub related
24+
**/doc/api/
25+
.dart_tool/
26+
.flutter-plugins
27+
.flutter-plugins-dependencies
28+
.packages
29+
.pub-cache/
30+
.pub/
31+
build/
32+
33+
# Android related
34+
**/android/**/gradle-wrapper.jar
35+
**/android/.gradle
36+
**/android/captures/
37+
**/android/gradlew
38+
**/android/gradlew.bat
39+
**/android/local.properties
40+
**/android/**/GeneratedPluginRegistrant.java
41+
42+
# iOS/XCode related
43+
**/ios/**/*.mode1v3
44+
**/ios/**/*.mode2v3
45+
**/ios/**/*.moved-aside
46+
**/ios/**/*.pbxuser
47+
**/ios/**/*.perspectivev3
48+
**/ios/**/*sync/
49+
**/ios/**/.sconsign.dblite
50+
**/ios/**/.tags*
51+
**/ios/**/.vagrant/
52+
**/ios/**/DerivedData/
53+
**/ios/**/Icon?
54+
**/ios/**/Pods/
55+
**/ios/**/.symlinks/
56+
**/ios/**/profile
57+
**/ios/**/xcuserdata
58+
**/ios/.generated/
59+
**/ios/Flutter/App.framework
60+
**/ios/Flutter/Flutter.framework
61+
**/ios/Flutter/Flutter.podspec
62+
**/ios/Flutter/Generated.xcconfig
63+
**/ios/Flutter/app.flx
64+
**/ios/Flutter/app.zip
65+
**/ios/Flutter/flutter_assets/
66+
**/ios/Flutter/flutter_export_environment.sh
67+
**/ios/ServiceDefinitions.json
68+
**/ios/Runner/GeneratedPluginRegistrant.*
69+
70+
# Exceptions to above rules.
71+
!**/ios/**/default.mode1v3
72+
!**/ios/**/default.mode2v3
73+
!**/ios/**/default.pbxuser
74+
!**/ios/**/default.perspectivev3
75+
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages

‎.metadata

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# This file tracks properties of this Flutter project.
2+
# Used by Flutter tool to assess capabilities and perform upgrades etc.
3+
#
4+
# This file should be version controlled and should not be manually edited.
5+
6+
version:
7+
revision: b041144f833e05cf463b8887fa12efdec9493488
8+
channel: unknown
9+
10+
project_type: package

‎CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## [0.0.1] - TODO: Add release date.
2+
3+
* TODO: Describe initial release.

‎LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2020 James Leahy
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

‎README.md

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# dart_code_coverage
2+
3+
A package with helpers for generating and editing code coverage reports in dart.
4+
5+
Inspired by [remove_from_coverage](https://pub.dev/packages/remove_from_coverage) and [dart_coverage_helper](https://stackoverflow.com/questions/54602840/how-can-i-generate-test-coverage-of-untested-files-on-my-flutter-tests).
6+
7+
## Getting Started
8+
9+
### generate_dart_file
10+
11+
Generates a dart file of all project files which should be covered by unit tests.
12+
13+
| option | abbreviation | info |
14+
|--------|--------------|---------------------------------------------------------------------------|
15+
| output | -o | A path to save output file. Defaults to `test/coverage_report_test.dart`. |
16+
| remove | -r | A regexp pattern of paths to exclude. Optional. |
17+
| help | -h | Displays help. |
18+
19+
```sh
20+
flutter pub run dart_code_coverage:generate_dart_file -r ".*\.g\.dart","localizations.dart"
21+
```
22+
23+
### edit_lcov
24+
25+
Removes files with given patterns from lcov.info
26+
27+
| option | abbreviation | info |
28+
|--------|--------------|---------------------------------------------------------------|
29+
| file | -f | The lcov.info file to edit. Defaults to `coverage/lcov.info`. |
30+
| remove | -r | A regexp pattern of paths to exclude. Must be given. |
31+
| help | -h | Displays help. |
32+
33+
```sh
34+
flutter pub run dart_code_coverage:edit_lcov -r ".*\.g\.dart","localizations.dart"
35+
```
36+
37+
## Example
38+
39+
```sh
40+
# generate dart file of all files to be covered by unit tests
41+
flutter pub run dart_code_coverage:generate_dart_file -r ".*\.g\.dart","localizations.dart"
42+
43+
# generate lcov
44+
flutter test --coverage
45+
46+
# edit lcov
47+
flutter pub run dart_code_coverage:edit_lcov -r ".*\.g\.dart","localizations.dart"
48+
49+
# generate report
50+
genhtml coverage/lcov.info -o coverage/html
51+
```

‎bin/edit_lcov.dart

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import 'dart:io';
2+
3+
import 'package:args/args.dart';
4+
5+
import 'utils/reg_exp_utils.dart';
6+
7+
const _lcovPath = 'coverage/lcov.info';
8+
const _emptyString = '';
9+
10+
void main(List<String> arguments) async {
11+
File file;
12+
RegExp regExp;
13+
14+
// setup parser
15+
final parser = ArgParser()
16+
..addSeparator('Removes files with given patterns from lcov.info.')
17+
..addOption('file',
18+
abbr: 'f', help: 'The lcov.info file to edit. Defaults to coverage/lcov.info.', valueHelp: 'FILE')
19+
..addMultiOption('remove',
20+
abbr: 'r', splitCommas: true, help: 'A regexp pattern of paths to remove.', valueHelp: 'PATTERN')
21+
..addFlag('help', abbr: 'h', negatable: false, defaultsTo: false, help: 'Displays help.');
22+
final args = parser.parse(arguments);
23+
24+
// process arguments
25+
if (args['help']) {
26+
exit(0);
27+
} else {
28+
final lcovFilepath = args['file'] ?? _lcovPath;
29+
file = File(lcovFilepath);
30+
if (!file.existsSync()) {
31+
print('Error! No lcov file found at ${lcovFilepath}');
32+
exit(0);
33+
}
34+
35+
if ((args['remove'] as List<String>).isNotEmpty) {
36+
if (args['remove'].contains(_emptyString)) {
37+
print('Error! No spaces should be in pattern list: ${args['remove']}');
38+
exit(0);
39+
}
40+
regExp = RegExpUtils.combinePatterns(args['remove']);
41+
} else {
42+
print('Error! No regexp given. Nothing to remove.');
43+
exit(0);
44+
}
45+
}
46+
47+
// edit file
48+
await _editLcov(file, regExp);
49+
}
50+
51+
Future<void> _editLcov(File file, RegExp regExpFilesRemove) async {
52+
// determine file contents
53+
final contents = await file.readAsLines();
54+
55+
// split report entries into a list of lists
56+
final entries = <List<String>>[];
57+
while (contents.isNotEmpty) {
58+
final index = contents.indexWhere((element) => element.contains('end_of_record'));
59+
if (index != null) {
60+
final entry = contents.sublist(0, index + 1);
61+
contents.removeRange(0, index + 1);
62+
entries.add(entry);
63+
}
64+
}
65+
66+
// determine entries to remove
67+
final entriesToRemove = <List<String>>[];
68+
for (final entry in entries) {
69+
if (regExpFilesRemove.hasMatch(entry.first)) {
70+
entriesToRemove.add(entry);
71+
}
72+
}
73+
74+
if (entriesToRemove.isEmpty) {
75+
print('Error! No entries found to remove.');
76+
exit(0);
77+
}
78+
79+
// remove entries
80+
for (final entry in entriesToRemove) {
81+
entries.remove(entry);
82+
}
83+
84+
// determine new content and write to disk
85+
final newContents =
86+
entries.reduce((value, element) => [...value, ...element]).reduce((value, element) => value + '\n' + element);
87+
file.writeAsStringSync(newContents);
88+
}

‎bin/generate_dart_file.dart

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import 'dart:io';
2+
3+
import 'package:args/args.dart';
4+
5+
import 'utils/reg_exp_utils.dart';
6+
7+
const _outputPath = 'test/coverage_report_test.dart';
8+
9+
void main(List<String> arguments) async {
10+
String filepath;
11+
RegExp regExp;
12+
13+
// setup parser
14+
final parser = ArgParser()
15+
..addSeparator('Generates a dart file of all project files which should be covered by unit tests.')
16+
..addOption('output',
17+
abbr: 'o', help: 'A path to save output file. Defaults to test/coverage_report_test.dart.', valueHelp: 'FILE')
18+
..addMultiOption('remove',
19+
abbr: 'r', splitCommas: true, help: 'A regexp pattern of paths to exclude.', valueHelp: 'PATTERN')
20+
..addFlag('help', abbr: 'h', negatable: false, defaultsTo: false, help: 'Displays help.');
21+
final args = parser.parse(arguments);
22+
23+
// process arguments
24+
if (args['help']) {
25+
exit(0);
26+
} else {
27+
filepath = args['output'] ?? _outputPath;
28+
print(filepath);
29+
30+
if ((args['remove'] as List<String>).isNotEmpty) {
31+
regExp = RegExpUtils.combinePatterns(args['remove']);
32+
}
33+
}
34+
35+
// generate file
36+
await _createCoverageReportDartFile(filepath, regExp);
37+
}
38+
39+
const _pubspecPath = 'pubspec.yaml';
40+
41+
Future<void> _createCoverageReportDartFile(String filepath, RegExp regExpFilesIgnore) async {
42+
// dtermine contents of pubspec
43+
File file = File(_pubspecPath);
44+
if (!file.existsSync()) {
45+
print('Error! Pubspec not found. Please run from project root. Exiting.');
46+
exit(0);
47+
}
48+
final contents = file.readAsStringSync();
49+
50+
// determine project name
51+
final regExp = RegExp(r'(name: )(\w*)');
52+
final matches = regExp.allMatches(contents)?.first;
53+
final projectName = matches != null && matches.groupCount >= 2 ? matches.group(2) : null;
54+
if (projectName == null) {
55+
print('Error! Cannot determine project name. Exiting.');
56+
exit(0);
57+
}
58+
59+
// determine all paths for valid files
60+
final paths = await _listDir('lib', regExp: regExpFilesIgnore);
61+
62+
// determine output content
63+
final sb = StringBuffer();
64+
sb.write('// GENERATED CODE - DO NOT MODIFY BY HAND\n');
65+
sb.write('\n');
66+
sb.write('// **************************************************************************\n');
67+
sb.write('// All files which should be covered by tests\n');
68+
sb.write('// **************************************************************************\n');
69+
sb.write('\n');
70+
sb.write('// ignore_for_file: unused_import\n');
71+
sb.write('\n');
72+
for (final path in paths) {
73+
sb.write('import \'package:$projectName/${path.replaceAll('lib/', '')}\';\n');
74+
}
75+
76+
// write to disk
77+
file = File(filepath);
78+
if (!await file.exists()) {
79+
await file.create(recursive: true);
80+
}
81+
file.writeAsStringSync(sb.toString());
82+
}
83+
84+
/// Lists all files (recursively) in a given folder
85+
///
86+
/// [regExp] is an optional RegExp for files to ignore
87+
Future<List<String>> _listDir(String folderPath, {RegExp regExp}) async {
88+
final paths = <String>[];
89+
final directory = Directory(folderPath);
90+
if (await directory.exists()) {
91+
await for (FileSystemEntity entity in directory.list(recursive: true, followLinks: false)) {
92+
FileSystemEntityType type = await FileSystemEntity.type(entity.path);
93+
if (type == FileSystemEntityType.file) {
94+
if (regExp != null) {
95+
if (regExp.hasMatch(entity.path)) {
96+
continue;
97+
}
98+
}
99+
100+
paths.add(entity.path);
101+
}
102+
}
103+
return paths;
104+
}
105+
106+
return paths;
107+
}

‎bin/utils/reg_exp_utils.dart

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/// A class of utils for RegExp
2+
abstract class RegExpUtils {
3+
/// Combines a list of string patterns into a RegExp
4+
static RegExp combinePatterns(List<String> patterns) {
5+
if (patterns != null && patterns.length > 0) {
6+
String combinedPattern = '';
7+
for (int i = 0; i < patterns.length; i++) {
8+
if (patterns[i] == null || patterns[i].isEmpty) {
9+
continue;
10+
}
11+
12+
if (i != 0) {
13+
combinedPattern += "|";
14+
}
15+
combinedPattern += patterns[i];
16+
}
17+
18+
return RegExp(combinedPattern);
19+
}
20+
21+
return null;
22+
}
23+
}

‎example/README.md

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# example
2+
3+
## generate_dart_file
4+
5+
Generates a dart file of all project files which should be covered by unit tests.
6+
7+
| option | abbreviation | info |
8+
|--------|--------------|---------------------------------------------------------------------------|
9+
| output | -o | A path to save output file. Defaults to `test/coverage_report_test.dart`. |
10+
| remove | -r | A regexp pattern of paths to exclude. Optional. |
11+
| help | -h | Displays help. |
12+
13+
```sh
14+
flutter pub run dart_code_coverage:generate_dart_file -r ".*\.g\.dart","localizations.dart"
15+
```
16+
17+
## edit_lcov
18+
19+
Removes files with given patterns from lcov.info
20+
21+
| option | abbreviation | info |
22+
|--------|--------------|---------------------------------------------------------------|
23+
| file | -f | The lcov.info file to edit. Defaults to `coverage/lcov.info`. |
24+
| remove | -r | A regexp pattern of paths to exclude. Must be given. |
25+
| help | -h | Displays help. |
26+
27+
```sh
28+
flutter pub run dart_code_coverage:edit_lcov -r ".*\.g\.dart","localizations.dart"
29+
```

‎example/coverage/lcov.info

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
SF:lib/models/user.g.dart
2+
DA:34,0
3+
LF:1
4+
LH:0
5+
end_of_record
6+
SF:lib/services/my_service.dart
7+
DA:2,10
8+
DA:3,6
9+
DA:4,6
10+
DA:5,6
11+
DA:6,6
12+
DA:7,6
13+
DA:8,6
14+
LF:7
15+
LH:7
16+
end_of_record
17+
SF:lib/localizations.dart
18+
DA:27,3
19+
DA:45,3
20+
DA:46,3
21+
DA:47,16
22+
DA:51,3
23+
DA:52,3
24+
DA:53,3
25+
DA:54,3
26+
DA:55,3
27+
DA:56,3
28+
DA:57,12
29+
DA:58,3
30+
DA:60,3
31+
DA:61,3
32+
DA:62,6
33+
DA:63,9
34+
DA:64,9
35+
DA:65,9
36+
DA:66,9
37+
DA:71,0
38+
DA:72,0
39+
DA:73,0
40+
DA:74,0
41+
DA:75,0
42+
DA:76,0
43+
DA:77,0
44+
DA:78,0
45+
DA:79,0
46+
DA:80,0
47+
DA:81,0
48+
DA:82,0
49+
DA:83,0
50+
DA:84,0
51+
DA:85,0
52+
LF:34
53+
LH:19
54+
end_of_record

‎example/lib/localizations.dart

Whitespace-only changes.

‎example/lib/models/user.g.dart

Whitespace-only changes.

‎example/lib/my_file.dart

Whitespace-only changes.

‎example/lib/services/my_service.dart

Whitespace-only changes.

‎example/pubspec.lock

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Generated by pub
2+
# See https://dart.dev/tools/pub/glossary#lockfile
3+
packages:
4+
args:
5+
dependency: transitive
6+
description:
7+
name: args
8+
url: "https://pub.dartlang.org"
9+
source: hosted
10+
version: "1.6.0"
11+
dart_code_coverage:
12+
dependency: "direct dev"
13+
description:
14+
path: ".."
15+
relative: true
16+
source: path
17+
version: "0.0.1"
18+
sdks:
19+
dart: ">=2.7.0 <3.0.0"

‎example/pubspec.yaml

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name: example
2+
description: An example for dart_code_coverage
3+
version: 1.0.0
4+
5+
environment:
6+
sdk: ">=2.7.0 <3.0.0"
7+
8+
dev_dependencies:
9+
dart_code_coverage:
10+
path: ../

‎example/test/coverage_report_test.dart

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎pubspec.lock

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Generated by pub
2+
# See https://dart.dev/tools/pub/glossary#lockfile
3+
packages:
4+
args:
5+
dependency: "direct main"
6+
description:
7+
name: args
8+
url: "https://pub.dartlang.org"
9+
source: hosted
10+
version: "1.6.0"
11+
sdks:
12+
dart: ">=2.7.0 <3.0.0"

‎pubspec.yaml

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
name: dart_code_coverage
2+
description: A package with helpers for generating and editing code coverage reports in dart.
3+
version: 0.0.1
4+
publish_to: 'none'
5+
homepage: https://github.com/defuncart/dart_code_coverage
6+
7+
environment:
8+
sdk: ">=2.7.0 <3.0.0"
9+
10+
dependencies:
11+
args: ^1.6.0

0 commit comments

Comments
 (0)
Please sign in to comment.