A CLI for optimizing test execution and manipulating coverage trace files. Optimize tests, validate coverage, transform trace files, and generate HTML reports.
- Installing
- Features
coverde.yamlconfiguration file- Usage with
melos - CI integration for coverage checks
You can make coverde globally available by executing the following command:
$ dart pub global activate coverdeNOTE: To run coverde directly from the terminal, add the system cache bin directory to your PATH environment variable.
- Optimize tests by gathering them.
- Check the coverage value (%) computed from a trace file.
- Filter a coverage trace file.
- Transform a coverage trace file.
- Generate the coverage report from a trace file.
- Remove a set of files and folders.
- Compute the coverage value (%) of an info file.
Optimize tests by gathering them.
Note
Why use coverde optimize-tests?
The optimize-tests command gathers all your Dart test files into a single "optimized" test entry point. This can lead to much faster test execution, especially in CI/CD pipelines or large test suites. By reducing the Dart VM spawn overhead and centralizing test discovery, it enables more efficient use of resources.
For more information, see the flutter/flutter#90225.
-
--includeThe glob pattern for the tests files to include.
Default value:test/**_test.dart -
--excludeThe glob pattern for the tests files to exclude.
-
--outputThe path to the optimized tests file.
Default value:test/optimized_test.dart
-
--flutter-goldensWhether to use golden tests in case of a Flutter package.
Default value: Enabled
Given the following test files:
test/user_test.dart:
import 'package:test/test.dart';
void main() {
test('user test', () {
// test implementation
});
}test/product_test.dart:
import 'package:test/test.dart';
void main() {
test('product test', () {
// test implementation
});
}Running:
$ coverde optimize-testsOutput: test/optimized_test.dart
// ignore_for_file: deprecated_member_use, type=lint
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:test_api/test_api.dart';
import 'product_test.dart' as _i1;
import 'user_test.dart' as _i2;
void main() {
group(
'product_test.dart',
() {
_i1.main();
},
);
group(
'user_test.dart',
() {
_i2.main();
},
);
}The command preserves test annotations from individual test files. Given the following test files with annotations:
test/slow_test.dart:
@Timeout(Duration(seconds: 45))
import 'package:test/test.dart';
void main() {
test('slow test', () {
// long-running test
});
}test/skipped_test.dart:
@Skip('Temporarily disabled')
import 'package:test/test.dart';
void main() {
test('skipped test', () {
// test implementation
});
}test/tagged_test.dart:
@Tags(['integration', 'slow'])
import 'package:test/test.dart';
void main() {
test('tagged test', () {
// test implementation
});
}test/vm_only_test.dart:
@TestOn('vm')
import 'package:test/test.dart';
void main() {
test('VM-only test', () {
// test implementation
});
}Running:
$ coverde optimize-tests --no-flutter-goldensOutput: test/optimized_test.dart
// ignore_for_file: deprecated_member_use, type=lint
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'package:test_api/test_api.dart';
import 'skipped_test.dart' as _i1;
import 'slow_test.dart' as _i2;
import 'tagged_test.dart' as _i3;
import 'vm_only_test.dart' as _i4;
void main() {
group(
'skipped_test.dart',
() {
_i1.main();
},
skip: 'Temporarily disabled',
);
group(
'slow_test.dart',
() {
_i2.main();
},
timeout: Timeout(Duration(seconds: 45)),
);
group(
'tagged_test.dart',
() {
_i3.main();
},
tags: ['integration', 'slow'],
);
group(
'vm_only_test.dart',
() {
_i4.main();
},
testOn: 'vm',
);
}The following annotations are supported and preserved:
@Skip()or@Skip('reason')→skip: trueorskip: 'reason'@Timeout(...)→timeout: Timeout(...)@Tags([...])→tags: [...]@TestOn('...')→testOn: '...'@OnPlatform({...})→onPlatform: {...}
To exclude certain test files using a glob pattern:
$ coverde optimize-tests --exclude "**/*_integration_test.dart"This will gather all test files matching the default include pattern (test/**_test.dart) except those matching the exclude pattern.
For non-Flutter packages or when golden tests are not needed:
$ coverde optimize-tests --no-flutter-goldensThis prevents the command from adding golden test setup code, which is only relevant for Flutter packages.
Check the coverage value (%) computed from a trace file.
The unique argument should be an integer between 0 and 100.
This parameter indicates the minimum value for the coverage to be accepted.
-
--inputTrace file used for the coverage check.
Default value:coverage/lcov.info -
--file-coverage-log-levelThe log level for the coverage value for each source file listed in the
inputinfo file.
Default value:line-content
Allowed values:none: Log nothing.overview: Log the overview of the coverage value for the file.line-numbers: Log only the uncovered line numbers.line-content: Log the uncovered line numbers and their content.
-
min-coverageThe minimum coverage value to be accepted. It should be an integer between 0 and 100.
Caution
The filter command will be removed in the next major update. Use coverde transform instead.
Filter a coverage trace file.
Filter the coverage info by ignoring data related to files with paths that matches the given FILTERS.
The coverage data is taken from the INPUT_LCOV_FILE file and the result is appended to the OUTPUT_LCOV_FILE file.
All the relative paths in the resulting coverage trace file will be resolved relative to the , if provided.
-
--inputOrigin coverage info file to pick coverage data from.
Default value:coverage/lcov.info -
--outputDestination coverage info file to dump the resulting coverage data into.
Default value:coverage/filtered.lcov.info -
--base-directoryBase directory relative to which trace file source paths are resolved.
-
--modeThe mode in which the OUTPUT_LCOV_FILE can be generated.
Default value:a
Allowed values:a: Append filtered content to the OUTPUT_LCOV_FILE content, if any.w: Override the OUTPUT_LCOV_FILE content, if any, with the filtered content.
-
--filtersSet of comma-separated path patterns of the files to be ignored.
Each pattern must be a valid regex expression. Invalid patterns will cause the command to fail.
Default value: None
Use coverde transform with equivalent options:
| Filter option | Transform equivalent |
|---|---|
--filters pattern1,pattern2 |
--transformations skip-by-regex=pattern1 --transformations skip-by-regex=pattern2 |
--base-directory B |
--transformations relative=B |
--input, --output, --mode |
Same options |
Example:
# Before (filter)
coverde filter --input coverage/lcov.info --output coverage/filtered.lcov.info \
--filters '\.g\.dart$' --base-directory /project --mode w
# After (transform)
coverde transform --input coverage/lcov.info --output coverage/filtered.lcov.info \
--transformations skip-by-regex='\.g\.dart$' --transformations relative=/project --mode wTransform a coverage trace file.
Apply a sequence of transformations to the coverage data.
The coverage data is taken from the INPUT_LCOV_FILE file and written to the OUTPUT_LCOV_FILE file.
Presets can be defined in coverde.yaml under the transformations key.
-
--inputOrigin coverage info file to transform.
Default value:coverage/lcov.info -
--outputDestination coverage info file to dump the transformed coverage data into.
Default value:coverage/transformed.lcov.info -
--modeThe mode in which the OUTPUT_LCOV_FILE can be generated.
Default value:a
Allowed values:a: Append transformed content to the OUTPUT_LCOV_FILE content, if any.w: Override the OUTPUT_LCOV_FILE content, if any, with the transformed content.
-
--transformationsTransformation steps to apply in order.
Default value: None
Allowed values:keep-by-regex=<regex>: Keep files that match the<regex>.skip-by-regex=<regex>: Skip files that match the<regex>.keep-by-glob=<glob>: Keep files that match the<glob>.skip-by-glob=<glob>: Skip files that match the<glob>.keep-by-coverage=<comparison>: Keep files that match the<comparison>(with reference values between 0 and 100).skip-by-coverage=<comparison>: Skip files that match the<comparison>(with reference values between 0 and 100).relative=<base-path>: Rewrite file paths to be relative to the<base-path>.preset=<name>: Expand a preset fromcoverde.yaml.
-
--explainPrint the resolved transformation list and exit without modifying files.
Default value: Disabled
$ coverde transform \
--transformations relative="/packages/my_package/" \
--transformations keep-by-glob="lib/**" \
--transformations skip-by-glob="**/*.g.dart" \
--transformations keep-by-coverage="lte|80"This transformation chain performs the following steps:
- Rewrite file paths to be relative to the
/packages/my_package/directory (useful for monorepos). - Keep files that match the
lib/**glob pattern, i.e. implementation files. - Skip files that match the
**/*.g.dartglob pattern, i.e. generated files. - Keep files that have a coverage value less than or equal to 80%.
Given the following coverde.yaml configuration:
# coverde.yaml
transformations:
implementation-without-generated:
- type: keep-by-glob
glob: "lib/**"
- type: skip-by-glob
glob: "**/*.g.dart"Running:
$ coverde transform \
--transformations relative="/packages/my_package/" \
--transformations preset=implementation-without-generated \
--transformations keep-by-coverage="lte|80"Is equivalent to the Inline Transformations example.
Generate the coverage report from a trace file.
Generate the coverage report inside REPORT_DIR from the TRACE_FILE trace file.
-
--inputCoverage trace file to be used for the coverage report generation.
Default value:coverage/lcov.info -
--outputDestination directory where the generated coverage report will be stored.
Default value:coverage/html/ -
--mediumMedium threshold.
Must be a number between 0 and 100, and must be less than the high threshold.
Default value:75 -
--highHigh threshold.
Must be a number between 0 and 100, and must be greater than the medium threshold.
Default value:90
-
--launchLaunch the generated report in the default browser. This option is only supported on desktop platforms. (defaults to off)
Default value: Disabled
Remove a set of files and folders.
-
--dry-runPreview what would be deleted without actually deleting. When enabled (default), the command will list what would be deleted but not perform the deletion. When disabled, the command will actually delete the specified files and folders.
Default value: Enabled -
--accept-absenceAccept absence of a file or folder. When an element is not present:
- If enabled, the command will continue.
- If disabled, the command will fail.
Default value: Enabled
-
pathsSet of file and/or directory paths to be removed.
Compute the coverage value (%) of an info file.
Compute the coverage value of the LCOV_FILE info file.
-
--inputCoverage info file to be used for the coverage value computation.
Default value:coverage/lcov.info -
--file-coverage-log-levelThe log level for the coverage value for each source file listed in the LCOV_FILE info file.
Default value:line-content
Allowed values:none: Log nothing.overview: Log the overview of the coverage value for the file.line-numbers: Log only the uncovered line numbers.line-content: Log the uncovered line numbers and their content.
The coverde.yaml file allows you to define reusable transformation presets for the coverde transform command.
The file is optional and is read from the current working directory when you run coverde transform (typically your project root).
The configuration file uses YAML format with a transformations key at the root level. Each preset is defined as a named list of transformation steps.
# coverde.yaml
transformations:
preset-name:
- type: <transformation-type>
<parameter-1-name>: <value-1>
<parameter-2-name>: <value-2>
- type: <transformation-type>
<parameter-1-name>: <value-1>Each transformation step requires a type field and the corresponding parameters.
Keep files whose paths match the provided regular expression pattern.
type: keep-by-regex
regex: <regex-pattern> # Example: "^lib/.*\\.dart$"Skip (exclude) files whose paths match the provided regular expression pattern.
type: skip-by-regex
regex: <regex-pattern> # Example: "^test/.*_integration\\.dart$"Keep files whose paths match the provided glob pattern.
type: keep-by-glob
glob: <glob-pattern> # Example: "**/lib/**"Skip (exclude) files whose paths match the provided glob pattern.
type: skip-by-glob
glob: <glob-pattern> # Example: "**/*.g.dart"Keep files whose coverage meets the specified comparison.
type: keep-by-coverage
comparison: <comparison> # Example: "lte|80" (less than or equal to 80%)Skip (exclude) files whose coverage meets the specified comparison.
type: skip-by-coverage
comparison: <comparison> # Example: "gt|50" (greater than 50%)See Comparison Operators for the allowed comparison operators.
Rewrite file paths so that they are relative to the given base path.
type: relative
base-path: <base-path> # Example: "/path/to/project"Include transformations defined in another preset by name.
Caution
Circular references between presets are detected and will result in an error.
type: preset
name: <other-preset-name> # Example: "production-only"eq|<value>
Checks if the value is equal to <value>.
neq|<value>
Checks if the value is not equal to <value>.
gt|<value>
Checks if the value is greater than <value>.
gte|<value>
Checks if the value is greater than or equal to <value>.
lt|<value>
Checks if the value is less than <value>.
lte|<value>
Checks if the value is less than or equal to <value>.
in|<range>
Checks if the value is within the specified <range>.
The <range> can be one of the following:
[lowerValue,upperValue](lowerValue,upperValue][lowerValue,upperValue)(lowerValue,upperValue)
The [ and ] brackets indicate that the lower and upper bounds are inclusive, while the ( and ) parentheses indicate that the lower and upper bounds are exclusive.
Both lower and upper bounds should be between 0 and 100 inclusive, as they are coverage percentages.
# coverde.yaml
transformations:
# Exclude generated code files
exclude-generated:
- type: skip-by-glob
glob: "**/*.g.dart"
- type: skip-by-glob
glob: "**/*.freezed.dart"
- type: skip-by-glob
glob: "**/*.gen.dart"
# Keep only production code (lib folder), excluding generated files
production-only:
- type: keep-by-glob
glob: "**/lib/**"
- type: preset
name: exclude-generated
# Filter to files with high coverage
high-coverage-only:
- type: keep-by-coverage
comparison: "gte|80"
# Common CI workflow preset
ci-workflow:
- type: preset
name: production-only
- type: relative
base-path: "/path/to/project"Once defined, presets can be used with the coverde transform command.
# Use a single preset
$ coverde transform --transformations preset=exclude-generated
# Combine presets with inline transformations
$ coverde transform \
--transformations preset=production-only \
--transformations skip-by-coverage="gt|50"
# Preview transformations without applying them
$ coverde transform --transformations preset=ci-workflow --explainIf coverde is installed via dart pub global activate --source=hosted, i.e. as a global package from the Pub.dev, it can prompt the user to update the package if a new compatible version is available.
This process verifies that the new release has a higher SemVer version than the current one, and that its environment constraints are met by the current Dart SDK.
--update-check
The update check mode to use.
Default value: enabled
Allowed values:
disabled: Disable the update check.enabled: Enable the update check with silent output, only prompting the user if an update is available, and ignoring any warnings and errors.enabled-verbose: Enable the update check with verbose output.
Usage with melos
If your project uses melos to manage its multi-package structure, coverde can help optimize test execution and collect test coverage data in a unified trace file.
Here are some examples of how to use coverde with melos to manage your monorepo. Adapt them to match your project structure and needs.
Optimize test execution by gathering all test files into a single optimized test file before running tests:
test:
description: Run tests and generate coverage trace file for a package
run: >
dart run coverde optimize-tests
--output=test/optimized_test.dart
&&
dart test
--coverage-path=coverage/lcov.info
--test-randomize-ordering-seed random
test/optimized_test.dart
packageFilters:
dependsOn:
- test
dirExists:
- testThis script:
- Optimizes tests by gathering them into a single file
- Runs the optimized test file with coverage collection, directly outputting an LCOV trace file
Merge coverage trace files from all packages into a unified trace file:
coverage.merge:
description: Merge all packages coverage trace files
run: >
dart run coverde rm
--no-dry-run
MELOS_ROOT_PATH/coverage/filtered.lcov.info
&&
melos exec
--file-exists coverage/lcov.info
--
"
dart run coverde transform
--input coverage/lcov.info
--output MELOS_ROOT_PATH/coverage/filtered.lcov.info
--transformations relative=MELOS_ROOT_PATH
--transformations skip-by-glob='**/*.g.dart'
"This script:
- Removes any existing merged coverage file
- Executes
coverde transformfor each package that contains acoverage/lcov.infofile:- Rewrites file paths to be relative to the monorepo root (using Melos’s
MELOS_ROOT_PATHenvironment variable) - Skips files that match the
**/*.g.dartglob pattern, i.e. generated files.
- Rewrites file paths to be relative to the monorepo root (using Melos’s
The resulting merged trace file can be used with coverde report to generate a unified HTML coverage report for the entire monorepo, or with coverde check to validate the coverage threshold for the overall project.
Validate minimum coverage threshold across all packages:
coverage.check:
description: Check test coverage
run: >
dart run coverde check
--input MELOS_ROOT_PATH/coverage/filtered.lcov.info
100This script checks that the merged coverage trace file meets the minimum coverage threshold (100% in this example). The command will fail if coverage is below the threshold, making it suitable for CI/CD pipelines.
If your project uses GitHub Actions for CI, you might already know very_good_coverage, which offers a simple but effective method for coverage validation.
However, adding coverage checks to CI workflows hosted by other alternatives is not always that straightforward.
To solve this, after enabling Dart or Flutter in your CI workflow, according to your project requirements, you can use coverde and its check tool by adding the following commands to your workflow steps:
dart pub global activate coverdecoverde check <min_coverage>
If you encounter any problems or you believe the CLI is missing a feature, feel free to open an issue on GitHub.
Pull requests are also welcome. See CONTRIBUTING.md.







