diff --git a/pkgs/coverage/.gitignore b/pkgs/coverage/.gitignore index 1a2509741..ca048c5ba 100644 --- a/pkgs/coverage/.gitignore +++ b/pkgs/coverage/.gitignore @@ -15,4 +15,4 @@ build # Temp files *~ coverage/ -var/ +var/ \ No newline at end of file diff --git a/pkgs/coverage/CHANGELOG.md b/pkgs/coverage/CHANGELOG.md index 685c1c0a3..86e248e7d 100644 --- a/pkgs/coverage/CHANGELOG.md +++ b/pkgs/coverage/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.12.0-wip + +- Introduced support for specifying coverage flags through a YAML file. + ## 1.11.1 - Update `package:vm_service` constraints to '>=12.0.0 <16.0.0'. diff --git a/pkgs/coverage/bin/collect_coverage.dart b/pkgs/coverage/bin/collect_coverage.dart index b02ac2110..5b228c36b 100644 --- a/pkgs/coverage/bin/collect_coverage.dart +++ b/pkgs/coverage/bin/collect_coverage.dart @@ -8,7 +8,10 @@ import 'dart:io'; import 'package:args/args.dart'; import 'package:coverage/src/collect.dart'; +import 'package:coverage/src/coverage_options.dart'; import 'package:logging/logging.dart'; +import 'package:meta/meta.dart'; +import 'package:path/path.dart' as p; import 'package:stack_trace/stack_trace.dart'; Future main(List arguments) async { @@ -17,15 +20,19 @@ Future main(List arguments) async { print('${rec.level.name}: ${rec.time}: ${rec.message}'); }); - final options = _parseArgs(arguments); + final defaultOptions = CoverageOptionsProvider().coverageOptions; + final options = parseArgs(arguments, defaultOptions); + + final out = options.out == null ? stdout : File(options.out!).openWrite(); + await Chain.capture(() async { final coverage = await collect(options.serviceUri, options.resume, options.waitPaused, options.includeDart, options.scopedOutput, timeout: options.timeout, functionCoverage: options.functionCoverage, branchCoverage: options.branchCoverage); - options.out.write(json.encode(coverage)); - await options.out.close(); + out.write(json.encode(coverage)); + await out.close(); }, onError: (dynamic error, Chain chain) { stderr.writeln(error); stderr.writeln(chain.terse); @@ -48,7 +55,7 @@ class Options { this.scopedOutput); final Uri serviceUri; - final IOSink out; + final String? out; final Duration? timeout; final bool waitPaused; final bool resume; @@ -58,7 +65,8 @@ class Options { final Set scopedOutput; } -Options _parseArgs(List arguments) { +@visibleForTesting +Options parseArgs(List arguments, CoverageOptions defaultOptions) { final parser = ArgParser() ..addOption('host', abbr: 'H', @@ -69,11 +77,11 @@ Options _parseArgs(List arguments) { help: 'remote VM port. DEPRECATED: use --uri', defaultsTo: '8181') ..addOption('uri', abbr: 'u', help: 'VM observatory service URI') - ..addOption('out', - abbr: 'o', defaultsTo: 'stdout', help: 'output: may be file or stdout') + ..addOption('out', abbr: 'o', help: 'output: may be file or stdout') ..addOption('connect-timeout', abbr: 't', help: 'connect timeout in seconds') ..addMultiOption('scope-output', + defaultsTo: defaultOptions.scopeOutput, help: 'restrict coverage results so that only scripts that start with ' 'the provided package path are considered') ..addFlag('wait-paused', @@ -85,10 +93,12 @@ Options _parseArgs(List arguments) { ..addFlag('include-dart', abbr: 'd', defaultsTo: false, help: 'include "dart:" libraries') ..addFlag('function-coverage', - abbr: 'f', defaultsTo: false, help: 'Collect function coverage info') + abbr: 'f', + defaultsTo: defaultOptions.functionCoverage, + help: 'Collect function coverage info') ..addFlag('branch-coverage', abbr: 'b', - defaultsTo: false, + defaultsTo: defaultOptions.branchCoverage, help: 'Collect branch coverage info (Dart VM must also be run with ' '--branch-coverage for this to work)') ..addFlag('help', abbr: 'h', negatable: false, help: 'show this help'); @@ -125,13 +135,24 @@ Options _parseArgs(List arguments) { } final scopedOutput = args['scope-output'] as List; - IOSink out; - if (args['out'] == 'stdout') { - out = stdout; + String? out; + final outPath = args['out'] as String?; + if (outPath == 'stdout' || + (outPath == null && defaultOptions.outputDirectory == null)) { + out = null; } else { - final outfile = File(args['out'] as String)..createSync(recursive: true); - out = outfile.openWrite(); + final outFilePath = p.normalize(outPath ?? + p.absolute(defaultOptions.outputDirectory!, 'coverage.json')); + + final outFile = File(outFilePath); + if (!FileSystemEntity.isDirectorySync(outFilePath) && + !FileSystemEntity.isFileSync(outFilePath)) { + outFile.createSync(recursive: true); + } + + out = outFile.path; } + final timeout = (args['connect-timeout'] == null) ? null : Duration(seconds: int.parse(args['connect-timeout'] as String)); diff --git a/pkgs/coverage/bin/format_coverage.dart b/pkgs/coverage/bin/format_coverage.dart index d24f60d9c..c2d8283ab 100644 --- a/pkgs/coverage/bin/format_coverage.dart +++ b/pkgs/coverage/bin/format_coverage.dart @@ -6,6 +6,7 @@ import 'dart:io'; import 'package:args/args.dart'; import 'package:coverage/coverage.dart'; +import 'package:coverage/src/coverage_options.dart'; import 'package:glob/glob.dart'; import 'package:path/path.dart' as p; @@ -37,7 +38,7 @@ class Environment { bool checkIgnore; String input; bool lcov; - IOSink output; + String? output; String? packagesPath; String packagePath; bool prettyPrint; @@ -51,7 +52,8 @@ class Environment { } Future main(List arguments) async { - final env = parseArgs(arguments); + final defaultOptions = CoverageOptionsProvider().coverageOptions; + final env = parseArgs(arguments, defaultOptions); final files = filesToProcess(env.input); if (env.verbose) { @@ -104,8 +106,11 @@ Future main(List arguments) async { basePath: env.baseDirectory); } - env.output.write(output); - await env.output.flush(); + final outputSink = + env.output == null ? stdout : File(env.output!).openWrite(); + + outputSink.write(output); + await outputSink.flush(); if (env.verbose) { print('Done flushing output. Took ${clock.elapsedMilliseconds} ms.'); } @@ -124,26 +129,43 @@ Future main(List arguments) async { } } } - await env.output.close(); + await outputSink.close(); } /// Checks the validity of the provided arguments. Does not initialize actual /// processing. -Environment parseArgs(List arguments) { +Environment parseArgs(List arguments, CoverageOptions defaultOptions) { final parser = ArgParser(); parser - ..addOption('sdk-root', abbr: 's', help: 'path to the SDK root') - ..addOption('packages', help: '[DEPRECATED] path to the package spec file') + ..addOption( + 'sdk-root', + abbr: 's', + help: 'path to the SDK root', + ) + ..addOption( + 'packages', + help: '[DEPRECATED] path to the package spec file', + ) ..addOption('package', - help: 'root directory of the package', defaultsTo: '.') - ..addOption('in', abbr: 'i', help: 'input(s): may be file or directory') - ..addOption('out', - abbr: 'o', defaultsTo: 'stdout', help: 'output: may be file or stdout') - ..addMultiOption('report-on', - help: 'which directories or files to report coverage on') - ..addOption('workers', - abbr: 'j', defaultsTo: '1', help: 'number of workers') + help: 'root directory of the package', + defaultsTo: defaultOptions.packageDirectory) + ..addOption( + 'in', + abbr: 'i', + help: 'input(s): may be file or directory', + ) + ..addOption('out', abbr: 'o', help: 'output: may be file or stdout') + ..addMultiOption( + 'report-on', + help: 'which directories or files to report coverage on', + ) + ..addOption( + 'workers', + abbr: 'j', + defaultsTo: '1', + help: 'number of workers', + ) ..addOption('bazel-workspace', defaultsTo: '', help: 'Bazel workspace directory') ..addOption('base-directory', @@ -220,20 +242,31 @@ Environment parseArgs(List arguments) { fail('Package spec "${args["package"]}" not found, or not a directory.'); } - if (args['in'] == null) fail('No input files given.'); - final input = p.absolute(p.normalize(args['in'] as String)); + if (args['in'] == null && defaultOptions.outputDirectory == null) { + fail('No input files given.'); + } + final input = p.normalize((args['in'] as String?) ?? + p.absolute(defaultOptions.outputDirectory!, 'coverage.json')); if (!FileSystemEntity.isDirectorySync(input) && !FileSystemEntity.isFileSync(input)) { fail('Provided input "${args["in"]}" is neither a directory nor a file.'); } - IOSink output; - if (args['out'] == 'stdout') { - output = stdout; + String? output; + final outPath = args['out'] as String?; + if (outPath == 'stdout' || + (outPath == null && defaultOptions.outputDirectory == null)) { + output = null; } else { - final outpath = p.absolute(p.normalize(args['out'] as String)); - final outfile = File(outpath)..createSync(recursive: true); - output = outfile.openWrite(); + final outFilePath = p.normalize( + outPath ?? p.absolute(defaultOptions.outputDirectory!, 'lcov.info')); + + final outfile = File(outFilePath); + if (!FileSystemEntity.isDirectorySync(outFilePath) && + !FileSystemEntity.isFileSync(outFilePath)) { + outfile.createSync(recursive: true); + } + output = outfile.path; } final reportOnRaw = args['report-on'] as List; diff --git a/pkgs/coverage/bin/test_with_coverage.dart b/pkgs/coverage/bin/test_with_coverage.dart index 010e6089a..91e9b5f06 100644 --- a/pkgs/coverage/bin/test_with_coverage.dart +++ b/pkgs/coverage/bin/test_with_coverage.dart @@ -6,8 +6,10 @@ import 'dart:async'; import 'dart:io'; import 'package:args/args.dart'; +import 'package:coverage/src/coverage_options.dart'; import 'package:coverage/src/util.dart' show StandardOutExtension, extractVMServiceUri; +import 'package:meta/meta.dart'; import 'package:package_config/package_config.dart'; import 'package:path/path.dart' as path; @@ -50,37 +52,41 @@ void _watchExitSignal(ProcessSignal signal) { }); } -ArgParser _createArgParser() => ArgParser() +ArgParser _createArgParser(CoverageOptions defaultOptions) => ArgParser() ..addOption( 'package', help: 'Root directory of the package to test.', - defaultsTo: '.', + defaultsTo: defaultOptions.packageDirectory, ) ..addOption( 'package-name', help: 'Name of the package to test. ' 'Deduced from --package if not provided.', + defaultsTo: defaultOptions.packageName, ) ..addOption('port', help: 'VM service port.', defaultsTo: '8181') ..addOption( 'out', + defaultsTo: defaultOptions.outputDirectory, abbr: 'o', help: 'Output directory. Defaults to /coverage.', ) - ..addOption('test', help: 'Test script to run.', defaultsTo: 'test') + ..addOption('test', + help: 'Test script to run.', defaultsTo: defaultOptions.testScript) ..addFlag( 'function-coverage', abbr: 'f', - defaultsTo: false, + defaultsTo: defaultOptions.functionCoverage, help: 'Collect function coverage info.', ) ..addFlag( 'branch-coverage', abbr: 'b', - defaultsTo: false, + defaultsTo: defaultOptions.branchCoverage, help: 'Collect branch coverage info.', ) ..addMultiOption('scope-output', + defaultsTo: defaultOptions.scopeOutput, help: 'restrict coverage results so that only scripts that start with ' 'the provided package path are considered. Defaults to the name of ' 'the package under test.') @@ -110,8 +116,10 @@ class Flags { final List rest; } -Future _parseArgs(List arguments) async { - final parser = _createArgParser(); +@visibleForTesting +Future parseArgs( + List arguments, CoverageOptions defaultOptions) async { + final parser = _createArgParser(defaultOptions); final args = parser.parse(arguments); void printUsage() { @@ -138,7 +146,7 @@ ${parser.usage} exit(0); } - final packageDir = path.canonicalize(args['package'] as String); + final packageDir = path.normalize(path.absolute(args['package'] as String)); if (!FileSystemEntity.isDirectorySync(packageDir)) { fail('--package is not a valid directory.'); } @@ -166,7 +174,8 @@ ${parser.usage} } Future main(List arguments) async { - final flags = await _parseArgs(arguments); + final defaultOptions = CoverageOptionsProvider().coverageOptions; + final flags = await parseArgs(arguments, defaultOptions); final outJson = path.join(flags.outDir, 'coverage.json'); final outLcov = path.join(flags.outDir, 'lcov.info'); diff --git a/pkgs/coverage/lib/src/coverage_options.dart b/pkgs/coverage/lib/src/coverage_options.dart new file mode 100644 index 000000000..a15c31dc5 --- /dev/null +++ b/pkgs/coverage/lib/src/coverage_options.dart @@ -0,0 +1,125 @@ +import 'dart:io'; +import 'package:cli_config/cli_config.dart'; +import 'package:path/path.dart' as path; + +class CoverageOptions { + const CoverageOptions({ + this.outputDirectory, + required this.scopeOutput, + required this.functionCoverage, + required this.branchCoverage, + required this.packageDirectory, + this.packageName, + required this.testScript, + }); + + factory CoverageOptions.fromConfig( + Config options, CoverageOptions defaultOptions, String? optionsFilePath) { + var outputDirectory = options.optionalString('output_directory') ?? + defaultOptions.outputDirectory; + var packageDirectory = options.optionalString('package_directory') ?? + defaultOptions.packageDirectory; + + if (optionsFilePath != null) { + if (outputDirectory != null && !path.isAbsolute(outputDirectory)) { + outputDirectory = path.normalize( + path.absolute(path.dirname(optionsFilePath), outputDirectory)); + } + if (!path.isAbsolute(packageDirectory)) { + packageDirectory = path.normalize( + path.absolute(path.dirname(optionsFilePath), packageDirectory)); + } + } + + return CoverageOptions( + outputDirectory: outputDirectory, + scopeOutput: options.optionalStringList('scope_output') ?? + defaultOptions.scopeOutput, + functionCoverage: options.optionalBool('function_coverage') ?? + defaultOptions.functionCoverage, + branchCoverage: options.optionalBool('branch_coverage') ?? + defaultOptions.branchCoverage, + packageDirectory: packageDirectory, + packageName: + options.optionalString('package_name') ?? defaultOptions.packageName, + testScript: + options.optionalString('test_script') ?? defaultOptions.testScript, + ); + } + + final String? outputDirectory; + final List scopeOutput; + final bool functionCoverage; + final bool branchCoverage; + final String packageDirectory; + final String? packageName; + final String testScript; +} + +class CoverageOptionsProvider { + CoverageOptionsProvider({ + String? filePath, + }) { + final file = _getOptionsFile(filePath); + final fileContents = file?.readAsStringSync(); + + // Pass null to fileContents if the file is empty + final options = Config.fromConfigFileContents( + fileContents: fileContents, + fileSourceUri: file?.uri, + ); + + coverageOptions = + CoverageOptions.fromConfig(options, defaultOptions, optionsFilePath); + } + + late final CoverageOptions coverageOptions; + late final String? optionsFilePath; + static const defaultFilePath = 'coverage_options.yaml'; + + File? _getOptionsFile(String? filePath) { + filePath ??= findOptionsFilePath(); + + optionsFilePath = + filePath != null ? path.normalize(path.absolute(filePath)) : null; + + if (optionsFilePath == null) { + return null; + } + + final file = File(optionsFilePath!); + return file.existsSync() ? file : null; + } + + static String? findOptionsFilePath({Directory? directory}) { + var currentDir = directory ?? Directory.current; + + while (true) { + final pubSpecFilePath = path.join(currentDir.path, 'pubspec.yaml'); + if (File(pubSpecFilePath).existsSync()) { + final optionsFilePath = path.join(currentDir.path, defaultFilePath); + + if (File(optionsFilePath).existsSync()) { + return optionsFilePath; + } else { + return null; + } + } + final parentDir = currentDir.parent; + if (parentDir.path == currentDir.path) { + return null; + } + currentDir = parentDir; + } + } + + static const defaultOptions = CoverageOptions( + outputDirectory: null, + scopeOutput: [], + functionCoverage: false, + branchCoverage: false, + packageDirectory: '.', + packageName: null, + testScript: 'test', + ); +} diff --git a/pkgs/coverage/pubspec.yaml b/pkgs/coverage/pubspec.yaml index 2721626e5..af9790c1e 100644 --- a/pkgs/coverage/pubspec.yaml +++ b/pkgs/coverage/pubspec.yaml @@ -1,5 +1,5 @@ name: coverage -version: 1.11.1 +version: 1.12.0-wip description: Coverage data manipulation and formatting repository: https://github.com/dart-lang/tools/tree/main/pkgs/coverage issue_tracker: https://github.com/dart-lang/tools/issues?q=is%3Aissue+is%3Aopen+label%3Apackage%3Acoverage @@ -9,6 +9,7 @@ environment: dependencies: args: ^2.0.0 + cli_config: ^0.2.0 glob: ^2.1.2 logging: ^1.0.0 meta: ^1.0.2 diff --git a/pkgs/coverage/test/collect_coverage_config_test.dart b/pkgs/coverage/test/collect_coverage_config_test.dart new file mode 100644 index 000000000..5f6c460a8 --- /dev/null +++ b/pkgs/coverage/test/collect_coverage_config_test.dart @@ -0,0 +1,235 @@ +import 'package:coverage/src/coverage_options.dart'; +import 'package:path/path.dart' as path; +import 'package:test/test.dart'; + +import '../bin/collect_coverage.dart' as collect_coverage; +import '../bin/format_coverage.dart' as format_coverage; +import '../bin/test_with_coverage.dart' as test_with_coverage; + +void main() { + final formatCoverageArgs = ['--in=./test/collect_coverage_config_test.dart']; + + group('defaults when no yaml or command-line args provided', () { + // Setup + final defaults = CoverageOptionsProvider().coverageOptions; + + test('collect coverage', () { + final collectedCoverage = collect_coverage.parseArgs([], defaults); + + expect(collectedCoverage.scopedOutput, defaults.scopeOutput); + expect(collectedCoverage.functionCoverage, defaults.functionCoverage); + expect(collectedCoverage.branchCoverage, defaults.branchCoverage); + expect(collectedCoverage.out, isNull); + }); + + test('format coverage', () { + final formattedCoverage = + format_coverage.parseArgs(formatCoverageArgs, defaults); + + expect(formattedCoverage.output, isNull); + expect(formattedCoverage.packagePath, defaults.packageDirectory); + }); + + test('test with coverage', () async { + final testCoverage = await test_with_coverage.parseArgs([], defaults); + + expect(path.canonicalize(testCoverage.packageDir), + path.canonicalize(defaults.packageDirectory)); + expect(testCoverage.packageName, 'coverage'); + expect(path.canonicalize(testCoverage.outDir), + path.canonicalize('coverage')); + expect(testCoverage.testScript, defaults.testScript); + expect(testCoverage.functionCoverage, defaults.functionCoverage); + expect(testCoverage.branchCoverage, defaults.branchCoverage); + expect(testCoverage.scopeOutput, defaults.scopeOutput); + }); + }); + + test('uses yaml values when all values are provided in yaml file', () async { + final configuredOptions = CoverageOptionsProvider( + filePath: 'test/test_coverage_options/all_field.yaml', + ).coverageOptions; + + // Parse arguments with empty command line args + final collectedCoverage = collect_coverage.parseArgs([], configuredOptions); + final formattedCoverage = + format_coverage.parseArgs(formatCoverageArgs, configuredOptions); + final testCoverage = + await test_with_coverage.parseArgs([], configuredOptions); + + // Verify collect coverage yaml values + expect(collectedCoverage.scopedOutput, ['lib', 'src']); + expect(collectedCoverage.functionCoverage, isTrue); + expect(collectedCoverage.branchCoverage, isFalse); + expect(path.canonicalize(collectedCoverage.out!), + path.canonicalize('var/coverage_data/coverage.json')); + + // Verify format coverage yaml values + expect(path.canonicalize(formattedCoverage.output!), + path.canonicalize('var/coverage_data/lcov.info')); + expect(path.canonicalize(formattedCoverage.packagePath), + path.canonicalize('test/test_files')); + + // Verify test with coverage yaml values + expect(path.canonicalize(testCoverage.packageDir), + path.canonicalize('test/test_files')); + expect(testCoverage.packageName, 'My Dart Package'); + expect(path.canonicalize(testCoverage.outDir), + path.canonicalize('var/coverage_data')); + expect(testCoverage.testScript, 'test1'); + expect(testCoverage.functionCoverage, isTrue); + expect(testCoverage.branchCoverage, isFalse); + expect(testCoverage.scopeOutput, ['lib', 'src']); + }); + + group('partial yaml configuration', () { + test('override default values with partial yaml values 1', () async { + final configuredOptions = CoverageOptionsProvider( + filePath: 'test/test_coverage_options/partial_fields1.yaml', + ).coverageOptions; + + // Parse arguments with empty command line args + final collectedCoverage = + collect_coverage.parseArgs([], configuredOptions); + final testCoverage = + await test_with_coverage.parseArgs([], configuredOptions); + final formattedCoverage = + format_coverage.parseArgs([], configuredOptions); + + expect(path.canonicalize(collectedCoverage.out!), + path.canonicalize('var/coverage_data/custom_coverage/coverage.json')); + expect(collectedCoverage.scopedOutput, ['lib', 'test']); + expect(collectedCoverage.functionCoverage, isFalse); + expect(path.canonicalize(formattedCoverage.output!), + path.canonicalize('var/coverage_data/custom_coverage/lcov.info')); + expect(testCoverage.packageName, 'Custom Dart Package'); + expect(testCoverage.scopeOutput, ['lib', 'test']); + }); + + test('override default values with partial yaml values 2', () async { + final configuredOptions = CoverageOptionsProvider( + filePath: 'test/test_coverage_options/partial_fields2.yaml', + ).coverageOptions; + + // Parse arguments with empty command line args + final collectedCoverage = + collect_coverage.parseArgs([], configuredOptions); + final formattedCoverage = + format_coverage.parseArgs([], configuredOptions); + final testCoverage = + await test_with_coverage.parseArgs([], configuredOptions); + + // Verify collect coverage yaml values + expect(collectedCoverage.scopedOutput, ['lib', 'tools']); + expect(collectedCoverage.branchCoverage, isFalse); + expect(collectedCoverage.functionCoverage, isTrue); + expect(path.canonicalize(collectedCoverage.out!), + path.canonicalize('var/coverage_data/custom_lcov/coverage.json')); + + // Verify format coverage yaml values + expect(path.canonicalize(formattedCoverage.output!), + path.canonicalize('var/coverage_data/custom_lcov/lcov.info')); + expect(path.canonicalize(formattedCoverage.packagePath), + path.canonicalize('test/test_coverage_options')); + + // Verify test with coverage yaml values + expect(testCoverage.packageName, 'coverage'); + expect(path.canonicalize(testCoverage.outDir), + path.canonicalize('var/coverage_data/custom_lcov')); + expect(testCoverage.testScript, 'custom_test'); + expect(testCoverage.functionCoverage, isTrue); + }); + }); + + group('override yaml values with command line args', () { + test('override 1', () async { + final configuredOptions = CoverageOptionsProvider( + filePath: 'test/test_coverage_options/all_field.yaml', + ).coverageOptions; + + // Parse arguments with command line args + final collectedCoverage = collect_coverage.parseArgs([ + '--out=var/coverage_data/coverage.json', + '--scope-output=lib', + '--no-function-coverage', + '--branch-coverage', + ], configuredOptions); + final formattedCoverage = format_coverage.parseArgs([ + '--out=var/coverage_data/out_test.info', + '--package=../code_builder', + ], configuredOptions); + final testCoverage = await test_with_coverage.parseArgs([ + '--package-name=test', + '--out=test_coverage.json', + '--test=test_test.dart', + '--function-coverage', + ], configuredOptions); + + // Verify collect coverage command line args + expect(collectedCoverage.out, + path.normalize('var/coverage_data/coverage.json')); + expect(collectedCoverage.scopedOutput, ['lib']); + expect(collectedCoverage.functionCoverage, isFalse); + expect(collectedCoverage.branchCoverage, isTrue); + + // Verify format coverage command line args + expect(formattedCoverage.output, + path.normalize('var/coverage_data/out_test.info')); + expect(formattedCoverage.packagePath, '../code_builder'); + + // Verify test with coverage command line args + expect(testCoverage.packageName, 'test'); + expect(testCoverage.outDir, 'test_coverage.json'); + expect(testCoverage.testScript, 'test_test.dart'); + expect(testCoverage.functionCoverage, isTrue); + }); + + test('override 2', () async { + final configuredOptions = CoverageOptionsProvider( + filePath: 'test/test_coverage_options/all_field.yaml', + ).coverageOptions; + + // Parse arguments with command line args + final collectedCoverage = collect_coverage.parseArgs([ + '--out=stdout', + '--scope-output=src', + '--function-coverage', + '--no-branch-coverage', + ], configuredOptions); + final formattedCoverage = format_coverage.parseArgs([ + '--out=stdout', + '--package=../cli_config', + ], configuredOptions); + final testCoverage = await test_with_coverage.parseArgs([ + '--package-name=cli_config', + '--out=cli_config_coverage.json', + '--test=cli_config_test.dart', + '--function-coverage', + ], configuredOptions); + + // Verify collect coverage command line args + expect(collectedCoverage.out, isNull); + expect(collectedCoverage.scopedOutput, ['src']); + expect(collectedCoverage.functionCoverage, isTrue); + expect(collectedCoverage.branchCoverage, isFalse); + + // Verify format coverage command line args + expect(formattedCoverage.output, isNull); + expect(formattedCoverage.packagePath, '../cli_config'); + + // Verify test with coverage command line args + expect(testCoverage.packageName, 'cli_config'); + expect(testCoverage.outDir, 'cli_config_coverage.json'); + expect(testCoverage.testScript, 'cli_config_test.dart'); + expect(testCoverage.functionCoverage, isTrue); + }); + }); + + test('format exception when empty yaml file', () { + expect( + () => CoverageOptionsProvider( + filePath: 'test/test_coverage_options/empty.yaml', + ), + throwsFormatException); + }); +} diff --git a/pkgs/coverage/test/config_file_locator_test.dart b/pkgs/coverage/test/config_file_locator_test.dart new file mode 100644 index 000000000..d46fc869b --- /dev/null +++ b/pkgs/coverage/test/config_file_locator_test.dart @@ -0,0 +1,45 @@ +import 'dart:io'; +import 'package:coverage/src/coverage_options.dart'; +import 'package:path/path.dart' as path; +import 'package:test/test.dart'; + +void main() { + final baseTestPath = 'test/test_file_locator'; + late Directory testDirectory; + + test('options file exists', () { + testDirectory = Directory('$baseTestPath/pkg1/lib/src'); + var filePath = + CoverageOptionsProvider.findOptionsFilePath(directory: testDirectory); + expect(path.normalize('$baseTestPath/pkg1/coverage_options.yaml'), + path.normalize(filePath!)); + + testDirectory = Directory('$baseTestPath/pkg1/lib'); + filePath = + CoverageOptionsProvider.findOptionsFilePath(directory: testDirectory); + expect(path.normalize('$baseTestPath/pkg1/coverage_options.yaml'), + path.normalize(filePath!)); + }); + + test('options file missing', () { + testDirectory = Directory('$baseTestPath/pkg2/lib/src'); + var filePath = + CoverageOptionsProvider.findOptionsFilePath(directory: testDirectory); + expect(filePath, isNull); + + testDirectory = Directory('$baseTestPath/pkg2/lib'); + filePath = + CoverageOptionsProvider.findOptionsFilePath(directory: testDirectory); + expect(filePath, isNull); + }); + + test('no pubspec found', () { + var filePath = CoverageOptionsProvider.findOptionsFilePath( + directory: Directory.systemTemp); + expect(filePath, isNull); + + filePath = CoverageOptionsProvider.findOptionsFilePath( + directory: Directory.systemTemp); + expect(filePath, isNull); + }); +} diff --git a/pkgs/coverage/test/test_coverage_options/all_field.yaml b/pkgs/coverage/test/test_coverage_options/all_field.yaml new file mode 100644 index 000000000..05a07a943 --- /dev/null +++ b/pkgs/coverage/test/test_coverage_options/all_field.yaml @@ -0,0 +1,7 @@ +output-directory: "../../var/coverage_data" +scope-output: ["lib", "src"] +function-coverage: true +branch-coverage: false +package-directory: "../test_files" +package-name: "My Dart Package" +test_script: "test1" diff --git a/pkgs/coverage/test/test_coverage_options/empty.yaml b/pkgs/coverage/test/test_coverage_options/empty.yaml new file mode 100644 index 000000000..e69de29bb diff --git a/pkgs/coverage/test/test_coverage_options/partial_fields1.yaml b/pkgs/coverage/test/test_coverage_options/partial_fields1.yaml new file mode 100644 index 000000000..165c3804f --- /dev/null +++ b/pkgs/coverage/test/test_coverage_options/partial_fields1.yaml @@ -0,0 +1,5 @@ +output-directory: '../../var/coverage_data/custom_coverage' +scope-output: + - 'lib' + - 'test' +package-name: 'Custom Dart Package' diff --git a/pkgs/coverage/test/test_coverage_options/partial_fields2.yaml b/pkgs/coverage/test/test_coverage_options/partial_fields2.yaml new file mode 100644 index 000000000..d1782546e --- /dev/null +++ b/pkgs/coverage/test/test_coverage_options/partial_fields2.yaml @@ -0,0 +1,8 @@ +scope-output: + - 'lib' + - 'tools' +branch-coverage: false +output-directory: '../../var/coverage_data/custom_lcov' +package-directory: '.' +test_script: 'custom_test' +function-coverage: true diff --git a/pkgs/coverage/test/test_file_locator/pkg1/coverage_options.yaml b/pkgs/coverage/test/test_file_locator/pkg1/coverage_options.yaml new file mode 100644 index 000000000..e69de29bb diff --git a/pkgs/coverage/test/test_file_locator/pkg1/pubspec.yaml b/pkgs/coverage/test/test_file_locator/pkg1/pubspec.yaml new file mode 100644 index 000000000..7c6c16bfe --- /dev/null +++ b/pkgs/coverage/test/test_file_locator/pkg1/pubspec.yaml @@ -0,0 +1 @@ +name: pkg1 diff --git a/pkgs/coverage/test/test_file_locator/pkg2/pubspec.yaml b/pkgs/coverage/test/test_file_locator/pkg2/pubspec.yaml new file mode 100644 index 000000000..956b21dc3 --- /dev/null +++ b/pkgs/coverage/test/test_file_locator/pkg2/pubspec.yaml @@ -0,0 +1 @@ +name: pkg2