From 99afba11c07401dd06e71cbc52fc0bfa93d6e88c Mon Sep 17 00:00:00 2001 From: Dhruv Maradiya Date: Tue, 31 Dec 2024 19:36:27 +0530 Subject: [PATCH 01/12] Implemented functionality to allows specifying coverage options and flags in yaml file --- pkgs/coverage/bin/collect_coverage.dart | 51 ++-- pkgs/coverage/bin/format_coverage.dart | 63 ++++- pkgs/coverage/bin/test_with_coverage.dart | 88 ++++--- pkgs/coverage/lib/src/coverage_options.dart | 267 ++++++++++++++++++++ pkgs/coverage/lib/src/util.dart | 33 +++ pkgs/coverage/lib/src/yaml_merger.dart | 60 +++++ pkgs/coverage/pubspec.yaml | 1 + 7 files changed, 493 insertions(+), 70 deletions(-) create mode 100644 pkgs/coverage/lib/src/coverage_options.dart create mode 100644 pkgs/coverage/lib/src/yaml_merger.dart diff --git a/pkgs/coverage/bin/collect_coverage.dart b/pkgs/coverage/bin/collect_coverage.dart index b02ac2110..086ff3a81 100644 --- a/pkgs/coverage/bin/collect_coverage.dart +++ b/pkgs/coverage/bin/collect_coverage.dart @@ -8,6 +8,7 @@ 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:stack_trace/stack_trace.dart'; @@ -17,7 +18,9 @@ Future main(List arguments) async { print('${rec.level.name}: ${rec.time}: ${rec.message}'); }); - final options = _parseArgs(arguments); + final defaultOptions = + CoverageOptionsProvider().coverageOptions.collectCoverage; + final options = _parseArgs(arguments, defaultOptions); await Chain.capture(() async { final coverage = await collect(options.serviceUri, options.resume, options.waitPaused, options.includeDart, options.scopedOutput, @@ -58,37 +61,45 @@ class Options { final Set scopedOutput; } -Options _parseArgs(List arguments) { +Options _parseArgs( + List arguments, CollectCoverageOptions defaultOptions) { final parser = ArgParser() ..addOption('host', - abbr: 'H', - help: 'remote VM host. DEPRECATED: use --uri', - defaultsTo: '127.0.0.1') + abbr: 'H', help: 'remote VM host. DEPRECATED: use --uri') ..addOption('port', - abbr: 'p', - help: 'remote VM port. DEPRECATED: use --uri', - defaultsTo: '8181') + abbr: 'p', help: 'remote VM port. DEPRECATED: use --uri') ..addOption('uri', abbr: 'u', help: 'VM observatory service URI') ..addOption('out', - abbr: 'o', defaultsTo: 'stdout', help: 'output: may be file or stdout') + abbr: 'o', + defaultsTo: defaultOptions.out, + help: 'output: may be file or stdout') ..addOption('connect-timeout', - abbr: 't', help: 'connect timeout in seconds') + defaultsTo: defaultOptions.connectTimeout?.toString(), + 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', abbr: 'w', - defaultsTo: false, + defaultsTo: defaultOptions.waitPaused, help: 'wait for all isolates to be paused before collecting coverage') ..addFlag('resume-isolates', - abbr: 'r', defaultsTo: false, help: 'resume all isolates on exit') + abbr: 'r', + defaultsTo: defaultOptions.resumeIsolates, + help: 'resume all isolates on exit') ..addFlag('include-dart', - abbr: 'd', defaultsTo: false, help: 'include "dart:" libraries') + abbr: 'd', + defaultsTo: defaultOptions.includeDart, + 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'); @@ -112,13 +123,17 @@ Options _parseArgs(List arguments) { } Uri serviceUri; - if (args['uri'] == null) { + if (args['uri'] == null && (args['host'] != null || args['port'] != null)) { // TODO(cbracken) eliminate --host and --port support when VM defaults to // requiring an auth token. Estimated for Dart SDK 1.22. - serviceUri = Uri.parse('http://${args['host']}:${args['port']}/'); + + final host = args['host'] ?? defaultOptions.host; + final port = args['port'] ?? defaultOptions.port; + + serviceUri = Uri.parse('http://$host:$port/'); } else { try { - serviceUri = Uri.parse(args['uri'] as String); + serviceUri = Uri.parse((args['uri'] ?? defaultOptions.uri) as String); } on FormatException { fail('Invalid service URI specified: ${args['uri']}'); } diff --git a/pkgs/coverage/bin/format_coverage.dart b/pkgs/coverage/bin/format_coverage.dart index d24f60d9c..4b79e05c7 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; @@ -51,7 +52,9 @@ class Environment { } Future main(List arguments) async { - final env = parseArgs(arguments); + final defaultOptions = + CoverageOptionsProvider().coverageOptions.formatCoverage; + final env = parseArgs(arguments, defaultOptions); final files = filesToProcess(env.input); if (env.verbose) { @@ -129,54 +132,86 @@ Future main(List arguments) async { /// Checks the validity of the provided arguments. Does not initialize actual /// processing. -Environment parseArgs(List arguments) { +Environment parseArgs( + List arguments, FormatCoverageOptions 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', + defaultsTo: defaultOptions.sdkRoot, + ) + ..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') + help: 'root directory of the package', + defaultsTo: defaultOptions.packagePath) + ..addOption('in', + abbr: 'i', + help: 'input(s): may be file or directory', + defaultsTo: defaultOptions.input) ..addOption('out', - abbr: 'o', defaultsTo: 'stdout', help: 'output: may be file or stdout') + abbr: 'o', + defaultsTo: defaultOptions.output, + 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: 'which directories or files to report coverage on', + defaultsTo: defaultOptions.reportOn) + ..addOption( + 'workers', + abbr: 'j', + defaultsTo: defaultOptions.workers.toString(), + help: 'number of workers', + ) ..addOption('bazel-workspace', - defaultsTo: '', help: 'Bazel workspace directory') + defaultsTo: defaultOptions.bazelWorkspace, + help: 'Bazel workspace directory') ..addOption('base-directory', abbr: 'b', + defaultsTo: defaultOptions.baseDirectory, help: 'the base directory relative to which source paths are output') ..addFlag('bazel', - defaultsTo: false, help: 'use Bazel-style path resolution') + defaultsTo: defaultOptions.bazel, + help: 'use Bazel-style path resolution') ..addFlag('pretty-print', abbr: 'r', + defaultsTo: defaultOptions.prettyPrint, negatable: false, help: 'convert line coverage data to pretty print format') ..addFlag('pretty-print-func', abbr: 'f', + defaultsTo: defaultOptions.prettyPrintFunc, negatable: false, help: 'convert function coverage data to pretty print format') ..addFlag('pretty-print-branch', negatable: false, + defaultsTo: defaultOptions.prettyPrintBranch, help: 'convert branch coverage data to pretty print format') ..addFlag('lcov', abbr: 'l', + defaultsTo: defaultOptions.lcov, negatable: false, help: 'convert coverage data to lcov format') - ..addFlag('verbose', abbr: 'v', negatable: false, help: 'verbose output') + ..addFlag('verbose', + abbr: 'v', + defaultsTo: defaultOptions.verbose, + negatable: false, + help: 'verbose output') ..addFlag( 'check-ignore', abbr: 'c', + defaultsTo: defaultOptions.checkIgnore, negatable: false, help: 'check for coverage ignore comments.' ' Not supported in web coverage.', ) ..addMultiOption( 'ignore-files', - defaultsTo: [], + defaultsTo: defaultOptions.ignoreFiles, help: 'Ignore files by glob patterns', ) ..addFlag('help', abbr: 'h', negatable: false, help: 'show this help'); diff --git a/pkgs/coverage/bin/test_with_coverage.dart b/pkgs/coverage/bin/test_with_coverage.dart index 010e6089a..e2e1e1458 100644 --- a/pkgs/coverage/bin/test_with_coverage.dart +++ b/pkgs/coverage/bin/test_with_coverage.dart @@ -6,6 +6,7 @@ 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:package_config/package_config.dart'; @@ -50,41 +51,48 @@ void _watchExitSignal(ProcessSignal signal) { }); } -ArgParser _createArgParser() => ArgParser() - ..addOption( - 'package', - help: 'Root directory of the package to test.', - defaultsTo: '.', - ) - ..addOption( - 'package-name', - help: 'Name of the package to test. ' - 'Deduced from --package if not provided.', - ) - ..addOption('port', help: 'VM service port.', defaultsTo: '8181') - ..addOption( - 'out', - abbr: 'o', - help: 'Output directory. Defaults to /coverage.', - ) - ..addOption('test', help: 'Test script to run.', defaultsTo: 'test') - ..addFlag( - 'function-coverage', - abbr: 'f', - defaultsTo: false, - help: 'Collect function coverage info.', - ) - ..addFlag( - 'branch-coverage', - abbr: 'b', - defaultsTo: false, - help: 'Collect branch coverage info.', - ) - ..addMultiOption('scope-output', - 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.') - ..addFlag('help', abbr: 'h', negatable: false, help: 'Show this help.'); +ArgParser _createArgParser(TestWithCoverageOptions defaultOptions) => + ArgParser() + ..addOption( + 'package', + help: 'Root directory of the package to test.', + defaultsTo: defaultOptions.packageDir, + ) + ..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: defaultOptions.port) + ..addOption( + 'out', + defaultsTo: defaultOptions.outDir, + abbr: 'o', + help: 'Output directory. Defaults to /coverage.', + ) + ..addOption('test', + help: 'Test script to run.', defaultsTo: defaultOptions.testScript) + ..addFlag( + 'function-coverage', + abbr: 'f', + defaultsTo: defaultOptions.functionCoverage, + help: 'Collect function coverage info.', + ) + ..addFlag( + 'branch-coverage', + abbr: 'b', + 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.') + ..addFlag('help', abbr: 'h', negatable: false, help: 'Show this help.'); class Flags { Flags( @@ -110,8 +118,9 @@ class Flags { final List rest; } -Future _parseArgs(List arguments) async { - final parser = _createArgParser(); +Future _parseArgs( + List arguments, TestWithCoverageOptions defaultOptions) async { + final parser = _createArgParser(defaultOptions); final args = parser.parse(arguments); void printUsage() { @@ -166,7 +175,10 @@ ${parser.usage} } Future main(List arguments) async { - final flags = await _parseArgs(arguments); + final defaultOptions = + CoverageOptionsProvider().coverageOptions.testWithCoverage; + 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..6f10634a4 --- /dev/null +++ b/pkgs/coverage/lib/src/coverage_options.dart @@ -0,0 +1,267 @@ +import 'dart:io'; + +import 'package:yaml/yaml.dart'; + +import 'util.dart'; +import 'yaml_merger.dart'; + +class CollectCoverageOptions { + const CollectCoverageOptions({ + required this.uri, + required this.out, + required this.connectTimeout, + required this.scopeOutput, + required this.waitPaused, + required this.resumeIsolates, + required this.includeDart, + required this.functionCoverage, + required this.branchCoverage, + }); + + /// Creates a [CollectCoverageOptions] instance from YAML configuration. + factory CollectCoverageOptions.fromYaml(YamlMap yaml) => + CollectCoverageOptions( + uri: YamlUtils.getString(yaml, 'uri') as String, + out: YamlUtils.getString(yaml, 'out') as String, + connectTimeout: YamlUtils.getInt(yaml, 'connect-timeout'), + scopeOutput: YamlUtils.getStringList(yaml, 'scope-output'), + waitPaused: YamlUtils.getBool(yaml, 'wait-paused') ?? false, + resumeIsolates: YamlUtils.getBool(yaml, 'resume-isolates') ?? false, + includeDart: YamlUtils.getBool(yaml, 'include-dart') ?? false, + functionCoverage: YamlUtils.getBool(yaml, 'function-coverage') ?? false, + branchCoverage: YamlUtils.getBool(yaml, 'branch-coverage') ?? false, + ); + + final String host = '127.0.0.1'; + final String port = '8181'; + final String uri; + final String out; + final int? connectTimeout; + final List scopeOutput; + final bool waitPaused; + final bool resumeIsolates; + final bool includeDart; + final bool functionCoverage; + final bool branchCoverage; +} + +class FormatCoverageOptions { + const FormatCoverageOptions({ + required this.baseDirectory, + required this.bazel, + required this.bazelWorkspace, + required this.checkIgnore, + required this.input, + required this.lcov, + required this.output, + required this.packagePath, + required this.prettyPrint, + required this.prettyPrintFunc, + required this.prettyPrintBranch, + required this.reportOn, + required this.ignoreFiles, + required this.sdkRoot, + required this.verbose, + required this.workers, + }); + + /// Creates a [FormatCoverageOptions] instance from YAML configuration. + factory FormatCoverageOptions.fromYaml(YamlMap yaml) => FormatCoverageOptions( + baseDirectory: YamlUtils.getString(yaml, 'base-directory'), + bazel: YamlUtils.getBool(yaml, 'bazel') ?? false, + bazelWorkspace: YamlUtils.getString(yaml, 'bazel-workspace') as String, + checkIgnore: YamlUtils.getBool(yaml, 'check-ignore') ?? false, + input: YamlUtils.getString(yaml, 'in'), + lcov: YamlUtils.getBool(yaml, 'lcov') ?? false, + output: YamlUtils.getString(yaml, 'out') as String, + packagePath: YamlUtils.getString(yaml, 'package') as String, + prettyPrint: YamlUtils.getBool(yaml, 'pretty-print') ?? false, + prettyPrintFunc: YamlUtils.getBool(yaml, 'pretty-print-func') ?? false, + prettyPrintBranch: + YamlUtils.getBool(yaml, 'pretty-print-branch') ?? false, + reportOn: YamlUtils.getStringList(yaml, 'report-on'), + ignoreFiles: YamlUtils.getStringList(yaml, 'ignore-files'), + sdkRoot: YamlUtils.getString(yaml, 'sdk-root'), + verbose: YamlUtils.getBool(yaml, 'verbose') ?? false, + workers: YamlUtils.getInt(yaml, 'workers') as int, + ); + + final String? baseDirectory; + final bool bazel; + final String bazelWorkspace; + final bool checkIgnore; + final String? input; + final bool lcov; + final String output; + final String packagePath; + final bool prettyPrint; + final bool prettyPrintFunc; + final bool prettyPrintBranch; + final List? reportOn; + final List? ignoreFiles; + final String? sdkRoot; + final bool verbose; + final int workers; +} + +class TestWithCoverageOptions { + const TestWithCoverageOptions({ + required this.packageDir, + required this.packageName, + required this.outDir, + required this.port, + required this.testScript, + required this.functionCoverage, + required this.branchCoverage, + required this.scopeOutput, + }); + + /// Creates a [TestWithCoverageOptions] instance from YAML configuration. + factory TestWithCoverageOptions.fromYaml(YamlMap yaml) => + TestWithCoverageOptions( + packageDir: YamlUtils.getString(yaml, 'package') as String, + packageName: YamlUtils.getString(yaml, 'package-name'), + outDir: YamlUtils.getString(yaml, 'out'), + port: YamlUtils.getString(yaml, 'port') as String, + testScript: YamlUtils.getString(yaml, 'test') as String, + functionCoverage: YamlUtils.getBool(yaml, 'function-coverage') ?? false, + branchCoverage: YamlUtils.getBool(yaml, 'branch-coverage') ?? false, + scopeOutput: YamlUtils.getStringList(yaml, 'scope-output'), + ); + + final String packageDir; + final String? packageName; + final String? outDir; + final String port; + final String testScript; + final bool functionCoverage; + final bool branchCoverage; + final List scopeOutput; +} + +class CoverageOptions { + const CoverageOptions({ + required this.collectCoverage, + required this.formatCoverage, + required this.testWithCoverage, + }); + + final CollectCoverageOptions collectCoverage; + final FormatCoverageOptions formatCoverage; + final TestWithCoverageOptions testWithCoverage; +} + +abstract class CoverageOptionsProvider { + factory CoverageOptionsProvider( + {final String? optionsFilePath, final Map? options}) { + YamlMap coverageOptions; + + final defaultOptionsProvider = DefaultCoverageOptionsProvider(); + + if (options != null) { + coverageOptions = YamlMap.wrap(options); + } else { + final optionsFile = + _getOptionsFile(optionsFilePath ?? CoverageOptionsProvider.filePath); + + if (optionsFile == null) { + return defaultOptionsProvider; + } + coverageOptions = _getOptionsFromFile(optionsFile); + } + + final mergedOptions = defaultOptionsProvider.merge( + defaultOptionsProvider.options, + coverageOptions, + ); + + return CustomCoverageOptionsProvider(mergedOptions); + } + + CoverageOptionsProvider._(); + + static const filePath = 'coverage_options.yaml'; + + static File? _getOptionsFile(String filePath) { + final file = File(filePath); + return file.existsSync() ? file : null; + } + + static YamlMap _getOptionsFromFile(File file) { + final yamlString = file.readAsStringSync(); + final content = loadYaml(yamlString); + + final options = content is YamlMap ? content['defaults'] : YamlMap.wrap({}); + + return options is YamlMap ? options : YamlMap.wrap({}); + } + + YamlMap merge(YamlMap defaults, YamlMap overrides) => + Merger().mergeMap(defaults, overrides); + + YamlMap get options; + CoverageOptions get coverageOptions => CoverageOptions( + collectCoverage: CollectCoverageOptions.fromYaml( + options['collect_coverage'] as YamlMap), + formatCoverage: FormatCoverageOptions.fromYaml( + options['format_coverage'] as YamlMap), + testWithCoverage: TestWithCoverageOptions.fromYaml( + options['test_with_coverage'] as YamlMap), + ); +} + +class DefaultCoverageOptionsProvider extends CoverageOptionsProvider { + DefaultCoverageOptionsProvider() : super._(); + + @override + YamlMap get options => defaultOptions; + + static final defaultOptions = YamlMap.wrap({ + 'collect_coverage': { + 'uri': 'http://127.0.0.1:8181/', + 'out': 'stdout', + 'connect-timeout': null, + 'scope-output': List.empty(), + 'wait-paused': false, + 'resume-isolates': false, + 'include-dart': false, + 'function-coverage': false, + 'branch-coverage': false, + }, + 'format_coverage': { + 'base-directory': '.', + 'bazel': false, + 'bazel-workspace': '', + 'check-ignore': false, + 'in': null, + 'lcov': false, + 'out': 'stdout', + 'package': '.', + 'pretty-print': false, + 'pretty-print-func': false, + 'pretty-print-branch': false, + 'report-on': null, + 'ignore-files': List.empty(), + 'sdk-root': null, + 'verbose': false, + 'workers': 1, + }, + 'test_with_coverage': { + 'package': '.', + 'package-name': null, + 'port': '8181', + 'out': null, + 'test': 'test', + 'function-coverage': false, + 'branch-coverage': false, + 'scope-output': List.empty(), + } + }); +} + +class CustomCoverageOptionsProvider extends CoverageOptionsProvider { + CustomCoverageOptionsProvider(this.options) : super._(); + + @override + final YamlMap options; +} diff --git a/pkgs/coverage/lib/src/util.dart b/pkgs/coverage/lib/src/util.dart index cc7f58425..7585a11a1 100644 --- a/pkgs/coverage/lib/src/util.dart +++ b/pkgs/coverage/lib/src/util.dart @@ -7,6 +7,7 @@ import 'dart:convert'; import 'dart:io'; import 'package:vm_service/vm_service.dart'; +import 'package:yaml/yaml.dart'; // TODO(cbracken) make generic /// Retries the specified function with the specified interval and returns @@ -184,3 +185,35 @@ Future serviceUriFromProcess(Stream procStdout) { Future> getAllIsolates(VmService service) async => (await service.getVM()).isolates ?? []; + +abstract class YamlUtils { + /// Safely extracts a boolean value from YAML. + static bool? getBool(YamlMap yaml, String key) => switch (yaml[key]) { + final YamlScalar scalar => scalar.value as bool?, + final bool value => value, + _ => null, + }; + + /// Safely extracts a list of strings from YAML. + static List getStringList(YamlMap yaml, String key) { + final value = yaml[key]; + if (value is YamlList) { + return value.cast(); + } + return const []; + } + + /// Safely extracts a string value from YAML. + static String? getString(YamlMap yaml, String key) => switch (yaml[key]) { + final YamlScalar scalar => scalar.value?.toString(), + final String value => value, + _ => null, + }; + + /// Safely extracts an integer value from YAML. + static int? getInt(YamlMap yaml, String key) => switch (yaml[key]) { + final YamlScalar scalar => scalar.value as int?, + final int value => value, + _ => null, + }; +} diff --git a/pkgs/coverage/lib/src/yaml_merger.dart b/pkgs/coverage/lib/src/yaml_merger.dart new file mode 100644 index 000000000..1116033b3 --- /dev/null +++ b/pkgs/coverage/lib/src/yaml_merger.dart @@ -0,0 +1,60 @@ +import 'dart:collection'; + +import 'package:yaml/yaml.dart'; + +/// Merges two maps (of yaml) with simple override semantics, suitable for +/// merging two maps where one map defines default values that are added to +/// (and possibly overridden) by an overriding map. +class Merger { + /// Merges a default [o1] with an overriding object [o2]. + /// + /// * lists are overridden. + /// * maps are merged recursively. + /// * if map values cannot be merged, the overriding value is taken. + /// + YamlNode merge(YamlNode o1, YamlNode o2) { + if (o1 is YamlMap && o2 is YamlMap) { + return mergeMap(o1, o2); + } + + // Default to override, unless the overriding value is `null`. + if (o2.value == null) { + return o1; + } + return o2; + } + + /// Merge maps (recursively). + YamlMap mergeMap(YamlMap m1, YamlMap m2) { + final Map merged = + HashMap(); // equals: _equals, hashCode: _hashCode + m1.nodeMap.forEach((k, v) { + merged[k] = v; + }); + m2.nodeMap.forEach((k, v) { + final value = k.value; + final mergedKey = + merged.keys.firstWhere((key) => key.value == value, orElse: () => k) + as YamlScalar; + final o1 = merged[mergedKey]; + if (o1 != null) { + merged[mergedKey] = merge(o1, v); + } else { + merged[mergedKey] = v; + } + }); + + final result = {}; + + merged.forEach((key, value) { + result[key.value] = value; + }); + + return YamlMap.wrap(result); + } +} + +extension YamlMapExtensions on YamlMap { + /// Return [nodes] as a Map with [YamlNode] keys. + Map get nodeMap => nodes.cast(); +} diff --git a/pkgs/coverage/pubspec.yaml b/pkgs/coverage/pubspec.yaml index 2721626e5..db193a3a7 100644 --- a/pkgs/coverage/pubspec.yaml +++ b/pkgs/coverage/pubspec.yaml @@ -17,6 +17,7 @@ dependencies: source_maps: ^0.10.10 stack_trace: ^1.10.0 vm_service: '>=12.0.0 <16.0.0' + yaml: ^3.1.3 dev_dependencies: benchmark_harness: ^2.2.0 From ff390a9c5a6950c1f1127d44bd11bae2c0283b44 Mon Sep 17 00:00:00 2001 From: Dhruv Maradiya Date: Tue, 31 Dec 2024 19:55:45 +0530 Subject: [PATCH 02/12] Added test cases to cover YAML configuration options. --- .../test/collect_coverage_config_test.dart | 438 ++++++++++++++++++ .../test/test_coverage_options/all_field.yaml | 39 ++ .../test/test_coverage_options/empty.yaml | 4 + .../test_coverage_options/partial_fields.dart | 53 +++ pkgs/coverage/test/test_util.dart | 171 +++++++ 5 files changed, 705 insertions(+) create mode 100644 pkgs/coverage/test/collect_coverage_config_test.dart create mode 100644 pkgs/coverage/test/test_coverage_options/all_field.yaml create mode 100644 pkgs/coverage/test/test_coverage_options/empty.yaml create mode 100644 pkgs/coverage/test/test_coverage_options/partial_fields.dart 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..a8656ca0a --- /dev/null +++ b/pkgs/coverage/test/collect_coverage_config_test.dart @@ -0,0 +1,438 @@ +import 'package:coverage/src/coverage_options.dart'; +import 'package:test/test.dart'; + +import 'test_coverage_options/partial_fields.dart'; +import 'test_util.dart'; + +void main() { + final defaultInputArgs = ['--in=coverage.json']; + + test('defaults when no yaml or command-line args provided', () async { + // Setup + final defaults = DefaultCoverageOptionsProvider().coverageOptions; + final configuredOptions = CoverageOptionsProvider().coverageOptions; + + // Parse arguments with empty command line args + final collectedCoverage = + parseArgsCollectCoverage([], configuredOptions.collectCoverage); + final formattedCoverage = parseArgsFormatCoverage( + defaultInputArgs, configuredOptions.formatCoverage); + final testCoverage = + await parseArgsTestWithCoverage([], configuredOptions.testWithCoverage); + + // Verify collect coverage defaults + expect(collectedCoverage.uri, defaults.collectCoverage.uri, + reason: 'URI should match default'); + expect(collectedCoverage.scopeOutput, defaults.collectCoverage.scopeOutput, + reason: 'scope output should match default'); + expect(collectedCoverage.resumeIsolates, + defaults.collectCoverage.resumeIsolates, + reason: 'resume isolates should match default'); + expect(collectedCoverage.waitPaused, defaults.collectCoverage.waitPaused, + reason: 'wait paused should match default'); + expect(collectedCoverage.functionCoverage, + defaults.collectCoverage.functionCoverage, + reason: 'function coverage should match default'); + expect(collectedCoverage.branchCoverage, + defaults.collectCoverage.branchCoverage, + reason: 'branch coverage should match default'); + expect(collectedCoverage.connectTimeout, + defaults.collectCoverage.connectTimeout, + reason: 'connect timeout should match default'); + expect(collectedCoverage.includeDart, defaults.collectCoverage.includeDart, + reason: 'include dart should match default'); + expect(collectedCoverage.out, defaults.collectCoverage.out, + reason: 'output path should match default'); + + // Verify format coverage defaults + expect( + formattedCoverage.baseDirectory, defaults.formatCoverage.baseDirectory, + reason: 'base directory should match default'); + expect(formattedCoverage.bazel, defaults.formatCoverage.bazel, + reason: 'bazel flag should match default'); + expect(formattedCoverage.bazelWorkspace, + defaults.formatCoverage.bazelWorkspace, + reason: 'bazel workspace should match default'); + expect(formattedCoverage.checkIgnore, defaults.formatCoverage.checkIgnore, + reason: 'check ignore should match default'); + expect(formattedCoverage.input, 'coverage.json', + reason: 'input should be coverage.json'); + expect(formattedCoverage.lcov, defaults.formatCoverage.lcov, + reason: 'lcov flag should match default'); + expect(formattedCoverage.output, defaults.formatCoverage.output, + reason: 'output path should match default'); + expect(formattedCoverage.packagePath, defaults.formatCoverage.packagePath, + reason: 'package path should match default'); + expect(formattedCoverage.prettyPrint, defaults.formatCoverage.prettyPrint, + reason: 'pretty print should match default'); + expect(formattedCoverage.prettyPrintFunc, + defaults.formatCoverage.prettyPrintFunc, + reason: 'pretty print func should match default'); + expect(formattedCoverage.prettyPrintBranch, + defaults.formatCoverage.prettyPrintBranch, + reason: 'pretty print branch should match default'); + expect(formattedCoverage.reportOn, isNull, + reason: 'report on should be null'); + expect(formattedCoverage.ignoreFiles, defaults.formatCoverage.ignoreFiles, + reason: 'ignore files should match default'); + expect(formattedCoverage.sdkRoot, defaults.formatCoverage.sdkRoot, + reason: 'sdk root should match default'); + expect(formattedCoverage.verbose, defaults.formatCoverage.verbose, + reason: 'verbose flag should match default'); + expect(formattedCoverage.workers, defaults.formatCoverage.workers, + reason: 'workers count should match default'); + + // Verify test with coverage defaults + expect(testCoverage.packageDir, + getPackageDir(defaults.testWithCoverage.packageDir), + reason: 'package directory should match current directory'); + expect(testCoverage.packageName, 'coverage', + reason: 'package name should be coverage'); + expect(testCoverage.outDir, 'coverage', + reason: 'output directory should be coverage'); + expect(testCoverage.port, defaults.testWithCoverage.port, + reason: 'port should match default'); + expect(testCoverage.testScript, defaults.testWithCoverage.testScript, + reason: 'test script should match default'); + expect(testCoverage.functionCoverage, + defaults.testWithCoverage.functionCoverage, + reason: 'function coverage should match default'); + expect( + testCoverage.branchCoverage, defaults.testWithCoverage.branchCoverage, + reason: 'branch coverage should match default'); + expect(testCoverage.scopeOutput, defaults.testWithCoverage.scopeOutput, + reason: 'scope output should match default'); + }); + + test('uses yaml values when all values are provided in yaml file', () async { + final configuredOptions = CoverageOptionsProvider( + optionsFilePath: 'test/test_coverage_options/all_field.yaml', + ).coverageOptions; + + // Parse arguments with empty command line args + final collectedCoverage = + parseArgsCollectCoverage([], configuredOptions.collectCoverage); + final formattedCoverage = parseArgsFormatCoverage( + defaultInputArgs, configuredOptions.formatCoverage); + final testCoverage = + await parseArgsTestWithCoverage([], configuredOptions.testWithCoverage); + + // Verify collect coverage yaml values + expect(collectedCoverage.uri, 'http://127.0.0.1:8181/'); + expect(collectedCoverage.scopeOutput, ['lib/']); + expect(collectedCoverage.resumeIsolates, isFalse); + expect(collectedCoverage.waitPaused, isTrue); + expect(collectedCoverage.functionCoverage, isTrue); + expect(collectedCoverage.branchCoverage, isFalse); + expect(collectedCoverage.connectTimeout, 30); + expect(collectedCoverage.includeDart, isFalse); + expect(collectedCoverage.out, 'coverage.json'); + + // Verify format coverage yaml values + expect(formattedCoverage.baseDirectory, '.'); + expect(formattedCoverage.bazel, isFalse); + expect(formattedCoverage.bazelWorkspace, ''); + expect(formattedCoverage.checkIgnore, isFalse); + expect(formattedCoverage.input, 'coverage.json'); + expect(formattedCoverage.lcov, isTrue); + expect(formattedCoverage.output, 'lcov.info'); + expect(formattedCoverage.packagePath, '.'); + expect(formattedCoverage.prettyPrint, isFalse); + expect(formattedCoverage.prettyPrintFunc, isFalse); + expect(formattedCoverage.prettyPrintBranch, isFalse); + expect(formattedCoverage.reportOn, ['lib/', 'bin/']); + expect(formattedCoverage.ignoreFiles, ['test/']); + expect(formattedCoverage.sdkRoot, '.'); + expect(formattedCoverage.verbose, isTrue); + expect(formattedCoverage.workers, 2); + + // Verify test with coverage yaml values + expect(testCoverage.packageDir, getPackageDir('.')); + expect(testCoverage.packageName, 'My Dart Package'); + expect(testCoverage.outDir, 'test_coverage.json'); + expect(testCoverage.port, '8181'); + expect(testCoverage.testScript, 'test'); + expect(testCoverage.functionCoverage, isTrue); + expect(testCoverage.branchCoverage, isFalse); + expect(testCoverage.scopeOutput, ['lib/src/']); + }); + + group('partial yaml configuration', () { + final partial1 = partialFieldOptions[0]; + final partial2 = partialFieldOptions[1]; + + test('override default values with partial yaml values 1', () async { + final configuredOptions = CoverageOptionsProvider( + options: partial1, + ).coverageOptions; + + // Parse arguments with empty command line args + final collectedCoverage = + parseArgsCollectCoverage([], configuredOptions.collectCoverage); + final formattedCoverage = parseArgsFormatCoverage( + defaultInputArgs, configuredOptions.formatCoverage); + final testCoverage = await parseArgsTestWithCoverage( + [], configuredOptions.testWithCoverage); + + // Verify collect coverage yaml values + expect(collectedCoverage.uri, partial1['collect_coverage']!['uri']); + expect(collectedCoverage.out, partial1['collect_coverage']!['out']); + expect(collectedCoverage.scopeOutput, + partial1['collect_coverage']!['scope-output']); + expect(collectedCoverage.resumeIsolates, + partial1['collect_coverage']!['resume-isolates']); + expect(collectedCoverage.functionCoverage, + partial1['collect_coverage']!['function-coverage']); + expect(collectedCoverage.includeDart, + partial1['collect_coverage']!['include-dart']); + expect(collectedCoverage.connectTimeout, + partial1['collect_coverage']!['connect-timeout']); + + // Verify format coverage yaml values + expect(formattedCoverage.lcov, partial1['format_coverage']!['lcov']); + expect( + formattedCoverage.verbose, partial1['format_coverage']!['verbose']); + expect(formattedCoverage.baseDirectory, + partial1['format_coverage']!['base-directory']); + expect(formattedCoverage.ignoreFiles, + partial1['format_coverage']!['ignore-files']); + expect(formattedCoverage.reportOn, + partial1['format_coverage']!['report-on']); + expect(formattedCoverage.prettyPrint, + partial1['format_coverage']!['pretty-print']); + expect(formattedCoverage.prettyPrintFunc, + partial1['format_coverage']!['pretty-print-func']); + + // Verify test with coverage yaml values + expect(testCoverage.packageName, + partial1['test_with_coverage']!['package-name']); + expect(testCoverage.port, + partial1['test_with_coverage']!['port'].toString()); + expect(testCoverage.scopeOutput, + partial1['test_with_coverage']!['scope-output']); + }); + + test('override default values with partial yaml values 2', () async { + final configuredOptions = CoverageOptionsProvider( + options: partial2, + ).coverageOptions; + + // Parse arguments with empty command line args + final collectedCoverage = + parseArgsCollectCoverage([], configuredOptions.collectCoverage); + final formattedCoverage = parseArgsFormatCoverage( + defaultInputArgs, configuredOptions.formatCoverage); + final testCoverage = await parseArgsTestWithCoverage( + [], configuredOptions.testWithCoverage); + + // Verify collect coverage yaml values + expect(collectedCoverage.uri, partial2['collect_coverage']!['uri']); + expect(collectedCoverage.scopeOutput, + partial2['collect_coverage']!['scope-output']); + expect(collectedCoverage.includeDart, + partial2['collect_coverage']!['include-dart']); + expect(collectedCoverage.branchCoverage, + partial2['collect_coverage']!['branch-coverage']); + expect(collectedCoverage.waitPaused, + partial2['collect_coverage']!['wait-paused']); + expect(collectedCoverage.connectTimeout, + partial2['collect_coverage']!['connect-timeout']); + + // Verify format coverage yaml values + expect(formattedCoverage.bazel, partial2['format_coverage']!['bazel']); + expect(formattedCoverage.checkIgnore, + partial2['format_coverage']!['check-ignore']); + expect(formattedCoverage.input, 'coverage.json'); + expect(formattedCoverage.output, partial2['format_coverage']!['out']); + expect(formattedCoverage.packagePath, + partial2['format_coverage']!['package']); + expect(formattedCoverage.reportOn, + partial2['format_coverage']!['report-on']); + expect( + formattedCoverage.sdkRoot, partial2['format_coverage']!['sdk-root']); + + // Verify test with coverage yaml values + expect(testCoverage.packageDir, + getPackageDir(partial2['test_with_coverage']!['package'] as String)); + expect(testCoverage.outDir, partial2['test_with_coverage']!['out']); + expect(testCoverage.port, + partial2['test_with_coverage']!['port'].toString()); + expect(testCoverage.testScript, partial2['test_with_coverage']!['test']); + expect(testCoverage.functionCoverage, + partial2['test_with_coverage']!['function-coverage']); + }); + }); + + test('override yaml values with command line args', () async { + final configuredOptions = CoverageOptionsProvider( + optionsFilePath: 'test/test_coverage_options/all_field.yaml', + ).coverageOptions; + + // Parse arguments with command line args + final collectedCoverage = parseArgsCollectCoverage([ + '--uri=http://localhost:8181/', + '--out=coverage.json', + '--scope-output=lib/', + '--connect-timeout=10', + '--resume-isolates', + '--no-wait-paused', + '--no-function-coverage', + '--branch-coverage', + ], configuredOptions.collectCoverage); + final formattedCoverage = parseArgsFormatCoverage([ + '--in=data.json', + '--out=out_test.info', + '--report-on=src/', + '--report-on=src2/', + '--ignore-files=bin/', + '--workers=4', + ], configuredOptions.formatCoverage); + final testCoverage = await parseArgsTestWithCoverage([ + '--package-name=test', + '--out=test_coverage.json', + '--port=2589', + '--test=test_test.dart', + '--function-coverage', + ], configuredOptions.testWithCoverage); + + // Verify collect coverage command line args + expect(collectedCoverage.uri, 'http://localhost:8181/'); + expect(collectedCoverage.out, 'coverage.json'); + expect(collectedCoverage.scopeOutput, ['lib/']); + expect(collectedCoverage.resumeIsolates, isTrue); + expect(collectedCoverage.waitPaused, isFalse); + expect(collectedCoverage.functionCoverage, isFalse); + expect(collectedCoverage.branchCoverage, isTrue); + expect(collectedCoverage.connectTimeout, 10); + + // Verify format coverage command line args + expect(formattedCoverage.input, 'data.json'); + expect(formattedCoverage.output, 'out_test.info'); + expect(formattedCoverage.reportOn, ['src/', 'src2/']); + expect(formattedCoverage.ignoreFiles, ['bin/']); + expect(formattedCoverage.workers, 4); + + // Verify test with coverage command line args + expect(testCoverage.packageName, 'test'); + expect(testCoverage.outDir, 'test_coverage.json'); + expect(testCoverage.port, '2589'); + expect(testCoverage.testScript, 'test_test.dart'); + expect(testCoverage.functionCoverage, isTrue); + }); + + test('verify default values with empty yaml file', () async { + final configuredOptions = CoverageOptionsProvider( + optionsFilePath: 'test/test_coverage_options/empty.yaml', + ).coverageOptions; + + // Parse arguments with empty command line args + final collectedCoverage = + parseArgsCollectCoverage([], configuredOptions.collectCoverage); + final formattedCoverage = parseArgsFormatCoverage( + defaultInputArgs, configuredOptions.formatCoverage); + final testCoverage = + await parseArgsTestWithCoverage([], configuredOptions.testWithCoverage); + + // Verify collect coverage defaults + expect(collectedCoverage.uri, 'http://127.0.0.1:8181/'); + expect(collectedCoverage.scopeOutput, isEmpty); + expect(collectedCoverage.resumeIsolates, isFalse); + expect(collectedCoverage.waitPaused, isFalse); + expect(collectedCoverage.functionCoverage, isFalse); + expect(collectedCoverage.branchCoverage, isFalse); + expect(collectedCoverage.connectTimeout, isNull); + expect(collectedCoverage.includeDart, isFalse); + expect(collectedCoverage.out, 'stdout'); + + // Verify format coverage defaults + expect(formattedCoverage.baseDirectory, '.'); + expect(formattedCoverage.bazel, isFalse); + expect(formattedCoverage.bazelWorkspace, ''); + expect(formattedCoverage.checkIgnore, isFalse); + expect(formattedCoverage.input, 'coverage.json'); + expect(formattedCoverage.lcov, isFalse); + expect(formattedCoverage.output, 'stdout'); + expect(formattedCoverage.packagePath, '.'); + expect(formattedCoverage.prettyPrint, isFalse); + expect(formattedCoverage.prettyPrintFunc, isFalse); + expect(formattedCoverage.prettyPrintBranch, isFalse); + expect(formattedCoverage.reportOn, isNull); + expect(formattedCoverage.ignoreFiles, isEmpty); + expect(formattedCoverage.sdkRoot, isNull); + expect(formattedCoverage.verbose, isFalse); + expect(formattedCoverage.workers, 1); + + // Verify test with coverage defaults + expect(testCoverage.packageDir, getPackageDir('.')); + expect(testCoverage.packageName, 'coverage'); + expect(testCoverage.outDir, 'coverage'); + expect(testCoverage.port, '8181'); + expect(testCoverage.testScript, 'test'); + expect(testCoverage.functionCoverage, isFalse); + expect(testCoverage.branchCoverage, isFalse); + expect(testCoverage.scopeOutput, isEmpty); + }); + + test('verify invalid yaml file', () async { + expect( + () => CoverageOptionsProvider( + optionsFilePath: 'test/test_coverage_options/partial_fields.dart', + ).coverageOptions, + throwsA(isA()), + ); + }); + + test('host, port, and uri for collect coverage', () { + var configuredOptions = CoverageOptionsProvider(options: { + 'collect_coverage': { + 'uri': 'http://127.0.0.1:8181/', + }, + }).coverageOptions; + + var collectedCoverage = parseArgsCollectCoverage([ + '--host=localhost', + '--port=8181', + ], configuredOptions.collectCoverage); + + expect(collectedCoverage.uri, 'http://localhost:8181/'); + + collectedCoverage = parseArgsCollectCoverage([ + '--uri=http://localhost:8181/', + ], configuredOptions.collectCoverage); + + expect(collectedCoverage.uri, 'http://localhost:8181/'); + + configuredOptions = CoverageOptionsProvider(options: { + 'collect_coverage': { + 'uri': 'http://127.0.0.1:8181/', + }, + }).coverageOptions; + + collectedCoverage = parseArgsCollectCoverage([ + '--uri=http://localhost:8181/', + ], configuredOptions.collectCoverage); + + expect(collectedCoverage.uri, 'http://localhost:8181/'); + + configuredOptions = CoverageOptionsProvider(options: { + 'collect_coverage': { + 'uri': 'http://test.com:8181/', + }, + }).coverageOptions; + + collectedCoverage = parseArgsCollectCoverage([ + '--host=localhost', + '--port=8181', + '--uri=http://127.0.0.1:8181/', + ], configuredOptions.collectCoverage); + + expect(collectedCoverage.uri, 'http://127.0.0.1:8181/'); + + configuredOptions = CoverageOptionsProvider(options: {}).coverageOptions; + + collectedCoverage = + parseArgsCollectCoverage([], configuredOptions.collectCoverage); + + expect(collectedCoverage.uri, 'http://127.0.0.1:8181/'); + }); +} 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..33aa688b3 --- /dev/null +++ b/pkgs/coverage/test/test_coverage_options/all_field.yaml @@ -0,0 +1,39 @@ +defaults: + collect_coverage: + uri: "http://127.0.0.1:8181/" + out: "coverage.json" + connect-timeout: 30 + scope-output: ["lib/"] + resume-isolates: false + include-dart: false + function-coverage: true + branch-coverage: false + wait-paused: true + + format_coverage: + base-directory: "." + bazel-workspace: null + in: "coverage.json" + out: "lcov.info" + package: "." + report-on: ["lib/", "bin/"] + ignore-files: ["test/"] + sdk-root: '.' + workers: 2 + check-ignore: false + bazel: false + verbose: true + lcov: true + pretty-print: false + pretty-print-func: false + pretty-print-branch: false + + test_with_coverage: + package: "." + package-name: "My Dart Package" + out: "test_coverage.json" + port: 8181 + test: "test" + scope-output: ["lib/src/"] + function-coverage: true + branch-coverage: false 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..7ce0c7bb8 --- /dev/null +++ b/pkgs/coverage/test/test_coverage_options/empty.yaml @@ -0,0 +1,4 @@ +defaults: + collect_coverage: + format_coverage: + run_and_collect: \ No newline at end of file diff --git a/pkgs/coverage/test/test_coverage_options/partial_fields.dart b/pkgs/coverage/test/test_coverage_options/partial_fields.dart new file mode 100644 index 000000000..67a730bd1 --- /dev/null +++ b/pkgs/coverage/test/test_coverage_options/partial_fields.dart @@ -0,0 +1,53 @@ +const partialFieldOptions = [ + { + 'collect_coverage': { + 'uri': 'http://127.0.0.1:9000/', + 'out': 'custom_coverage.json', + 'scope-output': ['lib/', 'test/'], + 'resume-isolates': true, + 'function-coverage': false, + 'include-dart': true, + 'connect-timeout': 20 + }, + 'format_coverage': { + 'lcov': false, + 'verbose': false, + 'base-directory': './src', + 'ignore-files': ['example/'], + 'report-on': ['lib/'], + 'pretty-print': true, + 'pretty-print-func': false + }, + 'test_with_coverage': { + 'package-name': 'Custom Dart Package', + 'port': 9000, + 'scope-output': ['lib/utils/'] + } + }, + { + 'collect_coverage': { + 'uri': 'http://127.0.0.1:8500/', + 'scope-output': ['lib/', 'tools/'], + 'include-dart': false, + 'branch-coverage': false, + 'wait-paused': false, + 'connect-timeout': 15 + }, + 'format_coverage': { + 'bazel': true, + 'check-ignore': true, + 'in': 'custom_coverage.json', + 'out': 'custom_lcov.info', + 'package': './packages', + 'report-on': ['src/', 'scripts/'], + 'sdk-root': './dart-sdk' + }, + 'test_with_coverage': { + 'package': './packages/custom_package', + 'out': 'custom_test_coverage.json', + 'port': 8500, + 'test': 'custom_test', + 'function-coverage': true + } + }, +]; diff --git a/pkgs/coverage/test/test_util.dart b/pkgs/coverage/test/test_util.dart index e0a3edbe7..a28a1b781 100644 --- a/pkgs/coverage/test/test_util.dart +++ b/pkgs/coverage/test/test_util.dart @@ -5,6 +5,9 @@ import 'dart:async'; import 'dart:io'; +import 'package:args/args.dart'; +import 'package:coverage/src/coverage_options.dart'; +import 'package:package_config/package_config.dart'; import 'package:path/path.dart' as p; import 'package:test/test.dart'; import 'package:test_process/test_process.dart'; @@ -89,3 +92,171 @@ extension ListTestExtension on List { }, ); } + +CollectCoverageOptions parseArgsCollectCoverage( + List arguments, CollectCoverageOptions defaultOptions) { + final parser = ArgParser() + ..addOption('host', abbr: 'H') + ..addOption('port', abbr: 'p') + ..addOption('uri', abbr: 'u') + ..addOption('out', abbr: 'o', defaultsTo: defaultOptions.out) + ..addOption('connect-timeout', + abbr: 't', defaultsTo: defaultOptions.connectTimeout?.toString()) + ..addMultiOption('scope-output', defaultsTo: defaultOptions.scopeOutput) + ..addFlag('wait-paused', abbr: 'w', defaultsTo: defaultOptions.waitPaused) + ..addFlag('resume-isolates', + abbr: 'r', defaultsTo: defaultOptions.resumeIsolates) + ..addFlag('include-dart', abbr: 'd', defaultsTo: defaultOptions.includeDart) + ..addFlag('function-coverage', + abbr: 'f', defaultsTo: defaultOptions.functionCoverage) + ..addFlag('branch-coverage', + abbr: 'b', defaultsTo: defaultOptions.branchCoverage) + ..addFlag('help', abbr: 'h', negatable: false); + + final args = parser.parse(arguments); + + String serviceUri; + if (args['uri'] == null && (args['host'] != null || args['port'] != null)) { + final host = args['host'] ?? defaultOptions.host; + final port = args['port'] ?? defaultOptions.port; + + serviceUri = 'http://$host:$port/'; + } else { + serviceUri = (args['uri'] ?? defaultOptions.uri) as String; + } + + return CollectCoverageOptions( + uri: serviceUri, + out: args['out'] as String, + connectTimeout: int.tryParse(args['connect-timeout'] as String? ?? ''), + scopeOutput: args['scope-output'] as List, + waitPaused: args['wait-paused'] as bool, + resumeIsolates: args['resume-isolates'] as bool, + includeDart: args['include-dart'] as bool, + functionCoverage: args['function-coverage'] as bool, + branchCoverage: args['branch-coverage'] as bool, + ); +} + +FormatCoverageOptions parseArgsFormatCoverage( + List arguments, FormatCoverageOptions defaultOptions) { + final parser = ArgParser() + ..addOption('sdk-root', abbr: 's', defaultsTo: defaultOptions.sdkRoot) + ..addOption('packages') + ..addOption('package', defaultsTo: defaultOptions.packagePath) + ..addOption('in', abbr: 'i', defaultsTo: defaultOptions.input) + ..addOption('out', abbr: 'o', defaultsTo: defaultOptions.output) + ..addMultiOption('report-on', defaultsTo: defaultOptions.reportOn) + ..addOption('workers', + abbr: 'j', defaultsTo: defaultOptions.workers.toString()) + ..addOption('bazel-workspace', defaultsTo: defaultOptions.bazelWorkspace) + ..addOption('base-directory', + abbr: 'b', defaultsTo: defaultOptions.baseDirectory) + ..addFlag('bazel', + defaultsTo: defaultOptions.bazel, + help: 'use Bazel-style path resolution') + ..addFlag('pretty-print', + abbr: 'r', defaultsTo: defaultOptions.prettyPrint, negatable: false) + ..addFlag('pretty-print-func', + abbr: 'f', defaultsTo: defaultOptions.prettyPrintFunc, negatable: false) + ..addFlag('pretty-print-branch', + negatable: false, defaultsTo: defaultOptions.prettyPrintBranch) + ..addFlag('lcov', + abbr: 'l', defaultsTo: defaultOptions.lcov, negatable: false) + ..addFlag('verbose', + abbr: 'v', defaultsTo: defaultOptions.verbose, negatable: false) + ..addFlag('check-ignore', + abbr: 'c', defaultsTo: defaultOptions.checkIgnore, negatable: false) + ..addMultiOption('ignore-files', defaultsTo: defaultOptions.ignoreFiles) + ..addFlag('help', abbr: 'h', negatable: false); + + final args = parser.parse(arguments); + + if (args['in'] == null) throw ArgumentError('Missing required argument: in'); + + return FormatCoverageOptions( + baseDirectory: args['base-directory'] as String?, + bazel: args['bazel'] as bool, + bazelWorkspace: args['bazel-workspace'] as String, + checkIgnore: args['check-ignore'] as bool, + input: args['in'] as String, + lcov: args['lcov'] as bool, + output: args['out'] as String, + packagePath: args['package'] as String, + prettyPrint: args['lcov'] as bool ? false : args['pretty-print'] as bool, + prettyPrintFunc: args['pretty-print-func'] as bool, + prettyPrintBranch: args['pretty-print-branch'] as bool, + reportOn: (args['report-on'] as List).isNotEmpty + ? args['report-on'] as List + : null, + ignoreFiles: args['ignore-files'] as List, + sdkRoot: args['sdk-root'] as String?, + verbose: args['verbose'] as bool, + workers: int.parse(args['workers'] as String), + ); +} + +Future parseArgsTestWithCoverage( + List arguments, TestWithCoverageOptions defaultOptions) async { + final parser = ArgParser() + ..addOption( + 'package', + defaultsTo: defaultOptions.packageDir, + ) + ..addOption( + 'package-name', + defaultsTo: defaultOptions.packageName, + ) + ..addOption('port', defaultsTo: defaultOptions.port) + ..addOption( + 'out', + defaultsTo: defaultOptions.outDir, + abbr: 'o', + ) + ..addOption('test', defaultsTo: defaultOptions.testScript) + ..addFlag( + 'function-coverage', + abbr: 'f', + defaultsTo: defaultOptions.functionCoverage, + ) + ..addFlag( + 'branch-coverage', + abbr: 'b', + defaultsTo: defaultOptions.branchCoverage, + ) + ..addMultiOption('scope-output', defaultsTo: defaultOptions.scopeOutput) + ..addFlag('help', abbr: 'h', negatable: false); + + final args = parser.parse(arguments); + + final packageDir = p.canonicalize(args['package'] as String); + if (!FileSystemEntity.isDirectorySync(packageDir)) { + ArgumentError('Invalid package directory: $packageDir'); + } + + final packageName = (args['package-name'] as String?) ?? + await _packageNameFromConfig(packageDir); + if (packageName == null) { + ArgumentError('Could not determine package name'); + } + + return TestWithCoverageOptions( + packageDir: packageDir, + packageName: packageName, + outDir: (args['out'] as String?) ?? 'coverage', + port: args['port'] as String, + testScript: args['test'] as String, + functionCoverage: args['function-coverage'] as bool, + branchCoverage: args['branch-coverage'] as bool, + scopeOutput: args['scope-output'] as List, + ); +} + +Future _packageNameFromConfig(String packageDir) async { + final config = await findPackageConfig(Directory(packageDir)); + return config?.packageOf(Uri.directory(packageDir))?.name; +} + +String getPackageDir(final String package) { + return p.canonicalize(package); +} From 9cd8dce100a6b2b5c2a35993e6e8346756f1de41 Mon Sep 17 00:00:00 2001 From: Dhruv Maradiya Date: Tue, 31 Dec 2024 20:00:12 +0530 Subject: [PATCH 03/12] Bump version to 1.11.2 and add support for specifying coverage flags via YAML file --- pkgs/coverage/CHANGELOG.md | 4 ++++ pkgs/coverage/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/pkgs/coverage/CHANGELOG.md b/pkgs/coverage/CHANGELOG.md index 685c1c0a3..d641416d1 100644 --- a/pkgs/coverage/CHANGELOG.md +++ b/pkgs/coverage/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.11.2 + +- 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/pubspec.yaml b/pkgs/coverage/pubspec.yaml index db193a3a7..0e9d518e5 100644 --- a/pkgs/coverage/pubspec.yaml +++ b/pkgs/coverage/pubspec.yaml @@ -1,5 +1,5 @@ name: coverage -version: 1.11.1 +version: 1.11.2 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 From 949917c8b33fc81b03df8734c2a5aabf0a516c7f Mon Sep 17 00:00:00 2001 From: Dhruv Maradiya Date: Tue, 7 Jan 2025 19:17:23 +0530 Subject: [PATCH 04/12] feat: update dependencies and refactor coverage options handling using cli_config library --- pkgs/coverage/bin/collect_coverage.dart | 26 +- pkgs/coverage/bin/format_coverage.dart | 6 +- pkgs/coverage/bin/test_with_coverage.dart | 98 +++--- pkgs/coverage/lib/src/coverage_options.dart | 347 +++++++------------- pkgs/coverage/lib/src/util.dart | 33 -- pkgs/coverage/lib/src/yaml_merger.dart | 60 ---- pkgs/coverage/pubspec.yaml | 2 +- 7 files changed, 189 insertions(+), 383 deletions(-) delete mode 100644 pkgs/coverage/lib/src/yaml_merger.dart diff --git a/pkgs/coverage/bin/collect_coverage.dart b/pkgs/coverage/bin/collect_coverage.dart index 086ff3a81..bb30b9580 100644 --- a/pkgs/coverage/bin/collect_coverage.dart +++ b/pkgs/coverage/bin/collect_coverage.dart @@ -18,8 +18,7 @@ Future main(List arguments) async { print('${rec.level.name}: ${rec.time}: ${rec.message}'); }); - final defaultOptions = - CoverageOptionsProvider().coverageOptions.collectCoverage; + final defaultOptions = CoverageOptionsProvider().coverageOptions; final options = _parseArgs(arguments, defaultOptions); await Chain.capture(() async { final coverage = await collect(options.serviceUri, options.resume, @@ -61,17 +60,20 @@ class Options { final Set scopedOutput; } -Options _parseArgs( - List arguments, CollectCoverageOptions defaultOptions) { +Options _parseArgs(List arguments, CoverageOptions defaultOptions) { final parser = ArgParser() ..addOption('host', - abbr: 'H', help: 'remote VM host. DEPRECATED: use --uri') + abbr: 'H', + help: 'remote VM host. DEPRECATED: use --uri', + defaultsTo: '127.0.0.1') ..addOption('port', - abbr: 'p', help: 'remote VM port. DEPRECATED: use --uri') + abbr: 'p', + help: 'remote VM port. DEPRECATED: use --uri', + defaultsTo: '8181') ..addOption('uri', abbr: 'u', help: 'VM observatory service URI') ..addOption('out', abbr: 'o', - defaultsTo: defaultOptions.out, + defaultsTo: defaultOptions.output, help: 'output: may be file or stdout') ..addOption('connect-timeout', defaultsTo: defaultOptions.connectTimeout?.toString(), @@ -123,17 +125,13 @@ Options _parseArgs( } Uri serviceUri; - if (args['uri'] == null && (args['host'] != null || args['port'] != null)) { + if (args['uri'] == null) { // TODO(cbracken) eliminate --host and --port support when VM defaults to // requiring an auth token. Estimated for Dart SDK 1.22. - - final host = args['host'] ?? defaultOptions.host; - final port = args['port'] ?? defaultOptions.port; - - serviceUri = Uri.parse('http://$host:$port/'); + serviceUri = Uri.parse('http://${args['host']}:${args['port']}/'); } else { try { - serviceUri = Uri.parse((args['uri'] ?? defaultOptions.uri) as String); + serviceUri = Uri.parse(args['uri'] as String); } on FormatException { fail('Invalid service URI specified: ${args['uri']}'); } diff --git a/pkgs/coverage/bin/format_coverage.dart b/pkgs/coverage/bin/format_coverage.dart index 4b79e05c7..4edbc0246 100644 --- a/pkgs/coverage/bin/format_coverage.dart +++ b/pkgs/coverage/bin/format_coverage.dart @@ -52,8 +52,7 @@ class Environment { } Future main(List arguments) async { - final defaultOptions = - CoverageOptionsProvider().coverageOptions.formatCoverage; + final defaultOptions = CoverageOptionsProvider().coverageOptions; final env = parseArgs(arguments, defaultOptions); final files = filesToProcess(env.input); @@ -132,8 +131,7 @@ Future main(List arguments) async { /// Checks the validity of the provided arguments. Does not initialize actual /// processing. -Environment parseArgs( - List arguments, FormatCoverageOptions defaultOptions) { +Environment parseArgs(List arguments, CoverageOptions defaultOptions) { final parser = ArgParser(); parser diff --git a/pkgs/coverage/bin/test_with_coverage.dart b/pkgs/coverage/bin/test_with_coverage.dart index e2e1e1458..9d0c009fc 100644 --- a/pkgs/coverage/bin/test_with_coverage.dart +++ b/pkgs/coverage/bin/test_with_coverage.dart @@ -51,48 +51,45 @@ void _watchExitSignal(ProcessSignal signal) { }); } -ArgParser _createArgParser(TestWithCoverageOptions defaultOptions) => - ArgParser() - ..addOption( - 'package', - help: 'Root directory of the package to test.', - defaultsTo: defaultOptions.packageDir, - ) - ..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: defaultOptions.port) - ..addOption( - 'out', - defaultsTo: defaultOptions.outDir, - abbr: 'o', - help: 'Output directory. Defaults to /coverage.', - ) - ..addOption('test', - help: 'Test script to run.', defaultsTo: defaultOptions.testScript) - ..addFlag( - 'function-coverage', - abbr: 'f', - defaultsTo: defaultOptions.functionCoverage, - help: 'Collect function coverage info.', - ) - ..addFlag( - 'branch-coverage', - abbr: 'b', - 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.') - ..addFlag('help', abbr: 'h', negatable: false, help: 'Show this help.'); +ArgParser _createArgParser(CoverageOptions defaultOptions) => ArgParser() + ..addOption( + 'package', + help: 'Root directory of the package to test.', + defaultsTo: defaultOptions.packageName, + ) + ..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.') + ..addOption( + 'out', + defaultsTo: defaultOptions.output, + abbr: 'o', + help: 'Output directory. Defaults to /coverage.', + ) + ..addOption('test', + help: 'Test script to run.', defaultsTo: defaultOptions.testScript) + ..addFlag( + 'function-coverage', + abbr: 'f', + defaultsTo: defaultOptions.functionCoverage, + help: 'Collect function coverage info.', + ) + ..addFlag( + 'branch-coverage', + abbr: 'b', + 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.') + ..addFlag('help', abbr: 'h', negatable: false, help: 'Show this help.'); class Flags { Flags( @@ -119,7 +116,7 @@ class Flags { } Future _parseArgs( - List arguments, TestWithCoverageOptions defaultOptions) async { + List arguments, CoverageOptions defaultOptions) async { final parser = _createArgParser(defaultOptions); final args = parser.parse(arguments); @@ -175,10 +172,21 @@ ${parser.usage} } Future main(List arguments) async { - final defaultOptions = - CoverageOptionsProvider().coverageOptions.testWithCoverage; + final defaultOptions = CoverageOptionsProvider().coverageOptions; final flags = await _parseArgs(arguments, defaultOptions); + print('Flags: '); + print(' packageDir: ${flags.packageDir}'); + print(' packageName: ${flags.packageName}'); + print(' outDir: ${flags.outDir}'); + print(' port: ${flags.port}'); + print(' testScript: ${flags.testScript}'); + print(' functionCoverage: ${flags.functionCoverage}'); + print(' branchCoverage: ${flags.branchCoverage}'); + print(' scopeOutput: ${flags.scopeOutput}'); + print(' rest: ${flags.rest}'); + print(''); + 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 index 6f10634a4..30db2fe8e 100644 --- a/pkgs/coverage/lib/src/coverage_options.dart +++ b/pkgs/coverage/lib/src/coverage_options.dart @@ -1,41 +1,84 @@ import 'dart:io'; +import 'package:cli_config/cli_config.dart'; -import 'package:yaml/yaml.dart'; - -import 'util.dart'; -import 'yaml_merger.dart'; - -class CollectCoverageOptions { - const CollectCoverageOptions({ - required this.uri, - required this.out, - required this.connectTimeout, +class CoverageOptions { + const CoverageOptions({ + required this.output, + this.connectTimeout, required this.scopeOutput, required this.waitPaused, required this.resumeIsolates, required this.includeDart, required this.functionCoverage, required this.branchCoverage, + this.sdkRoot, + required this.packagePath, + this.input, + this.reportOn, + required this.workers, + required this.bazelWorkspace, + this.baseDirectory, + required this.bazel, + required this.prettyPrint, + required this.prettyPrintFunc, + required this.prettyPrintBranch, + required this.lcov, + required this.checkIgnore, + required this.ignoreFiles, + this.packageName, + required this.testScript, + required this.verbose, }); - /// Creates a [CollectCoverageOptions] instance from YAML configuration. - factory CollectCoverageOptions.fromYaml(YamlMap yaml) => - CollectCoverageOptions( - uri: YamlUtils.getString(yaml, 'uri') as String, - out: YamlUtils.getString(yaml, 'out') as String, - connectTimeout: YamlUtils.getInt(yaml, 'connect-timeout'), - scopeOutput: YamlUtils.getStringList(yaml, 'scope-output'), - waitPaused: YamlUtils.getBool(yaml, 'wait-paused') ?? false, - resumeIsolates: YamlUtils.getBool(yaml, 'resume-isolates') ?? false, - includeDart: YamlUtils.getBool(yaml, 'include-dart') ?? false, - functionCoverage: YamlUtils.getBool(yaml, 'function-coverage') ?? false, - branchCoverage: YamlUtils.getBool(yaml, 'branch-coverage') ?? false, - ); + factory CoverageOptions.fromConfig( + Config options, CoverageOptions defaultOptions) { + return CoverageOptions( + output: options.optionalString('out') ?? defaultOptions.output, + connectTimeout: options.optionalInt('connect_timeout') ?? + defaultOptions.connectTimeout, + scopeOutput: options.optionalStringList('scope_output') ?? + defaultOptions.scopeOutput, + waitPaused: + options.optionalBool('wait_paused') ?? defaultOptions.waitPaused, + resumeIsolates: options.optionalBool('resume_isolates') ?? + defaultOptions.resumeIsolates, + includeDart: + options.optionalBool('include_dart') ?? defaultOptions.includeDart, + functionCoverage: options.optionalBool('function_coverage') ?? + defaultOptions.functionCoverage, + branchCoverage: options.optionalBool('branch_coverage') ?? + defaultOptions.branchCoverage, + sdkRoot: options.optionalString('sdk_root') ?? defaultOptions.sdkRoot, + packagePath: + options.optionalString('package') ?? defaultOptions.packagePath, + input: options.optionalString('in') ?? defaultOptions.input, + reportOn: + options.optionalStringList('report_on') ?? defaultOptions.reportOn, + workers: options.optionalInt('workers') ?? defaultOptions.workers, + bazelWorkspace: options.optionalString('bazel_workspace') ?? + defaultOptions.bazelWorkspace, + baseDirectory: options.optionalString('base_directory') ?? + defaultOptions.baseDirectory, + bazel: options.optionalBool('bazel') ?? defaultOptions.bazel, + prettyPrint: + options.optionalBool('pretty_print') ?? defaultOptions.prettyPrint, + prettyPrintFunc: options.optionalBool('pretty_print_func') ?? + defaultOptions.prettyPrintFunc, + prettyPrintBranch: options.optionalBool('pretty_print_branch') ?? + defaultOptions.prettyPrintBranch, + lcov: options.optionalBool('lcov') ?? defaultOptions.lcov, + checkIgnore: + options.optionalBool('check_ignore') ?? defaultOptions.checkIgnore, + ignoreFiles: options.optionalStringList('ignore_files') ?? + defaultOptions.ignoreFiles, + packageName: + options.optionalString('package_name') ?? defaultOptions.packageName, + testScript: options.optionalString('test') ?? defaultOptions.testScript, + verbose: options.optionalBool('verbose') ?? defaultOptions.verbose, + ); + } - final String host = '127.0.0.1'; - final String port = '8181'; - final String uri; - final String out; + final String output; final int? connectTimeout; final List scopeOutput; final bool waitPaused; @@ -43,225 +86,77 @@ class CollectCoverageOptions { final bool includeDart; final bool functionCoverage; final bool branchCoverage; -} - -class FormatCoverageOptions { - const FormatCoverageOptions({ - required this.baseDirectory, - required this.bazel, - required this.bazelWorkspace, - required this.checkIgnore, - required this.input, - required this.lcov, - required this.output, - required this.packagePath, - required this.prettyPrint, - required this.prettyPrintFunc, - required this.prettyPrintBranch, - required this.reportOn, - required this.ignoreFiles, - required this.sdkRoot, - required this.verbose, - required this.workers, - }); - - /// Creates a [FormatCoverageOptions] instance from YAML configuration. - factory FormatCoverageOptions.fromYaml(YamlMap yaml) => FormatCoverageOptions( - baseDirectory: YamlUtils.getString(yaml, 'base-directory'), - bazel: YamlUtils.getBool(yaml, 'bazel') ?? false, - bazelWorkspace: YamlUtils.getString(yaml, 'bazel-workspace') as String, - checkIgnore: YamlUtils.getBool(yaml, 'check-ignore') ?? false, - input: YamlUtils.getString(yaml, 'in'), - lcov: YamlUtils.getBool(yaml, 'lcov') ?? false, - output: YamlUtils.getString(yaml, 'out') as String, - packagePath: YamlUtils.getString(yaml, 'package') as String, - prettyPrint: YamlUtils.getBool(yaml, 'pretty-print') ?? false, - prettyPrintFunc: YamlUtils.getBool(yaml, 'pretty-print-func') ?? false, - prettyPrintBranch: - YamlUtils.getBool(yaml, 'pretty-print-branch') ?? false, - reportOn: YamlUtils.getStringList(yaml, 'report-on'), - ignoreFiles: YamlUtils.getStringList(yaml, 'ignore-files'), - sdkRoot: YamlUtils.getString(yaml, 'sdk-root'), - verbose: YamlUtils.getBool(yaml, 'verbose') ?? false, - workers: YamlUtils.getInt(yaml, 'workers') as int, - ); - + final String? sdkRoot; + final String packagePath; + final String? input; + final List? reportOn; + final int workers; + final String bazelWorkspace; final String? baseDirectory; final bool bazel; - final String bazelWorkspace; - final bool checkIgnore; - final String? input; - final bool lcov; - final String output; - final String packagePath; final bool prettyPrint; final bool prettyPrintFunc; final bool prettyPrintBranch; - final List? reportOn; - final List? ignoreFiles; - final String? sdkRoot; - final bool verbose; - final int workers; -} - -class TestWithCoverageOptions { - const TestWithCoverageOptions({ - required this.packageDir, - required this.packageName, - required this.outDir, - required this.port, - required this.testScript, - required this.functionCoverage, - required this.branchCoverage, - required this.scopeOutput, - }); - - /// Creates a [TestWithCoverageOptions] instance from YAML configuration. - factory TestWithCoverageOptions.fromYaml(YamlMap yaml) => - TestWithCoverageOptions( - packageDir: YamlUtils.getString(yaml, 'package') as String, - packageName: YamlUtils.getString(yaml, 'package-name'), - outDir: YamlUtils.getString(yaml, 'out'), - port: YamlUtils.getString(yaml, 'port') as String, - testScript: YamlUtils.getString(yaml, 'test') as String, - functionCoverage: YamlUtils.getBool(yaml, 'function-coverage') ?? false, - branchCoverage: YamlUtils.getBool(yaml, 'branch-coverage') ?? false, - scopeOutput: YamlUtils.getStringList(yaml, 'scope-output'), - ); - - final String packageDir; + final bool lcov; + final bool checkIgnore; + final List ignoreFiles; final String? packageName; - final String? outDir; - final String port; final String testScript; - final bool functionCoverage; - final bool branchCoverage; - final List scopeOutput; -} - -class CoverageOptions { - const CoverageOptions({ - required this.collectCoverage, - required this.formatCoverage, - required this.testWithCoverage, - }); - - final CollectCoverageOptions collectCoverage; - final FormatCoverageOptions formatCoverage; - final TestWithCoverageOptions testWithCoverage; + final bool verbose; } -abstract class CoverageOptionsProvider { - factory CoverageOptionsProvider( - {final String? optionsFilePath, final Map? options}) { - YamlMap coverageOptions; - - final defaultOptionsProvider = DefaultCoverageOptionsProvider(); - - if (options != null) { - coverageOptions = YamlMap.wrap(options); - } else { - final optionsFile = - _getOptionsFile(optionsFilePath ?? CoverageOptionsProvider.filePath); +class CoverageOptionsProvider { + CoverageOptionsProvider({ + String? filePath, + }) { + final file = _getOptionsFile(filePath ?? CoverageOptionsProvider.filePath); + final fileContents = file?.readAsStringSync(); - if (optionsFile == null) { - return defaultOptionsProvider; - } - coverageOptions = _getOptionsFromFile(optionsFile); - } + final isFileEmpty = fileContents?.isEmpty ?? false; - final mergedOptions = defaultOptionsProvider.merge( - defaultOptionsProvider.options, - coverageOptions, + // Pass null to fileContents if the file is empty + final options = Config.fromConfigFileContents( + fileContents: isFileEmpty ? null : fileContents, + fileSourceUri: file?.uri, ); - return CustomCoverageOptionsProvider(mergedOptions); + coverageOptions = CoverageOptions.fromConfig(options, defaultOptions); } - CoverageOptionsProvider._(); + late final CoverageOptions coverageOptions; - static const filePath = 'coverage_options.yaml'; + static const filePath = 'coverage.yaml'; static File? _getOptionsFile(String filePath) { final file = File(filePath); return file.existsSync() ? file : null; } - static YamlMap _getOptionsFromFile(File file) { - final yamlString = file.readAsStringSync(); - final content = loadYaml(yamlString); - - final options = content is YamlMap ? content['defaults'] : YamlMap.wrap({}); - - return options is YamlMap ? options : YamlMap.wrap({}); - } - - YamlMap merge(YamlMap defaults, YamlMap overrides) => - Merger().mergeMap(defaults, overrides); - - YamlMap get options; - CoverageOptions get coverageOptions => CoverageOptions( - collectCoverage: CollectCoverageOptions.fromYaml( - options['collect_coverage'] as YamlMap), - formatCoverage: FormatCoverageOptions.fromYaml( - options['format_coverage'] as YamlMap), - testWithCoverage: TestWithCoverageOptions.fromYaml( - options['test_with_coverage'] as YamlMap), - ); -} - -class DefaultCoverageOptionsProvider extends CoverageOptionsProvider { - DefaultCoverageOptionsProvider() : super._(); - - @override - YamlMap get options => defaultOptions; - - static final defaultOptions = YamlMap.wrap({ - 'collect_coverage': { - 'uri': 'http://127.0.0.1:8181/', - 'out': 'stdout', - 'connect-timeout': null, - 'scope-output': List.empty(), - 'wait-paused': false, - 'resume-isolates': false, - 'include-dart': false, - 'function-coverage': false, - 'branch-coverage': false, - }, - 'format_coverage': { - 'base-directory': '.', - 'bazel': false, - 'bazel-workspace': '', - 'check-ignore': false, - 'in': null, - 'lcov': false, - 'out': 'stdout', - 'package': '.', - 'pretty-print': false, - 'pretty-print-func': false, - 'pretty-print-branch': false, - 'report-on': null, - 'ignore-files': List.empty(), - 'sdk-root': null, - 'verbose': false, - 'workers': 1, - }, - 'test_with_coverage': { - 'package': '.', - 'package-name': null, - 'port': '8181', - 'out': null, - 'test': 'test', - 'function-coverage': false, - 'branch-coverage': false, - 'scope-output': List.empty(), - } - }); -} - -class CustomCoverageOptionsProvider extends CoverageOptionsProvider { - CustomCoverageOptionsProvider(this.options) : super._(); - - @override - final YamlMap options; + static const defaultOptions = CoverageOptions( + output: 'stdout', + connectTimeout: null, + scopeOutput: [], + waitPaused: false, + resumeIsolates: false, + includeDart: false, + functionCoverage: false, + branchCoverage: false, + sdkRoot: null, + packagePath: '.', + input: null, + reportOn: null, + workers: 1, + bazelWorkspace: '', + baseDirectory: '.', + bazel: false, + prettyPrint: false, + prettyPrintFunc: false, + prettyPrintBranch: false, + lcov: false, + checkIgnore: false, + ignoreFiles: [], + packageName: null, + testScript: 'test', + verbose: false, + ); } diff --git a/pkgs/coverage/lib/src/util.dart b/pkgs/coverage/lib/src/util.dart index 7585a11a1..cc7f58425 100644 --- a/pkgs/coverage/lib/src/util.dart +++ b/pkgs/coverage/lib/src/util.dart @@ -7,7 +7,6 @@ import 'dart:convert'; import 'dart:io'; import 'package:vm_service/vm_service.dart'; -import 'package:yaml/yaml.dart'; // TODO(cbracken) make generic /// Retries the specified function with the specified interval and returns @@ -185,35 +184,3 @@ Future serviceUriFromProcess(Stream procStdout) { Future> getAllIsolates(VmService service) async => (await service.getVM()).isolates ?? []; - -abstract class YamlUtils { - /// Safely extracts a boolean value from YAML. - static bool? getBool(YamlMap yaml, String key) => switch (yaml[key]) { - final YamlScalar scalar => scalar.value as bool?, - final bool value => value, - _ => null, - }; - - /// Safely extracts a list of strings from YAML. - static List getStringList(YamlMap yaml, String key) { - final value = yaml[key]; - if (value is YamlList) { - return value.cast(); - } - return const []; - } - - /// Safely extracts a string value from YAML. - static String? getString(YamlMap yaml, String key) => switch (yaml[key]) { - final YamlScalar scalar => scalar.value?.toString(), - final String value => value, - _ => null, - }; - - /// Safely extracts an integer value from YAML. - static int? getInt(YamlMap yaml, String key) => switch (yaml[key]) { - final YamlScalar scalar => scalar.value as int?, - final int value => value, - _ => null, - }; -} diff --git a/pkgs/coverage/lib/src/yaml_merger.dart b/pkgs/coverage/lib/src/yaml_merger.dart deleted file mode 100644 index 1116033b3..000000000 --- a/pkgs/coverage/lib/src/yaml_merger.dart +++ /dev/null @@ -1,60 +0,0 @@ -import 'dart:collection'; - -import 'package:yaml/yaml.dart'; - -/// Merges two maps (of yaml) with simple override semantics, suitable for -/// merging two maps where one map defines default values that are added to -/// (and possibly overridden) by an overriding map. -class Merger { - /// Merges a default [o1] with an overriding object [o2]. - /// - /// * lists are overridden. - /// * maps are merged recursively. - /// * if map values cannot be merged, the overriding value is taken. - /// - YamlNode merge(YamlNode o1, YamlNode o2) { - if (o1 is YamlMap && o2 is YamlMap) { - return mergeMap(o1, o2); - } - - // Default to override, unless the overriding value is `null`. - if (o2.value == null) { - return o1; - } - return o2; - } - - /// Merge maps (recursively). - YamlMap mergeMap(YamlMap m1, YamlMap m2) { - final Map merged = - HashMap(); // equals: _equals, hashCode: _hashCode - m1.nodeMap.forEach((k, v) { - merged[k] = v; - }); - m2.nodeMap.forEach((k, v) { - final value = k.value; - final mergedKey = - merged.keys.firstWhere((key) => key.value == value, orElse: () => k) - as YamlScalar; - final o1 = merged[mergedKey]; - if (o1 != null) { - merged[mergedKey] = merge(o1, v); - } else { - merged[mergedKey] = v; - } - }); - - final result = {}; - - merged.forEach((key, value) { - result[key.value] = value; - }); - - return YamlMap.wrap(result); - } -} - -extension YamlMapExtensions on YamlMap { - /// Return [nodes] as a Map with [YamlNode] keys. - Map get nodeMap => nodes.cast(); -} diff --git a/pkgs/coverage/pubspec.yaml b/pkgs/coverage/pubspec.yaml index 0e9d518e5..a8f4f84b5 100644 --- a/pkgs/coverage/pubspec.yaml +++ b/pkgs/coverage/pubspec.yaml @@ -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 @@ -17,7 +18,6 @@ dependencies: source_maps: ^0.10.10 stack_trace: ^1.10.0 vm_service: '>=12.0.0 <16.0.0' - yaml: ^3.1.3 dev_dependencies: benchmark_harness: ^2.2.0 From eebc68f66f555a7d00407944f4b7b53a113b32e6 Mon Sep 17 00:00:00 2001 From: Dhruv Maradiya Date: Tue, 7 Jan 2025 19:17:35 +0530 Subject: [PATCH 05/12] test: refactor test cases to align with updated coverage options handling --- .../test/collect_coverage_config_test.dart | 426 +++++++----------- .../test/test_coverage_options/all_field.yaml | 64 +-- .../test_coverage_options/partial_fields.dart | 53 --- .../partial_fields1.yaml | 18 + .../partial_fields2.yaml | 20 + pkgs/coverage/test/test_util.dart | 98 ++-- 6 files changed, 285 insertions(+), 394 deletions(-) delete mode 100644 pkgs/coverage/test/test_coverage_options/partial_fields.dart create mode 100644 pkgs/coverage/test/test_coverage_options/partial_fields1.yaml create mode 100644 pkgs/coverage/test/test_coverage_options/partial_fields2.yaml diff --git a/pkgs/coverage/test/collect_coverage_config_test.dart b/pkgs/coverage/test/collect_coverage_config_test.dart index a8656ca0a..9b1ee671e 100644 --- a/pkgs/coverage/test/collect_coverage_config_test.dart +++ b/pkgs/coverage/test/collect_coverage_config_test.dart @@ -1,132 +1,121 @@ import 'package:coverage/src/coverage_options.dart'; import 'package:test/test.dart'; -import 'test_coverage_options/partial_fields.dart'; import 'test_util.dart'; void main() { final defaultInputArgs = ['--in=coverage.json']; - test('defaults when no yaml or command-line args provided', () async { + group('defaults when no yaml or command-line args provided', () { // Setup - final defaults = DefaultCoverageOptionsProvider().coverageOptions; - final configuredOptions = CoverageOptionsProvider().coverageOptions; - - // Parse arguments with empty command line args - final collectedCoverage = - parseArgsCollectCoverage([], configuredOptions.collectCoverage); - final formattedCoverage = parseArgsFormatCoverage( - defaultInputArgs, configuredOptions.formatCoverage); - final testCoverage = - await parseArgsTestWithCoverage([], configuredOptions.testWithCoverage); - - // Verify collect coverage defaults - expect(collectedCoverage.uri, defaults.collectCoverage.uri, - reason: 'URI should match default'); - expect(collectedCoverage.scopeOutput, defaults.collectCoverage.scopeOutput, - reason: 'scope output should match default'); - expect(collectedCoverage.resumeIsolates, - defaults.collectCoverage.resumeIsolates, - reason: 'resume isolates should match default'); - expect(collectedCoverage.waitPaused, defaults.collectCoverage.waitPaused, - reason: 'wait paused should match default'); - expect(collectedCoverage.functionCoverage, - defaults.collectCoverage.functionCoverage, - reason: 'function coverage should match default'); - expect(collectedCoverage.branchCoverage, - defaults.collectCoverage.branchCoverage, - reason: 'branch coverage should match default'); - expect(collectedCoverage.connectTimeout, - defaults.collectCoverage.connectTimeout, - reason: 'connect timeout should match default'); - expect(collectedCoverage.includeDart, defaults.collectCoverage.includeDart, - reason: 'include dart should match default'); - expect(collectedCoverage.out, defaults.collectCoverage.out, - reason: 'output path should match default'); + final defaults = CoverageOptionsProvider.defaultOptions; + + test('collect coverage', () { + final collectedCoverage = parseArgsCollectCoverage([], defaults); + + expect(collectedCoverage.output, defaults.output); + expect(collectedCoverage.scopeOutput, defaults.scopeOutput); + expect(collectedCoverage.resumeIsolates, defaults.resumeIsolates); + expect(collectedCoverage.waitPaused, defaults.waitPaused); + expect(collectedCoverage.functionCoverage, defaults.functionCoverage); + expect(collectedCoverage.branchCoverage, defaults.branchCoverage); + expect(collectedCoverage.connectTimeout, defaults.connectTimeout); + expect(collectedCoverage.includeDart, defaults.includeDart); + expect(collectedCoverage.baseDirectory, defaults.baseDirectory); + expect(collectedCoverage.bazel, defaults.bazel); + expect(collectedCoverage.bazelWorkspace, defaults.bazelWorkspace); + expect(collectedCoverage.checkIgnore, defaults.checkIgnore); + expect(collectedCoverage.ignoreFiles, defaults.ignoreFiles); + expect(collectedCoverage.input, defaults.input); + expect(collectedCoverage.lcov, defaults.lcov); + expect(collectedCoverage.packagePath, defaults.packagePath); + expect(collectedCoverage.prettyPrint, defaults.prettyPrint); + expect(collectedCoverage.prettyPrintFunc, defaults.prettyPrintFunc); + expect(collectedCoverage.prettyPrintBranch, defaults.prettyPrintBranch); + expect(collectedCoverage.reportOn, defaults.reportOn); + expect(collectedCoverage.sdkRoot, defaults.sdkRoot); + expect(collectedCoverage.verbose, defaults.verbose); + expect(collectedCoverage.workers, defaults.workers); + }); - // Verify format coverage defaults - expect( - formattedCoverage.baseDirectory, defaults.formatCoverage.baseDirectory, - reason: 'base directory should match default'); - expect(formattedCoverage.bazel, defaults.formatCoverage.bazel, - reason: 'bazel flag should match default'); - expect(formattedCoverage.bazelWorkspace, - defaults.formatCoverage.bazelWorkspace, - reason: 'bazel workspace should match default'); - expect(formattedCoverage.checkIgnore, defaults.formatCoverage.checkIgnore, - reason: 'check ignore should match default'); - expect(formattedCoverage.input, 'coverage.json', - reason: 'input should be coverage.json'); - expect(formattedCoverage.lcov, defaults.formatCoverage.lcov, - reason: 'lcov flag should match default'); - expect(formattedCoverage.output, defaults.formatCoverage.output, - reason: 'output path should match default'); - expect(formattedCoverage.packagePath, defaults.formatCoverage.packagePath, - reason: 'package path should match default'); - expect(formattedCoverage.prettyPrint, defaults.formatCoverage.prettyPrint, - reason: 'pretty print should match default'); - expect(formattedCoverage.prettyPrintFunc, - defaults.formatCoverage.prettyPrintFunc, - reason: 'pretty print func should match default'); - expect(formattedCoverage.prettyPrintBranch, - defaults.formatCoverage.prettyPrintBranch, - reason: 'pretty print branch should match default'); - expect(formattedCoverage.reportOn, isNull, - reason: 'report on should be null'); - expect(formattedCoverage.ignoreFiles, defaults.formatCoverage.ignoreFiles, - reason: 'ignore files should match default'); - expect(formattedCoverage.sdkRoot, defaults.formatCoverage.sdkRoot, - reason: 'sdk root should match default'); - expect(formattedCoverage.verbose, defaults.formatCoverage.verbose, - reason: 'verbose flag should match default'); - expect(formattedCoverage.workers, defaults.formatCoverage.workers, - reason: 'workers count should match default'); + test('format coverage', () { + final formattedCoverage = + parseArgsFormatCoverage(defaultInputArgs, defaults); + + expect(formattedCoverage.output, defaults.output); + expect(formattedCoverage.scopeOutput, defaults.scopeOutput); + expect(formattedCoverage.resumeIsolates, defaults.resumeIsolates); + expect(formattedCoverage.waitPaused, defaults.waitPaused); + expect(formattedCoverage.functionCoverage, defaults.functionCoverage); + expect(formattedCoverage.branchCoverage, defaults.branchCoverage); + expect(formattedCoverage.connectTimeout, defaults.connectTimeout); + expect(formattedCoverage.includeDart, defaults.includeDart); + expect(formattedCoverage.baseDirectory, defaults.baseDirectory); + expect(formattedCoverage.bazel, defaults.bazel); + expect(formattedCoverage.bazelWorkspace, defaults.bazelWorkspace); + expect(formattedCoverage.checkIgnore, defaults.checkIgnore); + expect(formattedCoverage.ignoreFiles, defaults.ignoreFiles); + expect(formattedCoverage.input, 'coverage.json'); + expect(formattedCoverage.lcov, defaults.lcov); + expect(formattedCoverage.packagePath, defaults.packagePath); + expect(formattedCoverage.prettyPrint, defaults.prettyPrint); + expect(formattedCoverage.prettyPrintFunc, defaults.prettyPrintFunc); + expect(formattedCoverage.prettyPrintBranch, defaults.prettyPrintBranch); + expect(formattedCoverage.reportOn, defaults.reportOn); + expect(formattedCoverage.sdkRoot, defaults.sdkRoot); + expect(formattedCoverage.verbose, defaults.verbose); + expect(formattedCoverage.workers, defaults.workers); + }); - // Verify test with coverage defaults - expect(testCoverage.packageDir, - getPackageDir(defaults.testWithCoverage.packageDir), - reason: 'package directory should match current directory'); - expect(testCoverage.packageName, 'coverage', - reason: 'package name should be coverage'); - expect(testCoverage.outDir, 'coverage', - reason: 'output directory should be coverage'); - expect(testCoverage.port, defaults.testWithCoverage.port, - reason: 'port should match default'); - expect(testCoverage.testScript, defaults.testWithCoverage.testScript, - reason: 'test script should match default'); - expect(testCoverage.functionCoverage, - defaults.testWithCoverage.functionCoverage, - reason: 'function coverage should match default'); - expect( - testCoverage.branchCoverage, defaults.testWithCoverage.branchCoverage, - reason: 'branch coverage should match default'); - expect(testCoverage.scopeOutput, defaults.testWithCoverage.scopeOutput, - reason: 'scope output should match default'); + test('test with coverage', () async { + final testCoverage = await parseArgsTestWithCoverage([], defaults); + + expect(testCoverage.output, defaults.output); + expect(testCoverage.scopeOutput, defaults.scopeOutput); + expect(testCoverage.resumeIsolates, defaults.resumeIsolates); + expect(testCoverage.waitPaused, defaults.waitPaused); + expect(testCoverage.functionCoverage, defaults.functionCoverage); + expect(testCoverage.branchCoverage, defaults.branchCoverage); + expect(testCoverage.connectTimeout, defaults.connectTimeout); + expect(testCoverage.includeDart, defaults.includeDart); + expect(testCoverage.baseDirectory, defaults.baseDirectory); + expect(testCoverage.bazel, defaults.bazel); + expect(testCoverage.bazelWorkspace, defaults.bazelWorkspace); + expect(testCoverage.checkIgnore, defaults.checkIgnore); + expect(testCoverage.ignoreFiles, defaults.ignoreFiles); + expect(testCoverage.input, defaults.input); + expect(testCoverage.lcov, defaults.lcov); + expect(testCoverage.packagePath, getPackageDir(defaults.packagePath)); + expect(testCoverage.prettyPrint, defaults.prettyPrint); + expect(testCoverage.prettyPrintFunc, defaults.prettyPrintFunc); + expect(testCoverage.prettyPrintBranch, defaults.prettyPrintBranch); + expect(testCoverage.reportOn, defaults.reportOn); + expect(testCoverage.sdkRoot, defaults.sdkRoot); + expect(testCoverage.verbose, defaults.verbose); + expect(testCoverage.workers, defaults.workers); + }); }); test('uses yaml values when all values are provided in yaml file', () async { final configuredOptions = CoverageOptionsProvider( - optionsFilePath: 'test/test_coverage_options/all_field.yaml', + filePath: 'test/test_coverage_options/all_field.yaml', ).coverageOptions; // Parse arguments with empty command line args - final collectedCoverage = - parseArgsCollectCoverage([], configuredOptions.collectCoverage); - final formattedCoverage = parseArgsFormatCoverage( - defaultInputArgs, configuredOptions.formatCoverage); - final testCoverage = - await parseArgsTestWithCoverage([], configuredOptions.testWithCoverage); + final collectedCoverage = parseArgsCollectCoverage([], configuredOptions); + final formattedCoverage = + parseArgsFormatCoverage(defaultInputArgs, configuredOptions); + final testCoverage = await parseArgsTestWithCoverage([], configuredOptions); // Verify collect coverage yaml values - expect(collectedCoverage.uri, 'http://127.0.0.1:8181/'); - expect(collectedCoverage.scopeOutput, ['lib/']); + expect(collectedCoverage.scopeOutput, ['lib', 'src']); expect(collectedCoverage.resumeIsolates, isFalse); expect(collectedCoverage.waitPaused, isTrue); expect(collectedCoverage.functionCoverage, isTrue); expect(collectedCoverage.branchCoverage, isFalse); expect(collectedCoverage.connectTimeout, 30); expect(collectedCoverage.includeDart, isFalse); - expect(collectedCoverage.out, 'coverage.json'); + expect(collectedCoverage.output, 'coverage'); // Verify format coverage yaml values expect(formattedCoverage.baseDirectory, '.'); @@ -135,137 +124,101 @@ void main() { expect(formattedCoverage.checkIgnore, isFalse); expect(formattedCoverage.input, 'coverage.json'); expect(formattedCoverage.lcov, isTrue); - expect(formattedCoverage.output, 'lcov.info'); + expect(formattedCoverage.output, 'coverage'); expect(formattedCoverage.packagePath, '.'); expect(formattedCoverage.prettyPrint, isFalse); expect(formattedCoverage.prettyPrintFunc, isFalse); expect(formattedCoverage.prettyPrintBranch, isFalse); - expect(formattedCoverage.reportOn, ['lib/', 'bin/']); - expect(formattedCoverage.ignoreFiles, ['test/']); + expect(formattedCoverage.reportOn, ['lib', 'bin']); + expect(formattedCoverage.ignoreFiles, ['test']); expect(formattedCoverage.sdkRoot, '.'); expect(formattedCoverage.verbose, isTrue); expect(formattedCoverage.workers, 2); // Verify test with coverage yaml values - expect(testCoverage.packageDir, getPackageDir('.')); + expect(testCoverage.packagePath, getPackageDir('.')); expect(testCoverage.packageName, 'My Dart Package'); - expect(testCoverage.outDir, 'test_coverage.json'); - expect(testCoverage.port, '8181'); + expect(testCoverage.output, 'coverage'); expect(testCoverage.testScript, 'test'); expect(testCoverage.functionCoverage, isTrue); expect(testCoverage.branchCoverage, isFalse); - expect(testCoverage.scopeOutput, ['lib/src/']); + expect(testCoverage.scopeOutput, ['lib', 'src']); }); group('partial yaml configuration', () { - final partial1 = partialFieldOptions[0]; - final partial2 = partialFieldOptions[1]; - test('override default values with partial yaml values 1', () async { final configuredOptions = CoverageOptionsProvider( - options: partial1, + filePath: 'test/test_coverage_options/partial_fields1.yaml', ).coverageOptions; // Parse arguments with empty command line args - final collectedCoverage = - parseArgsCollectCoverage([], configuredOptions.collectCoverage); - final formattedCoverage = parseArgsFormatCoverage( - defaultInputArgs, configuredOptions.formatCoverage); - final testCoverage = await parseArgsTestWithCoverage( - [], configuredOptions.testWithCoverage); + final collectedCoverage = parseArgsCollectCoverage([], configuredOptions); + final formattedCoverage = + parseArgsFormatCoverage(defaultInputArgs, configuredOptions); + final testCoverage = + await parseArgsTestWithCoverage([], configuredOptions); // Verify collect coverage yaml values - expect(collectedCoverage.uri, partial1['collect_coverage']!['uri']); - expect(collectedCoverage.out, partial1['collect_coverage']!['out']); - expect(collectedCoverage.scopeOutput, - partial1['collect_coverage']!['scope-output']); - expect(collectedCoverage.resumeIsolates, - partial1['collect_coverage']!['resume-isolates']); - expect(collectedCoverage.functionCoverage, - partial1['collect_coverage']!['function-coverage']); - expect(collectedCoverage.includeDart, - partial1['collect_coverage']!['include-dart']); - expect(collectedCoverage.connectTimeout, - partial1['collect_coverage']!['connect-timeout']); + expect(collectedCoverage.output, 'custom_coverage.json'); + expect(collectedCoverage.scopeOutput, ['lib', 'test']); + expect(collectedCoverage.resumeIsolates, isTrue); + expect(collectedCoverage.functionCoverage, isFalse); + expect(collectedCoverage.includeDart, isTrue); + expect(collectedCoverage.connectTimeout, 20); // Verify format coverage yaml values - expect(formattedCoverage.lcov, partial1['format_coverage']!['lcov']); - expect( - formattedCoverage.verbose, partial1['format_coverage']!['verbose']); - expect(formattedCoverage.baseDirectory, - partial1['format_coverage']!['base-directory']); - expect(formattedCoverage.ignoreFiles, - partial1['format_coverage']!['ignore-files']); - expect(formattedCoverage.reportOn, - partial1['format_coverage']!['report-on']); - expect(formattedCoverage.prettyPrint, - partial1['format_coverage']!['pretty-print']); - expect(formattedCoverage.prettyPrintFunc, - partial1['format_coverage']!['pretty-print-func']); + expect(formattedCoverage.lcov, isFalse); + expect(formattedCoverage.verbose, isFalse); + expect(formattedCoverage.baseDirectory, 'src'); + expect(formattedCoverage.ignoreFiles, ['example']); + expect(formattedCoverage.reportOn, ['lib']); + expect(formattedCoverage.prettyPrint, isTrue); + expect(formattedCoverage.prettyPrintFunc, isFalse); // Verify test with coverage yaml values - expect(testCoverage.packageName, - partial1['test_with_coverage']!['package-name']); - expect(testCoverage.port, - partial1['test_with_coverage']!['port'].toString()); - expect(testCoverage.scopeOutput, - partial1['test_with_coverage']!['scope-output']); + expect(testCoverage.packageName, 'Custom Dart Package'); + expect(testCoverage.scopeOutput, ['lib', 'test']); }); test('override default values with partial yaml values 2', () async { final configuredOptions = CoverageOptionsProvider( - options: partial2, + filePath: 'test/test_coverage_options/partial_fields2.yaml', ).coverageOptions; // Parse arguments with empty command line args - final collectedCoverage = - parseArgsCollectCoverage([], configuredOptions.collectCoverage); - final formattedCoverage = parseArgsFormatCoverage( - defaultInputArgs, configuredOptions.formatCoverage); - final testCoverage = await parseArgsTestWithCoverage( - [], configuredOptions.testWithCoverage); + final collectedCoverage = parseArgsCollectCoverage([], configuredOptions); + final formattedCoverage = parseArgsFormatCoverage([], configuredOptions); + final testCoverage = + await parseArgsTestWithCoverage([], configuredOptions); // Verify collect coverage yaml values - expect(collectedCoverage.uri, partial2['collect_coverage']!['uri']); - expect(collectedCoverage.scopeOutput, - partial2['collect_coverage']!['scope-output']); - expect(collectedCoverage.includeDart, - partial2['collect_coverage']!['include-dart']); - expect(collectedCoverage.branchCoverage, - partial2['collect_coverage']!['branch-coverage']); - expect(collectedCoverage.waitPaused, - partial2['collect_coverage']!['wait-paused']); - expect(collectedCoverage.connectTimeout, - partial2['collect_coverage']!['connect-timeout']); + expect(collectedCoverage.scopeOutput, ['lib', 'tools']); + expect(collectedCoverage.includeDart, isFalse); + expect(collectedCoverage.branchCoverage, isFalse); + expect(collectedCoverage.waitPaused, isFalse); + expect(collectedCoverage.connectTimeout, 15); + expect(collectedCoverage.functionCoverage, isTrue); // Verify format coverage yaml values - expect(formattedCoverage.bazel, partial2['format_coverage']!['bazel']); - expect(formattedCoverage.checkIgnore, - partial2['format_coverage']!['check-ignore']); - expect(formattedCoverage.input, 'coverage.json'); - expect(formattedCoverage.output, partial2['format_coverage']!['out']); - expect(formattedCoverage.packagePath, - partial2['format_coverage']!['package']); - expect(formattedCoverage.reportOn, - partial2['format_coverage']!['report-on']); - expect( - formattedCoverage.sdkRoot, partial2['format_coverage']!['sdk-root']); + expect(formattedCoverage.bazel, isTrue); + expect(formattedCoverage.checkIgnore, isTrue); + expect(formattedCoverage.input, 'custom_coverage.json'); + expect(formattedCoverage.output, 'custom_lcov.info'); + expect(formattedCoverage.packagePath, '.'); + expect(formattedCoverage.reportOn, ['src', 'scripts']); + expect(formattedCoverage.sdkRoot, './dart-sdk'); // Verify test with coverage yaml values - expect(testCoverage.packageDir, - getPackageDir(partial2['test_with_coverage']!['package'] as String)); - expect(testCoverage.outDir, partial2['test_with_coverage']!['out']); - expect(testCoverage.port, - partial2['test_with_coverage']!['port'].toString()); - expect(testCoverage.testScript, partial2['test_with_coverage']!['test']); - expect(testCoverage.functionCoverage, - partial2['test_with_coverage']!['function-coverage']); + expect(testCoverage.packageName, 'coverage'); + expect(testCoverage.output, 'custom_lcov.info'); + expect(testCoverage.testScript, 'custom_test'); + expect(testCoverage.functionCoverage, isTrue); }); }); test('override yaml values with command line args', () async { final configuredOptions = CoverageOptionsProvider( - optionsFilePath: 'test/test_coverage_options/all_field.yaml', + filePath: 'test/test_coverage_options/all_field.yaml', ).coverageOptions; // Parse arguments with command line args @@ -278,7 +231,7 @@ void main() { '--no-wait-paused', '--no-function-coverage', '--branch-coverage', - ], configuredOptions.collectCoverage); + ], configuredOptions); final formattedCoverage = parseArgsFormatCoverage([ '--in=data.json', '--out=out_test.info', @@ -286,18 +239,17 @@ void main() { '--report-on=src2/', '--ignore-files=bin/', '--workers=4', - ], configuredOptions.formatCoverage); + ], configuredOptions); final testCoverage = await parseArgsTestWithCoverage([ '--package-name=test', '--out=test_coverage.json', '--port=2589', '--test=test_test.dart', '--function-coverage', - ], configuredOptions.testWithCoverage); + ], configuredOptions); // Verify collect coverage command line args - expect(collectedCoverage.uri, 'http://localhost:8181/'); - expect(collectedCoverage.out, 'coverage.json'); + expect(collectedCoverage.output, 'coverage.json'); expect(collectedCoverage.scopeOutput, ['lib/']); expect(collectedCoverage.resumeIsolates, isTrue); expect(collectedCoverage.waitPaused, isFalse); @@ -314,27 +266,23 @@ void main() { // Verify test with coverage command line args expect(testCoverage.packageName, 'test'); - expect(testCoverage.outDir, 'test_coverage.json'); - expect(testCoverage.port, '2589'); + expect(testCoverage.output, 'test_coverage.json'); expect(testCoverage.testScript, 'test_test.dart'); expect(testCoverage.functionCoverage, isTrue); }); test('verify default values with empty yaml file', () async { final configuredOptions = CoverageOptionsProvider( - optionsFilePath: 'test/test_coverage_options/empty.yaml', + filePath: 'test/test_coverage_options/empty.yaml', ).coverageOptions; // Parse arguments with empty command line args - final collectedCoverage = - parseArgsCollectCoverage([], configuredOptions.collectCoverage); - final formattedCoverage = parseArgsFormatCoverage( - defaultInputArgs, configuredOptions.formatCoverage); - final testCoverage = - await parseArgsTestWithCoverage([], configuredOptions.testWithCoverage); + final collectedCoverage = parseArgsCollectCoverage([], configuredOptions); + final formattedCoverage = + parseArgsFormatCoverage(defaultInputArgs, configuredOptions); + final testCoverage = await parseArgsTestWithCoverage([], configuredOptions); // Verify collect coverage defaults - expect(collectedCoverage.uri, 'http://127.0.0.1:8181/'); expect(collectedCoverage.scopeOutput, isEmpty); expect(collectedCoverage.resumeIsolates, isFalse); expect(collectedCoverage.waitPaused, isFalse); @@ -342,7 +290,7 @@ void main() { expect(collectedCoverage.branchCoverage, isFalse); expect(collectedCoverage.connectTimeout, isNull); expect(collectedCoverage.includeDart, isFalse); - expect(collectedCoverage.out, 'stdout'); + expect(collectedCoverage.output, 'stdout'); // Verify format coverage defaults expect(formattedCoverage.baseDirectory, '.'); @@ -363,76 +311,12 @@ void main() { expect(formattedCoverage.workers, 1); // Verify test with coverage defaults - expect(testCoverage.packageDir, getPackageDir('.')); + expect(testCoverage.packagePath, getPackageDir('.')); expect(testCoverage.packageName, 'coverage'); - expect(testCoverage.outDir, 'coverage'); - expect(testCoverage.port, '8181'); + expect(testCoverage.output, 'stdout'); expect(testCoverage.testScript, 'test'); expect(testCoverage.functionCoverage, isFalse); expect(testCoverage.branchCoverage, isFalse); expect(testCoverage.scopeOutput, isEmpty); }); - - test('verify invalid yaml file', () async { - expect( - () => CoverageOptionsProvider( - optionsFilePath: 'test/test_coverage_options/partial_fields.dart', - ).coverageOptions, - throwsA(isA()), - ); - }); - - test('host, port, and uri for collect coverage', () { - var configuredOptions = CoverageOptionsProvider(options: { - 'collect_coverage': { - 'uri': 'http://127.0.0.1:8181/', - }, - }).coverageOptions; - - var collectedCoverage = parseArgsCollectCoverage([ - '--host=localhost', - '--port=8181', - ], configuredOptions.collectCoverage); - - expect(collectedCoverage.uri, 'http://localhost:8181/'); - - collectedCoverage = parseArgsCollectCoverage([ - '--uri=http://localhost:8181/', - ], configuredOptions.collectCoverage); - - expect(collectedCoverage.uri, 'http://localhost:8181/'); - - configuredOptions = CoverageOptionsProvider(options: { - 'collect_coverage': { - 'uri': 'http://127.0.0.1:8181/', - }, - }).coverageOptions; - - collectedCoverage = parseArgsCollectCoverage([ - '--uri=http://localhost:8181/', - ], configuredOptions.collectCoverage); - - expect(collectedCoverage.uri, 'http://localhost:8181/'); - - configuredOptions = CoverageOptionsProvider(options: { - 'collect_coverage': { - 'uri': 'http://test.com:8181/', - }, - }).coverageOptions; - - collectedCoverage = parseArgsCollectCoverage([ - '--host=localhost', - '--port=8181', - '--uri=http://127.0.0.1:8181/', - ], configuredOptions.collectCoverage); - - expect(collectedCoverage.uri, 'http://127.0.0.1:8181/'); - - configuredOptions = CoverageOptionsProvider(options: {}).coverageOptions; - - collectedCoverage = - parseArgsCollectCoverage([], configuredOptions.collectCoverage); - - expect(collectedCoverage.uri, 'http://127.0.0.1:8181/'); - }); } diff --git a/pkgs/coverage/test/test_coverage_options/all_field.yaml b/pkgs/coverage/test/test_coverage_options/all_field.yaml index 33aa688b3..08e234060 100644 --- a/pkgs/coverage/test/test_coverage_options/all_field.yaml +++ b/pkgs/coverage/test/test_coverage_options/all_field.yaml @@ -1,39 +1,25 @@ -defaults: - collect_coverage: - uri: "http://127.0.0.1:8181/" - out: "coverage.json" - connect-timeout: 30 - scope-output: ["lib/"] - resume-isolates: false - include-dart: false - function-coverage: true - branch-coverage: false - wait-paused: true - - format_coverage: - base-directory: "." - bazel-workspace: null - in: "coverage.json" - out: "lcov.info" - package: "." - report-on: ["lib/", "bin/"] - ignore-files: ["test/"] - sdk-root: '.' - workers: 2 - check-ignore: false - bazel: false - verbose: true - lcov: true - pretty-print: false - pretty-print-func: false - pretty-print-branch: false - - test_with_coverage: - package: "." - package-name: "My Dart Package" - out: "test_coverage.json" - port: 8181 - test: "test" - scope-output: ["lib/src/"] - function-coverage: true - branch-coverage: false +out: "coverage" +connect-timeout: 30 +scope-output: ["lib", "src"] +resume-isolates: false +include-dart: false +function-coverage: true +branch-coverage: false +wait-paused: true +base-directory: "." +bazel: false +bazel-workspace: null +in: "coverage.json" +package: "." +package-name: "My Dart Package" +report-on: ["lib", "bin"] +ignore-files: ["test"] +sdk-root: "." +workers: 2 +check-ignore: false +verbose: true +lcov: true +pretty-print: false +pretty-print-func: false +pretty-print-branch: false +test: "test" diff --git a/pkgs/coverage/test/test_coverage_options/partial_fields.dart b/pkgs/coverage/test/test_coverage_options/partial_fields.dart deleted file mode 100644 index 67a730bd1..000000000 --- a/pkgs/coverage/test/test_coverage_options/partial_fields.dart +++ /dev/null @@ -1,53 +0,0 @@ -const partialFieldOptions = [ - { - 'collect_coverage': { - 'uri': 'http://127.0.0.1:9000/', - 'out': 'custom_coverage.json', - 'scope-output': ['lib/', 'test/'], - 'resume-isolates': true, - 'function-coverage': false, - 'include-dart': true, - 'connect-timeout': 20 - }, - 'format_coverage': { - 'lcov': false, - 'verbose': false, - 'base-directory': './src', - 'ignore-files': ['example/'], - 'report-on': ['lib/'], - 'pretty-print': true, - 'pretty-print-func': false - }, - 'test_with_coverage': { - 'package-name': 'Custom Dart Package', - 'port': 9000, - 'scope-output': ['lib/utils/'] - } - }, - { - 'collect_coverage': { - 'uri': 'http://127.0.0.1:8500/', - 'scope-output': ['lib/', 'tools/'], - 'include-dart': false, - 'branch-coverage': false, - 'wait-paused': false, - 'connect-timeout': 15 - }, - 'format_coverage': { - 'bazel': true, - 'check-ignore': true, - 'in': 'custom_coverage.json', - 'out': 'custom_lcov.info', - 'package': './packages', - 'report-on': ['src/', 'scripts/'], - 'sdk-root': './dart-sdk' - }, - 'test_with_coverage': { - 'package': './packages/custom_package', - 'out': 'custom_test_coverage.json', - 'port': 8500, - 'test': 'custom_test', - 'function-coverage': true - } - }, -]; 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..023b09c1a --- /dev/null +++ b/pkgs/coverage/test/test_coverage_options/partial_fields1.yaml @@ -0,0 +1,18 @@ +out: 'custom_coverage.json' +scope-output: + - 'lib' + - 'test' +resume-isolates: true +function-coverage: false +include-dart: true +connect-timeout: 20 +lcov: false +verbose: false +base-directory: 'src' +ignore-files: + - 'example' +report-on: + - 'lib' +pretty-print: true +pretty-print-func: false +package-name: 'Custom Dart Package' \ No newline at end of file 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..e0580a973 --- /dev/null +++ b/pkgs/coverage/test/test_coverage_options/partial_fields2.yaml @@ -0,0 +1,20 @@ +scope-output: + - 'lib' + - 'tools' +include-dart: false +branch-coverage: false +wait-paused: false +connect-timeout: 15 + +bazel: true +check-ignore: true +in: 'custom_coverage.json' +out: 'custom_lcov.info' +package: '.' +report-on: + - 'src' + - 'scripts' +sdk-root: './dart-sdk' + +test: 'custom_test' +function-coverage: true \ No newline at end of file diff --git a/pkgs/coverage/test/test_util.dart b/pkgs/coverage/test/test_util.dart index a28a1b781..86854d7e5 100644 --- a/pkgs/coverage/test/test_util.dart +++ b/pkgs/coverage/test/test_util.dart @@ -93,13 +93,13 @@ extension ListTestExtension on List { ); } -CollectCoverageOptions parseArgsCollectCoverage( - List arguments, CollectCoverageOptions defaultOptions) { +CoverageOptions parseArgsCollectCoverage( + List arguments, CoverageOptions defaultOptions) { final parser = ArgParser() ..addOption('host', abbr: 'H') ..addOption('port', abbr: 'p') ..addOption('uri', abbr: 'u') - ..addOption('out', abbr: 'o', defaultsTo: defaultOptions.out) + ..addOption('out', abbr: 'o', defaultsTo: defaultOptions.output) ..addOption('connect-timeout', abbr: 't', defaultsTo: defaultOptions.connectTimeout?.toString()) ..addMultiOption('scope-output', defaultsTo: defaultOptions.scopeOutput) @@ -115,31 +115,40 @@ CollectCoverageOptions parseArgsCollectCoverage( final args = parser.parse(arguments); - String serviceUri; - if (args['uri'] == null && (args['host'] != null || args['port'] != null)) { - final host = args['host'] ?? defaultOptions.host; - final port = args['port'] ?? defaultOptions.port; - - serviceUri = 'http://$host:$port/'; - } else { - serviceUri = (args['uri'] ?? defaultOptions.uri) as String; - } - - return CollectCoverageOptions( - uri: serviceUri, - out: args['out'] as String, - connectTimeout: int.tryParse(args['connect-timeout'] as String? ?? ''), + return CoverageOptions( + output: args['out'] as String, + connectTimeout: args['connect-timeout'] == null + ? defaultOptions.connectTimeout + : int.parse(args['connect-timeout'] as String), scopeOutput: args['scope-output'] as List, waitPaused: args['wait-paused'] as bool, resumeIsolates: args['resume-isolates'] as bool, includeDart: args['include-dart'] as bool, functionCoverage: args['function-coverage'] as bool, branchCoverage: args['branch-coverage'] as bool, + bazel: defaultOptions.bazel, + bazelWorkspace: defaultOptions.bazelWorkspace, + baseDirectory: defaultOptions.baseDirectory, + checkIgnore: defaultOptions.checkIgnore, + ignoreFiles: defaultOptions.ignoreFiles, + input: defaultOptions.input, + lcov: defaultOptions.lcov, + packagePath: defaultOptions.packagePath, + packageName: defaultOptions.packageName, + prettyPrint: defaultOptions.prettyPrint, + prettyPrintBranch: defaultOptions.prettyPrintBranch, + prettyPrintFunc: defaultOptions.prettyPrintFunc, + reportOn: defaultOptions.reportOn, + sdkRoot: defaultOptions.sdkRoot, + testScript: defaultOptions.testScript, + verbose: defaultOptions.verbose, + workers: defaultOptions.workers, + ); } -FormatCoverageOptions parseArgsFormatCoverage( - List arguments, FormatCoverageOptions defaultOptions) { +CoverageOptions parseArgsFormatCoverage( + List arguments, CoverageOptions defaultOptions) { final parser = ArgParser() ..addOption('sdk-root', abbr: 's', defaultsTo: defaultOptions.sdkRoot) ..addOption('packages') @@ -174,7 +183,7 @@ FormatCoverageOptions parseArgsFormatCoverage( if (args['in'] == null) throw ArgumentError('Missing required argument: in'); - return FormatCoverageOptions( + return CoverageOptions( baseDirectory: args['base-directory'] as String?, bazel: args['bazel'] as bool, bazelWorkspace: args['bazel-workspace'] as String, @@ -193,24 +202,33 @@ FormatCoverageOptions parseArgsFormatCoverage( sdkRoot: args['sdk-root'] as String?, verbose: args['verbose'] as bool, workers: int.parse(args['workers'] as String), + branchCoverage: defaultOptions.branchCoverage, + functionCoverage: defaultOptions.functionCoverage, + connectTimeout: defaultOptions.connectTimeout, + includeDart: defaultOptions.includeDart, + packageName: defaultOptions.packageName, + resumeIsolates: defaultOptions.resumeIsolates, + scopeOutput: defaultOptions.scopeOutput, + waitPaused: defaultOptions.waitPaused, + testScript: defaultOptions.testScript, ); } -Future parseArgsTestWithCoverage( - List arguments, TestWithCoverageOptions defaultOptions) async { +Future parseArgsTestWithCoverage( + List arguments, CoverageOptions defaultOptions) async { final parser = ArgParser() ..addOption( 'package', - defaultsTo: defaultOptions.packageDir, + defaultsTo: defaultOptions.packagePath, ) ..addOption( 'package-name', defaultsTo: defaultOptions.packageName, ) - ..addOption('port', defaultsTo: defaultOptions.port) + ..addOption('port') ..addOption( 'out', - defaultsTo: defaultOptions.outDir, + defaultsTo: defaultOptions.output, abbr: 'o', ) ..addOption('test', defaultsTo: defaultOptions.testScript) @@ -234,21 +252,39 @@ Future parseArgsTestWithCoverage( ArgumentError('Invalid package directory: $packageDir'); } - final packageName = (args['package-name'] as String?) ?? + final packageName = + args['package-name'] ?? await _packageNameFromConfig(packageDir); if (packageName == null) { ArgumentError('Could not determine package name'); } - return TestWithCoverageOptions( - packageDir: packageDir, - packageName: packageName, - outDir: (args['out'] as String?) ?? 'coverage', - port: args['port'] as String, + return CoverageOptions( + packagePath: packageDir, + packageName: packageName as String, + output: (args['out'] as String?) ?? 'coverage', testScript: args['test'] as String, functionCoverage: args['function-coverage'] as bool, branchCoverage: args['branch-coverage'] as bool, scopeOutput: args['scope-output'] as List, + bazel: defaultOptions.bazel, + bazelWorkspace: defaultOptions.bazelWorkspace, + baseDirectory: defaultOptions.baseDirectory, + checkIgnore: defaultOptions.checkIgnore, + connectTimeout: defaultOptions.connectTimeout, + ignoreFiles: defaultOptions.ignoreFiles, + includeDart: defaultOptions.includeDart, + input: defaultOptions.input, + lcov: defaultOptions.lcov, + prettyPrint: defaultOptions.prettyPrint, + prettyPrintBranch: defaultOptions.prettyPrintBranch, + prettyPrintFunc: defaultOptions.prettyPrintFunc, + reportOn: defaultOptions.reportOn, + resumeIsolates: defaultOptions.resumeIsolates, + sdkRoot: defaultOptions.sdkRoot, + verbose: defaultOptions.verbose, + waitPaused: defaultOptions.waitPaused, + workers: defaultOptions.workers, ); } From bb0eb603c1773df18ff63d71ad1a7be135ab8edd Mon Sep 17 00:00:00 2001 From: Dhruv Maradiya Date: Tue, 7 Jan 2025 20:41:45 +0530 Subject: [PATCH 06/12] test: optimized test cases --- .../test/collect_coverage_config_test.dart | 68 ++++--------------- .../test/test_coverage_options/empty.yaml | 4 -- 2 files changed, 14 insertions(+), 58 deletions(-) diff --git a/pkgs/coverage/test/collect_coverage_config_test.dart b/pkgs/coverage/test/collect_coverage_config_test.dart index 9b1ee671e..3a73af7f0 100644 --- a/pkgs/coverage/test/collect_coverage_config_test.dart +++ b/pkgs/coverage/test/collect_coverage_config_test.dart @@ -13,7 +13,6 @@ void main() { test('collect coverage', () { final collectedCoverage = parseArgsCollectCoverage([], defaults); - expect(collectedCoverage.output, defaults.output); expect(collectedCoverage.scopeOutput, defaults.scopeOutput); expect(collectedCoverage.resumeIsolates, defaults.resumeIsolates); expect(collectedCoverage.waitPaused, defaults.waitPaused); @@ -21,47 +20,26 @@ void main() { expect(collectedCoverage.branchCoverage, defaults.branchCoverage); expect(collectedCoverage.connectTimeout, defaults.connectTimeout); expect(collectedCoverage.includeDart, defaults.includeDart); - expect(collectedCoverage.baseDirectory, defaults.baseDirectory); - expect(collectedCoverage.bazel, defaults.bazel); - expect(collectedCoverage.bazelWorkspace, defaults.bazelWorkspace); - expect(collectedCoverage.checkIgnore, defaults.checkIgnore); - expect(collectedCoverage.ignoreFiles, defaults.ignoreFiles); - expect(collectedCoverage.input, defaults.input); - expect(collectedCoverage.lcov, defaults.lcov); - expect(collectedCoverage.packagePath, defaults.packagePath); - expect(collectedCoverage.prettyPrint, defaults.prettyPrint); - expect(collectedCoverage.prettyPrintFunc, defaults.prettyPrintFunc); - expect(collectedCoverage.prettyPrintBranch, defaults.prettyPrintBranch); - expect(collectedCoverage.reportOn, defaults.reportOn); - expect(collectedCoverage.sdkRoot, defaults.sdkRoot); - expect(collectedCoverage.verbose, defaults.verbose); - expect(collectedCoverage.workers, defaults.workers); + expect(collectedCoverage.output, defaults.output); }); test('format coverage', () { final formattedCoverage = parseArgsFormatCoverage(defaultInputArgs, defaults); - expect(formattedCoverage.output, defaults.output); - expect(formattedCoverage.scopeOutput, defaults.scopeOutput); - expect(formattedCoverage.resumeIsolates, defaults.resumeIsolates); - expect(formattedCoverage.waitPaused, defaults.waitPaused); - expect(formattedCoverage.functionCoverage, defaults.functionCoverage); - expect(formattedCoverage.branchCoverage, defaults.branchCoverage); - expect(formattedCoverage.connectTimeout, defaults.connectTimeout); - expect(formattedCoverage.includeDart, defaults.includeDart); expect(formattedCoverage.baseDirectory, defaults.baseDirectory); expect(formattedCoverage.bazel, defaults.bazel); expect(formattedCoverage.bazelWorkspace, defaults.bazelWorkspace); expect(formattedCoverage.checkIgnore, defaults.checkIgnore); - expect(formattedCoverage.ignoreFiles, defaults.ignoreFiles); expect(formattedCoverage.input, 'coverage.json'); expect(formattedCoverage.lcov, defaults.lcov); + expect(formattedCoverage.output, defaults.output); expect(formattedCoverage.packagePath, defaults.packagePath); expect(formattedCoverage.prettyPrint, defaults.prettyPrint); expect(formattedCoverage.prettyPrintFunc, defaults.prettyPrintFunc); expect(formattedCoverage.prettyPrintBranch, defaults.prettyPrintBranch); expect(formattedCoverage.reportOn, defaults.reportOn); + expect(formattedCoverage.ignoreFiles, defaults.ignoreFiles); expect(formattedCoverage.sdkRoot, defaults.sdkRoot); expect(formattedCoverage.verbose, defaults.verbose); expect(formattedCoverage.workers, defaults.workers); @@ -70,29 +48,13 @@ void main() { test('test with coverage', () async { final testCoverage = await parseArgsTestWithCoverage([], defaults); + expect(testCoverage.packagePath, getPackageDir(defaults.packagePath)); + expect(testCoverage.packageName, 'coverage'); expect(testCoverage.output, defaults.output); - expect(testCoverage.scopeOutput, defaults.scopeOutput); - expect(testCoverage.resumeIsolates, defaults.resumeIsolates); - expect(testCoverage.waitPaused, defaults.waitPaused); + expect(testCoverage.testScript, defaults.testScript); expect(testCoverage.functionCoverage, defaults.functionCoverage); expect(testCoverage.branchCoverage, defaults.branchCoverage); - expect(testCoverage.connectTimeout, defaults.connectTimeout); - expect(testCoverage.includeDart, defaults.includeDart); - expect(testCoverage.baseDirectory, defaults.baseDirectory); - expect(testCoverage.bazel, defaults.bazel); - expect(testCoverage.bazelWorkspace, defaults.bazelWorkspace); - expect(testCoverage.checkIgnore, defaults.checkIgnore); - expect(testCoverage.ignoreFiles, defaults.ignoreFiles); - expect(testCoverage.input, defaults.input); - expect(testCoverage.lcov, defaults.lcov); - expect(testCoverage.packagePath, getPackageDir(defaults.packagePath)); - expect(testCoverage.prettyPrint, defaults.prettyPrint); - expect(testCoverage.prettyPrintFunc, defaults.prettyPrintFunc); - expect(testCoverage.prettyPrintBranch, defaults.prettyPrintBranch); - expect(testCoverage.reportOn, defaults.reportOn); - expect(testCoverage.sdkRoot, defaults.sdkRoot); - expect(testCoverage.verbose, defaults.verbose); - expect(testCoverage.workers, defaults.workers); + expect(testCoverage.scopeOutput, defaults.scopeOutput); }); }); @@ -223,9 +185,8 @@ void main() { // Parse arguments with command line args final collectedCoverage = parseArgsCollectCoverage([ - '--uri=http://localhost:8181/', '--out=coverage.json', - '--scope-output=lib/', + '--scope-output=lib', '--connect-timeout=10', '--resume-isolates', '--no-wait-paused', @@ -235,22 +196,21 @@ void main() { final formattedCoverage = parseArgsFormatCoverage([ '--in=data.json', '--out=out_test.info', - '--report-on=src/', - '--report-on=src2/', - '--ignore-files=bin/', + '--report-on=src', + '--report-on=src2', + '--ignore-files=bin', '--workers=4', ], configuredOptions); final testCoverage = await parseArgsTestWithCoverage([ '--package-name=test', '--out=test_coverage.json', - '--port=2589', '--test=test_test.dart', '--function-coverage', ], configuredOptions); // Verify collect coverage command line args expect(collectedCoverage.output, 'coverage.json'); - expect(collectedCoverage.scopeOutput, ['lib/']); + expect(collectedCoverage.scopeOutput, ['lib']); expect(collectedCoverage.resumeIsolates, isTrue); expect(collectedCoverage.waitPaused, isFalse); expect(collectedCoverage.functionCoverage, isFalse); @@ -260,8 +220,8 @@ void main() { // Verify format coverage command line args expect(formattedCoverage.input, 'data.json'); expect(formattedCoverage.output, 'out_test.info'); - expect(formattedCoverage.reportOn, ['src/', 'src2/']); - expect(formattedCoverage.ignoreFiles, ['bin/']); + expect(formattedCoverage.reportOn, ['src', 'src2']); + expect(formattedCoverage.ignoreFiles, ['bin']); expect(formattedCoverage.workers, 4); // Verify test with coverage command line args diff --git a/pkgs/coverage/test/test_coverage_options/empty.yaml b/pkgs/coverage/test/test_coverage_options/empty.yaml index 7ce0c7bb8..e69de29bb 100644 --- a/pkgs/coverage/test/test_coverage_options/empty.yaml +++ b/pkgs/coverage/test/test_coverage_options/empty.yaml @@ -1,4 +0,0 @@ -defaults: - collect_coverage: - format_coverage: - run_and_collect: \ No newline at end of file From 781b2d77a670d15f709a58a38b7e666f8fcd4ceb Mon Sep 17 00:00:00 2001 From: Dhruv Maradiya Date: Tue, 7 Jan 2025 21:05:07 +0530 Subject: [PATCH 07/12] fix: set default VM service port to 8181 in test_with_coverage.dart --- pkgs/coverage/bin/test_with_coverage.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/coverage/bin/test_with_coverage.dart b/pkgs/coverage/bin/test_with_coverage.dart index 9d0c009fc..c0bb37789 100644 --- a/pkgs/coverage/bin/test_with_coverage.dart +++ b/pkgs/coverage/bin/test_with_coverage.dart @@ -63,7 +63,7 @@ ArgParser _createArgParser(CoverageOptions defaultOptions) => ArgParser() 'Deduced from --package if not provided.', defaultsTo: defaultOptions.packageName, ) - ..addOption('port', help: 'VM service port.') + ..addOption('port', help: 'VM service port.', defaultsTo: '8181') ..addOption( 'out', defaultsTo: defaultOptions.output, From c3cbd550b53ff3e131b365bc1640ed8e3e2b6e19 Mon Sep 17 00:00:00 2001 From: Dhruv Maradiya Date: Thu, 9 Jan 2025 14:14:07 +0530 Subject: [PATCH 08/12] refactor: streamline argument parsing in coverage scripts --- pkgs/coverage/bin/collect_coverage.dart | 10 ++--- pkgs/coverage/bin/format_coverage.dart | 10 +---- pkgs/coverage/lib/src/coverage_options.dart | 38 ----------------- .../test/collect_coverage_config_test.dart | 36 ---------------- .../test/test_coverage_options/all_field.yaml | 9 ---- .../partial_fields1.yaml | 4 -- .../partial_fields2.yaml | 5 --- pkgs/coverage/test/test_util.dart | 41 +------------------ 8 files changed, 6 insertions(+), 147 deletions(-) diff --git a/pkgs/coverage/bin/collect_coverage.dart b/pkgs/coverage/bin/collect_coverage.dart index bb30b9580..c2e122ea6 100644 --- a/pkgs/coverage/bin/collect_coverage.dart +++ b/pkgs/coverage/bin/collect_coverage.dart @@ -85,16 +85,12 @@ Options _parseArgs(List arguments, CoverageOptions defaultOptions) { 'the provided package path are considered') ..addFlag('wait-paused', abbr: 'w', - defaultsTo: defaultOptions.waitPaused, + defaultsTo: false, help: 'wait for all isolates to be paused before collecting coverage') ..addFlag('resume-isolates', - abbr: 'r', - defaultsTo: defaultOptions.resumeIsolates, - help: 'resume all isolates on exit') + abbr: 'r', defaultsTo: false, help: 'resume all isolates on exit') ..addFlag('include-dart', - abbr: 'd', - defaultsTo: defaultOptions.includeDart, - help: 'include "dart:" libraries') + abbr: 'd', defaultsTo: false, help: 'include "dart:" libraries') ..addFlag('function-coverage', abbr: 'f', defaultsTo: defaultOptions.functionCoverage, diff --git a/pkgs/coverage/bin/format_coverage.dart b/pkgs/coverage/bin/format_coverage.dart index 4edbc0246..b2b64d7e1 100644 --- a/pkgs/coverage/bin/format_coverage.dart +++ b/pkgs/coverage/bin/format_coverage.dart @@ -139,7 +139,6 @@ Environment parseArgs(List arguments, CoverageOptions defaultOptions) { 'sdk-root', abbr: 's', help: 'path to the SDK root', - defaultsTo: defaultOptions.sdkRoot, ) ..addOption( 'packages', @@ -166,15 +165,13 @@ Environment parseArgs(List arguments, CoverageOptions defaultOptions) { help: 'number of workers', ) ..addOption('bazel-workspace', - defaultsTo: defaultOptions.bazelWorkspace, - help: 'Bazel workspace directory') + defaultsTo: '', help: 'Bazel workspace directory') ..addOption('base-directory', abbr: 'b', defaultsTo: defaultOptions.baseDirectory, help: 'the base directory relative to which source paths are output') ..addFlag('bazel', - defaultsTo: defaultOptions.bazel, - help: 'use Bazel-style path resolution') + defaultsTo: false, help: 'use Bazel-style path resolution') ..addFlag('pretty-print', abbr: 'r', defaultsTo: defaultOptions.prettyPrint, @@ -182,12 +179,10 @@ Environment parseArgs(List arguments, CoverageOptions defaultOptions) { help: 'convert line coverage data to pretty print format') ..addFlag('pretty-print-func', abbr: 'f', - defaultsTo: defaultOptions.prettyPrintFunc, negatable: false, help: 'convert function coverage data to pretty print format') ..addFlag('pretty-print-branch', negatable: false, - defaultsTo: defaultOptions.prettyPrintBranch, help: 'convert branch coverage data to pretty print format') ..addFlag('lcov', abbr: 'l', @@ -202,7 +197,6 @@ Environment parseArgs(List arguments, CoverageOptions defaultOptions) { ..addFlag( 'check-ignore', abbr: 'c', - defaultsTo: defaultOptions.checkIgnore, negatable: false, help: 'check for coverage ignore comments.' ' Not supported in web coverage.', diff --git a/pkgs/coverage/lib/src/coverage_options.dart b/pkgs/coverage/lib/src/coverage_options.dart index 30db2fe8e..dbb9da8b9 100644 --- a/pkgs/coverage/lib/src/coverage_options.dart +++ b/pkgs/coverage/lib/src/coverage_options.dart @@ -6,24 +6,16 @@ class CoverageOptions { required this.output, this.connectTimeout, required this.scopeOutput, - required this.waitPaused, required this.resumeIsolates, - required this.includeDart, required this.functionCoverage, required this.branchCoverage, - this.sdkRoot, required this.packagePath, this.input, this.reportOn, required this.workers, - required this.bazelWorkspace, this.baseDirectory, - required this.bazel, required this.prettyPrint, - required this.prettyPrintFunc, - required this.prettyPrintBranch, required this.lcov, - required this.checkIgnore, required this.ignoreFiles, this.packageName, required this.testScript, @@ -38,37 +30,23 @@ class CoverageOptions { defaultOptions.connectTimeout, scopeOutput: options.optionalStringList('scope_output') ?? defaultOptions.scopeOutput, - waitPaused: - options.optionalBool('wait_paused') ?? defaultOptions.waitPaused, resumeIsolates: options.optionalBool('resume_isolates') ?? defaultOptions.resumeIsolates, - includeDart: - options.optionalBool('include_dart') ?? defaultOptions.includeDart, functionCoverage: options.optionalBool('function_coverage') ?? defaultOptions.functionCoverage, branchCoverage: options.optionalBool('branch_coverage') ?? defaultOptions.branchCoverage, - sdkRoot: options.optionalString('sdk_root') ?? defaultOptions.sdkRoot, packagePath: options.optionalString('package') ?? defaultOptions.packagePath, input: options.optionalString('in') ?? defaultOptions.input, reportOn: options.optionalStringList('report_on') ?? defaultOptions.reportOn, workers: options.optionalInt('workers') ?? defaultOptions.workers, - bazelWorkspace: options.optionalString('bazel_workspace') ?? - defaultOptions.bazelWorkspace, baseDirectory: options.optionalString('base_directory') ?? defaultOptions.baseDirectory, - bazel: options.optionalBool('bazel') ?? defaultOptions.bazel, prettyPrint: options.optionalBool('pretty_print') ?? defaultOptions.prettyPrint, - prettyPrintFunc: options.optionalBool('pretty_print_func') ?? - defaultOptions.prettyPrintFunc, - prettyPrintBranch: options.optionalBool('pretty_print_branch') ?? - defaultOptions.prettyPrintBranch, lcov: options.optionalBool('lcov') ?? defaultOptions.lcov, - checkIgnore: - options.optionalBool('check_ignore') ?? defaultOptions.checkIgnore, ignoreFiles: options.optionalStringList('ignore_files') ?? defaultOptions.ignoreFiles, packageName: @@ -81,24 +59,16 @@ class CoverageOptions { final String output; final int? connectTimeout; final List scopeOutput; - final bool waitPaused; final bool resumeIsolates; - final bool includeDart; final bool functionCoverage; final bool branchCoverage; - final String? sdkRoot; final String packagePath; final String? input; final List? reportOn; final int workers; - final String bazelWorkspace; final String? baseDirectory; - final bool bazel; final bool prettyPrint; - final bool prettyPrintFunc; - final bool prettyPrintBranch; final bool lcov; - final bool checkIgnore; final List ignoreFiles; final String? packageName; final String testScript; @@ -136,24 +106,16 @@ class CoverageOptionsProvider { output: 'stdout', connectTimeout: null, scopeOutput: [], - waitPaused: false, resumeIsolates: false, - includeDart: false, functionCoverage: false, branchCoverage: false, - sdkRoot: null, packagePath: '.', input: null, reportOn: null, workers: 1, - bazelWorkspace: '', baseDirectory: '.', - bazel: false, prettyPrint: false, - prettyPrintFunc: false, - prettyPrintBranch: false, lcov: false, - checkIgnore: false, ignoreFiles: [], packageName: null, testScript: 'test', diff --git a/pkgs/coverage/test/collect_coverage_config_test.dart b/pkgs/coverage/test/collect_coverage_config_test.dart index 3a73af7f0..51f66cd20 100644 --- a/pkgs/coverage/test/collect_coverage_config_test.dart +++ b/pkgs/coverage/test/collect_coverage_config_test.dart @@ -15,11 +15,9 @@ void main() { expect(collectedCoverage.scopeOutput, defaults.scopeOutput); expect(collectedCoverage.resumeIsolates, defaults.resumeIsolates); - expect(collectedCoverage.waitPaused, defaults.waitPaused); expect(collectedCoverage.functionCoverage, defaults.functionCoverage); expect(collectedCoverage.branchCoverage, defaults.branchCoverage); expect(collectedCoverage.connectTimeout, defaults.connectTimeout); - expect(collectedCoverage.includeDart, defaults.includeDart); expect(collectedCoverage.output, defaults.output); }); @@ -28,19 +26,13 @@ void main() { parseArgsFormatCoverage(defaultInputArgs, defaults); expect(formattedCoverage.baseDirectory, defaults.baseDirectory); - expect(formattedCoverage.bazel, defaults.bazel); - expect(formattedCoverage.bazelWorkspace, defaults.bazelWorkspace); - expect(formattedCoverage.checkIgnore, defaults.checkIgnore); expect(formattedCoverage.input, 'coverage.json'); expect(formattedCoverage.lcov, defaults.lcov); expect(formattedCoverage.output, defaults.output); expect(formattedCoverage.packagePath, defaults.packagePath); expect(formattedCoverage.prettyPrint, defaults.prettyPrint); - expect(formattedCoverage.prettyPrintFunc, defaults.prettyPrintFunc); - expect(formattedCoverage.prettyPrintBranch, defaults.prettyPrintBranch); expect(formattedCoverage.reportOn, defaults.reportOn); expect(formattedCoverage.ignoreFiles, defaults.ignoreFiles); - expect(formattedCoverage.sdkRoot, defaults.sdkRoot); expect(formattedCoverage.verbose, defaults.verbose); expect(formattedCoverage.workers, defaults.workers); }); @@ -72,28 +64,20 @@ void main() { // Verify collect coverage yaml values expect(collectedCoverage.scopeOutput, ['lib', 'src']); expect(collectedCoverage.resumeIsolates, isFalse); - expect(collectedCoverage.waitPaused, isTrue); expect(collectedCoverage.functionCoverage, isTrue); expect(collectedCoverage.branchCoverage, isFalse); expect(collectedCoverage.connectTimeout, 30); - expect(collectedCoverage.includeDart, isFalse); expect(collectedCoverage.output, 'coverage'); // Verify format coverage yaml values expect(formattedCoverage.baseDirectory, '.'); - expect(formattedCoverage.bazel, isFalse); - expect(formattedCoverage.bazelWorkspace, ''); - expect(formattedCoverage.checkIgnore, isFalse); expect(formattedCoverage.input, 'coverage.json'); expect(formattedCoverage.lcov, isTrue); expect(formattedCoverage.output, 'coverage'); expect(formattedCoverage.packagePath, '.'); expect(formattedCoverage.prettyPrint, isFalse); - expect(formattedCoverage.prettyPrintFunc, isFalse); - expect(formattedCoverage.prettyPrintBranch, isFalse); expect(formattedCoverage.reportOn, ['lib', 'bin']); expect(formattedCoverage.ignoreFiles, ['test']); - expect(formattedCoverage.sdkRoot, '.'); expect(formattedCoverage.verbose, isTrue); expect(formattedCoverage.workers, 2); @@ -123,9 +107,7 @@ void main() { // Verify collect coverage yaml values expect(collectedCoverage.output, 'custom_coverage.json'); expect(collectedCoverage.scopeOutput, ['lib', 'test']); - expect(collectedCoverage.resumeIsolates, isTrue); expect(collectedCoverage.functionCoverage, isFalse); - expect(collectedCoverage.includeDart, isTrue); expect(collectedCoverage.connectTimeout, 20); // Verify format coverage yaml values @@ -135,7 +117,6 @@ void main() { expect(formattedCoverage.ignoreFiles, ['example']); expect(formattedCoverage.reportOn, ['lib']); expect(formattedCoverage.prettyPrint, isTrue); - expect(formattedCoverage.prettyPrintFunc, isFalse); // Verify test with coverage yaml values expect(testCoverage.packageName, 'Custom Dart Package'); @@ -155,20 +136,15 @@ void main() { // Verify collect coverage yaml values expect(collectedCoverage.scopeOutput, ['lib', 'tools']); - expect(collectedCoverage.includeDart, isFalse); expect(collectedCoverage.branchCoverage, isFalse); - expect(collectedCoverage.waitPaused, isFalse); expect(collectedCoverage.connectTimeout, 15); expect(collectedCoverage.functionCoverage, isTrue); // Verify format coverage yaml values - expect(formattedCoverage.bazel, isTrue); - expect(formattedCoverage.checkIgnore, isTrue); expect(formattedCoverage.input, 'custom_coverage.json'); expect(formattedCoverage.output, 'custom_lcov.info'); expect(formattedCoverage.packagePath, '.'); expect(formattedCoverage.reportOn, ['src', 'scripts']); - expect(formattedCoverage.sdkRoot, './dart-sdk'); // Verify test with coverage yaml values expect(testCoverage.packageName, 'coverage'); @@ -188,8 +164,6 @@ void main() { '--out=coverage.json', '--scope-output=lib', '--connect-timeout=10', - '--resume-isolates', - '--no-wait-paused', '--no-function-coverage', '--branch-coverage', ], configuredOptions); @@ -211,8 +185,6 @@ void main() { // Verify collect coverage command line args expect(collectedCoverage.output, 'coverage.json'); expect(collectedCoverage.scopeOutput, ['lib']); - expect(collectedCoverage.resumeIsolates, isTrue); - expect(collectedCoverage.waitPaused, isFalse); expect(collectedCoverage.functionCoverage, isFalse); expect(collectedCoverage.branchCoverage, isTrue); expect(collectedCoverage.connectTimeout, 10); @@ -245,28 +217,20 @@ void main() { // Verify collect coverage defaults expect(collectedCoverage.scopeOutput, isEmpty); expect(collectedCoverage.resumeIsolates, isFalse); - expect(collectedCoverage.waitPaused, isFalse); expect(collectedCoverage.functionCoverage, isFalse); expect(collectedCoverage.branchCoverage, isFalse); expect(collectedCoverage.connectTimeout, isNull); - expect(collectedCoverage.includeDart, isFalse); expect(collectedCoverage.output, 'stdout'); // Verify format coverage defaults expect(formattedCoverage.baseDirectory, '.'); - expect(formattedCoverage.bazel, isFalse); - expect(formattedCoverage.bazelWorkspace, ''); - expect(formattedCoverage.checkIgnore, isFalse); expect(formattedCoverage.input, 'coverage.json'); expect(formattedCoverage.lcov, isFalse); expect(formattedCoverage.output, 'stdout'); expect(formattedCoverage.packagePath, '.'); expect(formattedCoverage.prettyPrint, isFalse); - expect(formattedCoverage.prettyPrintFunc, isFalse); - expect(formattedCoverage.prettyPrintBranch, isFalse); expect(formattedCoverage.reportOn, isNull); expect(formattedCoverage.ignoreFiles, isEmpty); - expect(formattedCoverage.sdkRoot, isNull); expect(formattedCoverage.verbose, isFalse); expect(formattedCoverage.workers, 1); diff --git a/pkgs/coverage/test/test_coverage_options/all_field.yaml b/pkgs/coverage/test/test_coverage_options/all_field.yaml index 08e234060..8554bf56b 100644 --- a/pkgs/coverage/test/test_coverage_options/all_field.yaml +++ b/pkgs/coverage/test/test_coverage_options/all_field.yaml @@ -1,25 +1,16 @@ out: "coverage" connect-timeout: 30 scope-output: ["lib", "src"] -resume-isolates: false -include-dart: false function-coverage: true branch-coverage: false -wait-paused: true base-directory: "." -bazel: false -bazel-workspace: null in: "coverage.json" package: "." package-name: "My Dart Package" report-on: ["lib", "bin"] ignore-files: ["test"] -sdk-root: "." workers: 2 -check-ignore: false verbose: true lcov: true pretty-print: false -pretty-print-func: false -pretty-print-branch: false test: "test" diff --git a/pkgs/coverage/test/test_coverage_options/partial_fields1.yaml b/pkgs/coverage/test/test_coverage_options/partial_fields1.yaml index 023b09c1a..3da3ed1ca 100644 --- a/pkgs/coverage/test/test_coverage_options/partial_fields1.yaml +++ b/pkgs/coverage/test/test_coverage_options/partial_fields1.yaml @@ -2,9 +2,6 @@ out: 'custom_coverage.json' scope-output: - 'lib' - 'test' -resume-isolates: true -function-coverage: false -include-dart: true connect-timeout: 20 lcov: false verbose: false @@ -14,5 +11,4 @@ ignore-files: report-on: - 'lib' pretty-print: true -pretty-print-func: false package-name: 'Custom Dart Package' \ No newline at end of file diff --git a/pkgs/coverage/test/test_coverage_options/partial_fields2.yaml b/pkgs/coverage/test/test_coverage_options/partial_fields2.yaml index e0580a973..36efc2fb4 100644 --- a/pkgs/coverage/test/test_coverage_options/partial_fields2.yaml +++ b/pkgs/coverage/test/test_coverage_options/partial_fields2.yaml @@ -1,20 +1,15 @@ scope-output: - 'lib' - 'tools' -include-dart: false branch-coverage: false -wait-paused: false connect-timeout: 15 -bazel: true -check-ignore: true in: 'custom_coverage.json' out: 'custom_lcov.info' package: '.' report-on: - 'src' - 'scripts' -sdk-root: './dart-sdk' test: 'custom_test' function-coverage: true \ No newline at end of file diff --git a/pkgs/coverage/test/test_util.dart b/pkgs/coverage/test/test_util.dart index 86854d7e5..50aab8d2f 100644 --- a/pkgs/coverage/test/test_util.dart +++ b/pkgs/coverage/test/test_util.dart @@ -103,10 +103,8 @@ CoverageOptions parseArgsCollectCoverage( ..addOption('connect-timeout', abbr: 't', defaultsTo: defaultOptions.connectTimeout?.toString()) ..addMultiOption('scope-output', defaultsTo: defaultOptions.scopeOutput) - ..addFlag('wait-paused', abbr: 'w', defaultsTo: defaultOptions.waitPaused) ..addFlag('resume-isolates', abbr: 'r', defaultsTo: defaultOptions.resumeIsolates) - ..addFlag('include-dart', abbr: 'd', defaultsTo: defaultOptions.includeDart) ..addFlag('function-coverage', abbr: 'f', defaultsTo: defaultOptions.functionCoverage) ..addFlag('branch-coverage', @@ -121,36 +119,26 @@ CoverageOptions parseArgsCollectCoverage( ? defaultOptions.connectTimeout : int.parse(args['connect-timeout'] as String), scopeOutput: args['scope-output'] as List, - waitPaused: args['wait-paused'] as bool, resumeIsolates: args['resume-isolates'] as bool, - includeDart: args['include-dart'] as bool, functionCoverage: args['function-coverage'] as bool, branchCoverage: args['branch-coverage'] as bool, - bazel: defaultOptions.bazel, - bazelWorkspace: defaultOptions.bazelWorkspace, baseDirectory: defaultOptions.baseDirectory, - checkIgnore: defaultOptions.checkIgnore, ignoreFiles: defaultOptions.ignoreFiles, input: defaultOptions.input, lcov: defaultOptions.lcov, packagePath: defaultOptions.packagePath, packageName: defaultOptions.packageName, prettyPrint: defaultOptions.prettyPrint, - prettyPrintBranch: defaultOptions.prettyPrintBranch, - prettyPrintFunc: defaultOptions.prettyPrintFunc, reportOn: defaultOptions.reportOn, - sdkRoot: defaultOptions.sdkRoot, testScript: defaultOptions.testScript, verbose: defaultOptions.verbose, workers: defaultOptions.workers, - ); } CoverageOptions parseArgsFormatCoverage( List arguments, CoverageOptions defaultOptions) { final parser = ArgParser() - ..addOption('sdk-root', abbr: 's', defaultsTo: defaultOptions.sdkRoot) ..addOption('packages') ..addOption('package', defaultsTo: defaultOptions.packagePath) ..addOption('in', abbr: 'i', defaultsTo: defaultOptions.input) @@ -158,24 +146,14 @@ CoverageOptions parseArgsFormatCoverage( ..addMultiOption('report-on', defaultsTo: defaultOptions.reportOn) ..addOption('workers', abbr: 'j', defaultsTo: defaultOptions.workers.toString()) - ..addOption('bazel-workspace', defaultsTo: defaultOptions.bazelWorkspace) ..addOption('base-directory', abbr: 'b', defaultsTo: defaultOptions.baseDirectory) - ..addFlag('bazel', - defaultsTo: defaultOptions.bazel, - help: 'use Bazel-style path resolution') ..addFlag('pretty-print', abbr: 'r', defaultsTo: defaultOptions.prettyPrint, negatable: false) - ..addFlag('pretty-print-func', - abbr: 'f', defaultsTo: defaultOptions.prettyPrintFunc, negatable: false) - ..addFlag('pretty-print-branch', - negatable: false, defaultsTo: defaultOptions.prettyPrintBranch) ..addFlag('lcov', abbr: 'l', defaultsTo: defaultOptions.lcov, negatable: false) ..addFlag('verbose', abbr: 'v', defaultsTo: defaultOptions.verbose, negatable: false) - ..addFlag('check-ignore', - abbr: 'c', defaultsTo: defaultOptions.checkIgnore, negatable: false) ..addMultiOption('ignore-files', defaultsTo: defaultOptions.ignoreFiles) ..addFlag('help', abbr: 'h', negatable: false); @@ -185,31 +163,23 @@ CoverageOptions parseArgsFormatCoverage( return CoverageOptions( baseDirectory: args['base-directory'] as String?, - bazel: args['bazel'] as bool, - bazelWorkspace: args['bazel-workspace'] as String, - checkIgnore: args['check-ignore'] as bool, input: args['in'] as String, lcov: args['lcov'] as bool, output: args['out'] as String, packagePath: args['package'] as String, prettyPrint: args['lcov'] as bool ? false : args['pretty-print'] as bool, - prettyPrintFunc: args['pretty-print-func'] as bool, - prettyPrintBranch: args['pretty-print-branch'] as bool, reportOn: (args['report-on'] as List).isNotEmpty ? args['report-on'] as List : null, ignoreFiles: args['ignore-files'] as List, - sdkRoot: args['sdk-root'] as String?, verbose: args['verbose'] as bool, workers: int.parse(args['workers'] as String), branchCoverage: defaultOptions.branchCoverage, functionCoverage: defaultOptions.functionCoverage, connectTimeout: defaultOptions.connectTimeout, - includeDart: defaultOptions.includeDart, packageName: defaultOptions.packageName, resumeIsolates: defaultOptions.resumeIsolates, scopeOutput: defaultOptions.scopeOutput, - waitPaused: defaultOptions.waitPaused, testScript: defaultOptions.testScript, ); } @@ -253,8 +223,7 @@ Future parseArgsTestWithCoverage( } final packageName = - args['package-name'] ?? - await _packageNameFromConfig(packageDir); + args['package-name'] ?? await _packageNameFromConfig(packageDir); if (packageName == null) { ArgumentError('Could not determine package name'); } @@ -267,23 +236,15 @@ Future parseArgsTestWithCoverage( functionCoverage: args['function-coverage'] as bool, branchCoverage: args['branch-coverage'] as bool, scopeOutput: args['scope-output'] as List, - bazel: defaultOptions.bazel, - bazelWorkspace: defaultOptions.bazelWorkspace, baseDirectory: defaultOptions.baseDirectory, - checkIgnore: defaultOptions.checkIgnore, connectTimeout: defaultOptions.connectTimeout, ignoreFiles: defaultOptions.ignoreFiles, - includeDart: defaultOptions.includeDart, input: defaultOptions.input, lcov: defaultOptions.lcov, prettyPrint: defaultOptions.prettyPrint, - prettyPrintBranch: defaultOptions.prettyPrintBranch, - prettyPrintFunc: defaultOptions.prettyPrintFunc, reportOn: defaultOptions.reportOn, resumeIsolates: defaultOptions.resumeIsolates, - sdkRoot: defaultOptions.sdkRoot, verbose: defaultOptions.verbose, - waitPaused: defaultOptions.waitPaused, workers: defaultOptions.workers, ); } From 4a1d57f98d6746505eb154973c08b059db0efd32 Mon Sep 17 00:00:00 2001 From: Dhruv Maradiya Date: Thu, 16 Jan 2025 22:23:16 +0530 Subject: [PATCH 09/12] removed support for all uncommon flags --- pkgs/coverage/CHANGELOG.md | 2 +- pkgs/coverage/bin/collect_coverage.dart | 21 +++++---- pkgs/coverage/bin/format_coverage.dart | 51 ++++++++++----------- pkgs/coverage/bin/test_with_coverage.dart | 2 +- pkgs/coverage/lib/src/coverage_options.dart | 50 +------------------- pkgs/coverage/pubspec.yaml | 2 +- 6 files changed, 41 insertions(+), 87 deletions(-) diff --git a/pkgs/coverage/CHANGELOG.md b/pkgs/coverage/CHANGELOG.md index d641416d1..bc4ddab72 100644 --- a/pkgs/coverage/CHANGELOG.md +++ b/pkgs/coverage/CHANGELOG.md @@ -1,4 +1,4 @@ -## 1.11.2 +## 1.12.0 - Introduced support for specifying coverage flags through a YAML file. diff --git a/pkgs/coverage/bin/collect_coverage.dart b/pkgs/coverage/bin/collect_coverage.dart index c2e122ea6..1808fdb17 100644 --- a/pkgs/coverage/bin/collect_coverage.dart +++ b/pkgs/coverage/bin/collect_coverage.dart @@ -10,6 +10,7 @@ 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:path/path.dart' as p; import 'package:stack_trace/stack_trace.dart'; Future main(List arguments) async { @@ -71,14 +72,9 @@ Options _parseArgs(List arguments, CoverageOptions defaultOptions) { help: 'remote VM port. DEPRECATED: use --uri', defaultsTo: '8181') ..addOption('uri', abbr: 'u', help: 'VM observatory service URI') - ..addOption('out', - abbr: 'o', - defaultsTo: defaultOptions.output, - help: 'output: may be file or stdout') + ..addOption('out', abbr: 'o', help: 'output: may be file or stdout') ..addOption('connect-timeout', - defaultsTo: defaultOptions.connectTimeout?.toString(), - abbr: 't', - help: 'connect timeout in seconds') + abbr: 't', help: 'connect timeout in seconds') ..addMultiOption('scope-output', defaultsTo: defaultOptions.scopeOutput, help: 'restrict coverage results so that only scripts that start with ' @@ -135,12 +131,17 @@ Options _parseArgs(List arguments, CoverageOptions defaultOptions) { final scopedOutput = args['scope-output'] as List; IOSink out; - if (args['out'] == 'stdout') { + final outPath = args['out'] as String?; + if (outPath == 'stdout' || + (outPath == null && defaultOptions.output == null)) { out = stdout; } else { - final outfile = File(args['out'] as String)..createSync(recursive: true); - out = outfile.openWrite(); + final outFilePath = p.absolute( + p.normalize(outPath ?? '${defaultOptions.output}/coverage.json')); + final outFile = File(outFilePath)..createSync(recursive: true); + out = outFile.openWrite(); } + 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 b2b64d7e1..d4a0ec7eb 100644 --- a/pkgs/coverage/bin/format_coverage.dart +++ b/pkgs/coverage/bin/format_coverage.dart @@ -147,34 +147,31 @@ Environment parseArgs(List arguments, CoverageOptions defaultOptions) { ..addOption('package', help: 'root directory of the package', defaultsTo: defaultOptions.packagePath) - ..addOption('in', - abbr: 'i', - help: 'input(s): may be file or directory', - defaultsTo: defaultOptions.input) - ..addOption('out', - abbr: 'o', - defaultsTo: defaultOptions.output, - help: 'output: may be file or stdout') - ..addMultiOption('report-on', - help: 'which directories or files to report coverage on', - defaultsTo: defaultOptions.reportOn) + ..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: defaultOptions.workers.toString(), + defaultsTo: '1', help: 'number of workers', ) ..addOption('bazel-workspace', defaultsTo: '', help: 'Bazel workspace directory') ..addOption('base-directory', abbr: 'b', - defaultsTo: defaultOptions.baseDirectory, help: 'the base directory relative to which source paths are output') ..addFlag('bazel', defaultsTo: false, help: 'use Bazel-style path resolution') ..addFlag('pretty-print', abbr: 'r', - defaultsTo: defaultOptions.prettyPrint, negatable: false, help: 'convert line coverage data to pretty print format') ..addFlag('pretty-print-func', @@ -186,14 +183,9 @@ Environment parseArgs(List arguments, CoverageOptions defaultOptions) { help: 'convert branch coverage data to pretty print format') ..addFlag('lcov', abbr: 'l', - defaultsTo: defaultOptions.lcov, negatable: false, help: 'convert coverage data to lcov format') - ..addFlag('verbose', - abbr: 'v', - defaultsTo: defaultOptions.verbose, - negatable: false, - help: 'verbose output') + ..addFlag('verbose', abbr: 'v', negatable: false, help: 'verbose output') ..addFlag( 'check-ignore', abbr: 'c', @@ -203,7 +195,7 @@ Environment parseArgs(List arguments, CoverageOptions defaultOptions) { ) ..addMultiOption( 'ignore-files', - defaultsTo: defaultOptions.ignoreFiles, + defaultsTo: [], help: 'Ignore files by glob patterns', ) ..addFlag('help', abbr: 'h', negatable: false, help: 'show this help'); @@ -247,19 +239,26 @@ Environment parseArgs(List arguments, CoverageOptions defaultOptions) { 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.output == null) { + fail('No input files given.'); + } + final inputPath = + args['in'] as String? ?? '${defaultOptions.output}/coverage.json'; + final input = p.absolute(p.normalize(inputPath)); 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') { + final outPath = args['out'] as String?; + if (outPath == 'stdout' || + (outPath == null && defaultOptions.output == null)) { output = stdout; } else { - final outpath = p.absolute(p.normalize(args['out'] as String)); - final outfile = File(outpath)..createSync(recursive: true); + final outFilePath = p + .absolute(p.normalize(outPath ?? '${defaultOptions.output}/lcov.info')); + final outfile = File(outFilePath)..createSync(recursive: true); output = outfile.openWrite(); } diff --git a/pkgs/coverage/bin/test_with_coverage.dart b/pkgs/coverage/bin/test_with_coverage.dart index c0bb37789..046db9786 100644 --- a/pkgs/coverage/bin/test_with_coverage.dart +++ b/pkgs/coverage/bin/test_with_coverage.dart @@ -55,7 +55,7 @@ ArgParser _createArgParser(CoverageOptions defaultOptions) => ArgParser() ..addOption( 'package', help: 'Root directory of the package to test.', - defaultsTo: defaultOptions.packageName, + defaultsTo: defaultOptions.packagePath, ) ..addOption( 'package-name', diff --git a/pkgs/coverage/lib/src/coverage_options.dart b/pkgs/coverage/lib/src/coverage_options.dart index dbb9da8b9..180455ae5 100644 --- a/pkgs/coverage/lib/src/coverage_options.dart +++ b/pkgs/coverage/lib/src/coverage_options.dart @@ -4,75 +4,39 @@ import 'package:cli_config/cli_config.dart'; class CoverageOptions { const CoverageOptions({ required this.output, - this.connectTimeout, required this.scopeOutput, - required this.resumeIsolates, required this.functionCoverage, required this.branchCoverage, required this.packagePath, - this.input, - this.reportOn, - required this.workers, - this.baseDirectory, - required this.prettyPrint, - required this.lcov, - required this.ignoreFiles, this.packageName, required this.testScript, - required this.verbose, }); factory CoverageOptions.fromConfig( Config options, CoverageOptions defaultOptions) { return CoverageOptions( output: options.optionalString('out') ?? defaultOptions.output, - connectTimeout: options.optionalInt('connect_timeout') ?? - defaultOptions.connectTimeout, scopeOutput: options.optionalStringList('scope_output') ?? defaultOptions.scopeOutput, - resumeIsolates: options.optionalBool('resume_isolates') ?? - defaultOptions.resumeIsolates, functionCoverage: options.optionalBool('function_coverage') ?? defaultOptions.functionCoverage, branchCoverage: options.optionalBool('branch_coverage') ?? defaultOptions.branchCoverage, packagePath: options.optionalString('package') ?? defaultOptions.packagePath, - input: options.optionalString('in') ?? defaultOptions.input, - reportOn: - options.optionalStringList('report_on') ?? defaultOptions.reportOn, - workers: options.optionalInt('workers') ?? defaultOptions.workers, - baseDirectory: options.optionalString('base_directory') ?? - defaultOptions.baseDirectory, - prettyPrint: - options.optionalBool('pretty_print') ?? defaultOptions.prettyPrint, - lcov: options.optionalBool('lcov') ?? defaultOptions.lcov, - ignoreFiles: options.optionalStringList('ignore_files') ?? - defaultOptions.ignoreFiles, packageName: options.optionalString('package_name') ?? defaultOptions.packageName, testScript: options.optionalString('test') ?? defaultOptions.testScript, - verbose: options.optionalBool('verbose') ?? defaultOptions.verbose, ); } - final String output; - final int? connectTimeout; + final String? output; final List scopeOutput; - final bool resumeIsolates; final bool functionCoverage; final bool branchCoverage; final String packagePath; - final String? input; - final List? reportOn; - final int workers; - final String? baseDirectory; - final bool prettyPrint; - final bool lcov; - final List ignoreFiles; final String? packageName; final String testScript; - final bool verbose; } class CoverageOptionsProvider { @@ -103,22 +67,12 @@ class CoverageOptionsProvider { } static const defaultOptions = CoverageOptions( - output: 'stdout', - connectTimeout: null, + output: null, scopeOutput: [], - resumeIsolates: false, functionCoverage: false, branchCoverage: false, packagePath: '.', - input: null, - reportOn: null, - workers: 1, - baseDirectory: '.', - prettyPrint: false, - lcov: false, - ignoreFiles: [], packageName: null, testScript: 'test', - verbose: false, ); } diff --git a/pkgs/coverage/pubspec.yaml b/pkgs/coverage/pubspec.yaml index a8f4f84b5..6bd452470 100644 --- a/pkgs/coverage/pubspec.yaml +++ b/pkgs/coverage/pubspec.yaml @@ -1,5 +1,5 @@ name: coverage -version: 1.11.2 +version: 1.12.0 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 From c610504458926f33ec9d1bfb9bbff7b43621561d Mon Sep 17 00:00:00 2001 From: Dhruv Maradiya Date: Thu, 16 Jan 2025 22:47:19 +0530 Subject: [PATCH 10/12] refactor: changed test cases to align with updated coverage options --- .../test/collect_coverage_config_test.dart | 89 ++++--------------- .../test/test_coverage_options/all_field.yaml | 9 -- .../partial_fields1.yaml | 11 +-- .../partial_fields2.yaml | 9 +- pkgs/coverage/test/test_util.dart | 87 +++++------------- 5 files changed, 40 insertions(+), 165 deletions(-) diff --git a/pkgs/coverage/test/collect_coverage_config_test.dart b/pkgs/coverage/test/collect_coverage_config_test.dart index 51f66cd20..8c64240c3 100644 --- a/pkgs/coverage/test/collect_coverage_config_test.dart +++ b/pkgs/coverage/test/collect_coverage_config_test.dart @@ -4,8 +4,6 @@ import 'package:test/test.dart'; import 'test_util.dart'; void main() { - final defaultInputArgs = ['--in=coverage.json']; - group('defaults when no yaml or command-line args provided', () { // Setup final defaults = CoverageOptionsProvider.defaultOptions; @@ -14,27 +12,17 @@ void main() { final collectedCoverage = parseArgsCollectCoverage([], defaults); expect(collectedCoverage.scopeOutput, defaults.scopeOutput); - expect(collectedCoverage.resumeIsolates, defaults.resumeIsolates); expect(collectedCoverage.functionCoverage, defaults.functionCoverage); expect(collectedCoverage.branchCoverage, defaults.branchCoverage); - expect(collectedCoverage.connectTimeout, defaults.connectTimeout); - expect(collectedCoverage.output, defaults.output); + expect(collectedCoverage.output, 'stdout'); }); test('format coverage', () { final formattedCoverage = - parseArgsFormatCoverage(defaultInputArgs, defaults); + parseArgsFormatCoverage([], defaults); - expect(formattedCoverage.baseDirectory, defaults.baseDirectory); - expect(formattedCoverage.input, 'coverage.json'); - expect(formattedCoverage.lcov, defaults.lcov); - expect(formattedCoverage.output, defaults.output); + expect(formattedCoverage.output, 'stdout'); expect(formattedCoverage.packagePath, defaults.packagePath); - expect(formattedCoverage.prettyPrint, defaults.prettyPrint); - expect(formattedCoverage.reportOn, defaults.reportOn); - expect(formattedCoverage.ignoreFiles, defaults.ignoreFiles); - expect(formattedCoverage.verbose, defaults.verbose); - expect(formattedCoverage.workers, defaults.workers); }); test('test with coverage', () async { @@ -42,7 +30,7 @@ void main() { expect(testCoverage.packagePath, getPackageDir(defaults.packagePath)); expect(testCoverage.packageName, 'coverage'); - expect(testCoverage.output, defaults.output); + expect(testCoverage.output, 'coverage'); expect(testCoverage.testScript, defaults.testScript); expect(testCoverage.functionCoverage, defaults.functionCoverage); expect(testCoverage.branchCoverage, defaults.branchCoverage); @@ -58,28 +46,18 @@ void main() { // Parse arguments with empty command line args final collectedCoverage = parseArgsCollectCoverage([], configuredOptions); final formattedCoverage = - parseArgsFormatCoverage(defaultInputArgs, configuredOptions); + parseArgsFormatCoverage([], configuredOptions); final testCoverage = await parseArgsTestWithCoverage([], configuredOptions); // Verify collect coverage yaml values expect(collectedCoverage.scopeOutput, ['lib', 'src']); - expect(collectedCoverage.resumeIsolates, isFalse); expect(collectedCoverage.functionCoverage, isTrue); expect(collectedCoverage.branchCoverage, isFalse); - expect(collectedCoverage.connectTimeout, 30); - expect(collectedCoverage.output, 'coverage'); + expect(collectedCoverage.output, 'coverage/coverage.json'); // Verify format coverage yaml values - expect(formattedCoverage.baseDirectory, '.'); - expect(formattedCoverage.input, 'coverage.json'); - expect(formattedCoverage.lcov, isTrue); - expect(formattedCoverage.output, 'coverage'); + expect(formattedCoverage.output, 'coverage/lcov.info'); expect(formattedCoverage.packagePath, '.'); - expect(formattedCoverage.prettyPrint, isFalse); - expect(formattedCoverage.reportOn, ['lib', 'bin']); - expect(formattedCoverage.ignoreFiles, ['test']); - expect(formattedCoverage.verbose, isTrue); - expect(formattedCoverage.workers, 2); // Verify test with coverage yaml values expect(testCoverage.packagePath, getPackageDir('.')); @@ -99,26 +77,14 @@ void main() { // Parse arguments with empty command line args final collectedCoverage = parseArgsCollectCoverage([], configuredOptions); - final formattedCoverage = - parseArgsFormatCoverage(defaultInputArgs, configuredOptions); final testCoverage = await parseArgsTestWithCoverage([], configuredOptions); + final formattedCoverage = parseArgsFormatCoverage([], configuredOptions); - // Verify collect coverage yaml values - expect(collectedCoverage.output, 'custom_coverage.json'); + expect(collectedCoverage.output, 'custom_coverage/coverage.json'); expect(collectedCoverage.scopeOutput, ['lib', 'test']); expect(collectedCoverage.functionCoverage, isFalse); - expect(collectedCoverage.connectTimeout, 20); - - // Verify format coverage yaml values - expect(formattedCoverage.lcov, isFalse); - expect(formattedCoverage.verbose, isFalse); - expect(formattedCoverage.baseDirectory, 'src'); - expect(formattedCoverage.ignoreFiles, ['example']); - expect(formattedCoverage.reportOn, ['lib']); - expect(formattedCoverage.prettyPrint, isTrue); - - // Verify test with coverage yaml values + expect(formattedCoverage.output, 'custom_coverage/lcov.info'); expect(testCoverage.packageName, 'Custom Dart Package'); expect(testCoverage.scopeOutput, ['lib', 'test']); }); @@ -137,18 +103,16 @@ void main() { // Verify collect coverage yaml values expect(collectedCoverage.scopeOutput, ['lib', 'tools']); expect(collectedCoverage.branchCoverage, isFalse); - expect(collectedCoverage.connectTimeout, 15); expect(collectedCoverage.functionCoverage, isTrue); + expect(collectedCoverage.output, 'custom_lcov/coverage.json'); // Verify format coverage yaml values - expect(formattedCoverage.input, 'custom_coverage.json'); - expect(formattedCoverage.output, 'custom_lcov.info'); + expect(formattedCoverage.output, 'custom_lcov/lcov.info'); expect(formattedCoverage.packagePath, '.'); - expect(formattedCoverage.reportOn, ['src', 'scripts']); // Verify test with coverage yaml values expect(testCoverage.packageName, 'coverage'); - expect(testCoverage.output, 'custom_lcov.info'); + expect(testCoverage.output, 'custom_lcov'); expect(testCoverage.testScript, 'custom_test'); expect(testCoverage.functionCoverage, isTrue); }); @@ -163,17 +127,12 @@ void main() { final collectedCoverage = parseArgsCollectCoverage([ '--out=coverage.json', '--scope-output=lib', - '--connect-timeout=10', '--no-function-coverage', '--branch-coverage', ], configuredOptions); final formattedCoverage = parseArgsFormatCoverage([ - '--in=data.json', '--out=out_test.info', - '--report-on=src', - '--report-on=src2', - '--ignore-files=bin', - '--workers=4', + '--package=code_builder', ], configuredOptions); final testCoverage = await parseArgsTestWithCoverage([ '--package-name=test', @@ -187,14 +146,10 @@ void main() { expect(collectedCoverage.scopeOutput, ['lib']); expect(collectedCoverage.functionCoverage, isFalse); expect(collectedCoverage.branchCoverage, isTrue); - expect(collectedCoverage.connectTimeout, 10); // Verify format coverage command line args - expect(formattedCoverage.input, 'data.json'); expect(formattedCoverage.output, 'out_test.info'); - expect(formattedCoverage.reportOn, ['src', 'src2']); - expect(formattedCoverage.ignoreFiles, ['bin']); - expect(formattedCoverage.workers, 4); + expect(formattedCoverage.packagePath, 'code_builder'); // Verify test with coverage command line args expect(testCoverage.packageName, 'test'); @@ -211,33 +166,23 @@ void main() { // Parse arguments with empty command line args final collectedCoverage = parseArgsCollectCoverage([], configuredOptions); final formattedCoverage = - parseArgsFormatCoverage(defaultInputArgs, configuredOptions); + parseArgsFormatCoverage([], configuredOptions); final testCoverage = await parseArgsTestWithCoverage([], configuredOptions); // Verify collect coverage defaults expect(collectedCoverage.scopeOutput, isEmpty); - expect(collectedCoverage.resumeIsolates, isFalse); expect(collectedCoverage.functionCoverage, isFalse); expect(collectedCoverage.branchCoverage, isFalse); - expect(collectedCoverage.connectTimeout, isNull); expect(collectedCoverage.output, 'stdout'); // Verify format coverage defaults - expect(formattedCoverage.baseDirectory, '.'); - expect(formattedCoverage.input, 'coverage.json'); - expect(formattedCoverage.lcov, isFalse); expect(formattedCoverage.output, 'stdout'); expect(formattedCoverage.packagePath, '.'); - expect(formattedCoverage.prettyPrint, isFalse); - expect(formattedCoverage.reportOn, isNull); - expect(formattedCoverage.ignoreFiles, isEmpty); - expect(formattedCoverage.verbose, isFalse); - expect(formattedCoverage.workers, 1); // Verify test with coverage defaults expect(testCoverage.packagePath, getPackageDir('.')); expect(testCoverage.packageName, 'coverage'); - expect(testCoverage.output, 'stdout'); + expect(testCoverage.output, 'coverage'); expect(testCoverage.testScript, 'test'); expect(testCoverage.functionCoverage, isFalse); expect(testCoverage.branchCoverage, isFalse); diff --git a/pkgs/coverage/test/test_coverage_options/all_field.yaml b/pkgs/coverage/test/test_coverage_options/all_field.yaml index 8554bf56b..57f1cd41a 100644 --- a/pkgs/coverage/test/test_coverage_options/all_field.yaml +++ b/pkgs/coverage/test/test_coverage_options/all_field.yaml @@ -1,16 +1,7 @@ out: "coverage" -connect-timeout: 30 scope-output: ["lib", "src"] function-coverage: true branch-coverage: false -base-directory: "." -in: "coverage.json" package: "." package-name: "My Dart Package" -report-on: ["lib", "bin"] -ignore-files: ["test"] -workers: 2 -verbose: true -lcov: true -pretty-print: false test: "test" diff --git a/pkgs/coverage/test/test_coverage_options/partial_fields1.yaml b/pkgs/coverage/test/test_coverage_options/partial_fields1.yaml index 3da3ed1ca..137c0b355 100644 --- a/pkgs/coverage/test/test_coverage_options/partial_fields1.yaml +++ b/pkgs/coverage/test/test_coverage_options/partial_fields1.yaml @@ -1,14 +1,5 @@ -out: 'custom_coverage.json' +out: 'custom_coverage' scope-output: - 'lib' - 'test' -connect-timeout: 20 -lcov: false -verbose: false -base-directory: 'src' -ignore-files: - - 'example' -report-on: - - 'lib' -pretty-print: true package-name: 'Custom Dart Package' \ No newline at end of file diff --git a/pkgs/coverage/test/test_coverage_options/partial_fields2.yaml b/pkgs/coverage/test/test_coverage_options/partial_fields2.yaml index 36efc2fb4..1df32004d 100644 --- a/pkgs/coverage/test/test_coverage_options/partial_fields2.yaml +++ b/pkgs/coverage/test/test_coverage_options/partial_fields2.yaml @@ -2,14 +2,7 @@ scope-output: - 'lib' - 'tools' branch-coverage: false -connect-timeout: 15 - -in: 'custom_coverage.json' -out: 'custom_lcov.info' +out: 'custom_lcov' package: '.' -report-on: - - 'src' - - 'scripts' - test: 'custom_test' function-coverage: true \ No newline at end of file diff --git a/pkgs/coverage/test/test_util.dart b/pkgs/coverage/test/test_util.dart index 50aab8d2f..271e45727 100644 --- a/pkgs/coverage/test/test_util.dart +++ b/pkgs/coverage/test/test_util.dart @@ -96,89 +96,56 @@ extension ListTestExtension on List { CoverageOptions parseArgsCollectCoverage( List arguments, CoverageOptions defaultOptions) { final parser = ArgParser() - ..addOption('host', abbr: 'H') - ..addOption('port', abbr: 'p') - ..addOption('uri', abbr: 'u') - ..addOption('out', abbr: 'o', defaultsTo: defaultOptions.output) - ..addOption('connect-timeout', - abbr: 't', defaultsTo: defaultOptions.connectTimeout?.toString()) + ..addOption('out', abbr: 'o') ..addMultiOption('scope-output', defaultsTo: defaultOptions.scopeOutput) - ..addFlag('resume-isolates', - abbr: 'r', defaultsTo: defaultOptions.resumeIsolates) ..addFlag('function-coverage', abbr: 'f', defaultsTo: defaultOptions.functionCoverage) ..addFlag('branch-coverage', - abbr: 'b', defaultsTo: defaultOptions.branchCoverage) - ..addFlag('help', abbr: 'h', negatable: false); + abbr: 'b', defaultsTo: defaultOptions.branchCoverage); final args = parser.parse(arguments); + String out; + final outPath = args['out'] as String?; + if (outPath == null && defaultOptions.output == null) { + out = 'stdout'; + } else { + out = outPath ?? '${defaultOptions.output}/coverage.json'; + } + return CoverageOptions( - output: args['out'] as String, - connectTimeout: args['connect-timeout'] == null - ? defaultOptions.connectTimeout - : int.parse(args['connect-timeout'] as String), + output: out, scopeOutput: args['scope-output'] as List, - resumeIsolates: args['resume-isolates'] as bool, functionCoverage: args['function-coverage'] as bool, branchCoverage: args['branch-coverage'] as bool, - baseDirectory: defaultOptions.baseDirectory, - ignoreFiles: defaultOptions.ignoreFiles, - input: defaultOptions.input, - lcov: defaultOptions.lcov, packagePath: defaultOptions.packagePath, packageName: defaultOptions.packageName, - prettyPrint: defaultOptions.prettyPrint, - reportOn: defaultOptions.reportOn, testScript: defaultOptions.testScript, - verbose: defaultOptions.verbose, - workers: defaultOptions.workers, ); } CoverageOptions parseArgsFormatCoverage( List arguments, CoverageOptions defaultOptions) { final parser = ArgParser() - ..addOption('packages') ..addOption('package', defaultsTo: defaultOptions.packagePath) - ..addOption('in', abbr: 'i', defaultsTo: defaultOptions.input) - ..addOption('out', abbr: 'o', defaultsTo: defaultOptions.output) - ..addMultiOption('report-on', defaultsTo: defaultOptions.reportOn) - ..addOption('workers', - abbr: 'j', defaultsTo: defaultOptions.workers.toString()) - ..addOption('base-directory', - abbr: 'b', defaultsTo: defaultOptions.baseDirectory) - ..addFlag('pretty-print', - abbr: 'r', defaultsTo: defaultOptions.prettyPrint, negatable: false) - ..addFlag('lcov', - abbr: 'l', defaultsTo: defaultOptions.lcov, negatable: false) - ..addFlag('verbose', - abbr: 'v', defaultsTo: defaultOptions.verbose, negatable: false) - ..addMultiOption('ignore-files', defaultsTo: defaultOptions.ignoreFiles) - ..addFlag('help', abbr: 'h', negatable: false); + ..addOption('out', abbr: 'o'); final args = parser.parse(arguments); - if (args['in'] == null) throw ArgumentError('Missing required argument: in'); + String out; + final outPath = args['out'] as String?; + if (outPath == null && defaultOptions.output == null) { + out = 'stdout'; + } else { + out = outPath ?? '${defaultOptions.output}/lcov.info'; + } return CoverageOptions( - baseDirectory: args['base-directory'] as String?, - input: args['in'] as String, - lcov: args['lcov'] as bool, - output: args['out'] as String, + output: out, packagePath: args['package'] as String, - prettyPrint: args['lcov'] as bool ? false : args['pretty-print'] as bool, - reportOn: (args['report-on'] as List).isNotEmpty - ? args['report-on'] as List - : null, - ignoreFiles: args['ignore-files'] as List, - verbose: args['verbose'] as bool, - workers: int.parse(args['workers'] as String), branchCoverage: defaultOptions.branchCoverage, functionCoverage: defaultOptions.functionCoverage, - connectTimeout: defaultOptions.connectTimeout, packageName: defaultOptions.packageName, - resumeIsolates: defaultOptions.resumeIsolates, scopeOutput: defaultOptions.scopeOutput, testScript: defaultOptions.testScript, ); @@ -195,7 +162,6 @@ Future parseArgsTestWithCoverage( 'package-name', defaultsTo: defaultOptions.packageName, ) - ..addOption('port') ..addOption( 'out', defaultsTo: defaultOptions.output, @@ -212,8 +178,7 @@ Future parseArgsTestWithCoverage( abbr: 'b', defaultsTo: defaultOptions.branchCoverage, ) - ..addMultiOption('scope-output', defaultsTo: defaultOptions.scopeOutput) - ..addFlag('help', abbr: 'h', negatable: false); + ..addMultiOption('scope-output', defaultsTo: defaultOptions.scopeOutput); final args = parser.parse(arguments); @@ -236,16 +201,6 @@ Future parseArgsTestWithCoverage( functionCoverage: args['function-coverage'] as bool, branchCoverage: args['branch-coverage'] as bool, scopeOutput: args['scope-output'] as List, - baseDirectory: defaultOptions.baseDirectory, - connectTimeout: defaultOptions.connectTimeout, - ignoreFiles: defaultOptions.ignoreFiles, - input: defaultOptions.input, - lcov: defaultOptions.lcov, - prettyPrint: defaultOptions.prettyPrint, - reportOn: defaultOptions.reportOn, - resumeIsolates: defaultOptions.resumeIsolates, - verbose: defaultOptions.verbose, - workers: defaultOptions.workers, ); } From b1efad036396dcd562dc6cf3cee61cdf88887145 Mon Sep 17 00:00:00 2001 From: Dhruv Maradiya Date: Fri, 17 Jan 2025 20:18:27 +0530 Subject: [PATCH 11/12] change: Resolve output and package paths to absolute paths; add upward search for options file --- pkgs/coverage/CHANGELOG.md | 2 +- pkgs/coverage/bin/collect_coverage.dart | 6 +- pkgs/coverage/bin/format_coverage.dart | 12 +-- pkgs/coverage/bin/test_with_coverage.dart | 17 +--- pkgs/coverage/lib/src/coverage_options.dart | 71 +++++++++++---- pkgs/coverage/pubspec.yaml | 2 +- .../test/collect_coverage_config_test.dart | 88 ++++++++++++------- .../test/test_coverage_options/all_field.yaml | 6 +- .../partial_fields1.yaml | 2 +- .../partial_fields2.yaml | 6 +- pkgs/coverage/test/test_util.dart | 28 +++--- 11 files changed, 144 insertions(+), 96 deletions(-) diff --git a/pkgs/coverage/CHANGELOG.md b/pkgs/coverage/CHANGELOG.md index bc4ddab72..86e248e7d 100644 --- a/pkgs/coverage/CHANGELOG.md +++ b/pkgs/coverage/CHANGELOG.md @@ -1,4 +1,4 @@ -## 1.12.0 +## 1.12.0-wip - Introduced support for specifying coverage flags through a YAML file. diff --git a/pkgs/coverage/bin/collect_coverage.dart b/pkgs/coverage/bin/collect_coverage.dart index 1808fdb17..e25cd0049 100644 --- a/pkgs/coverage/bin/collect_coverage.dart +++ b/pkgs/coverage/bin/collect_coverage.dart @@ -133,11 +133,11 @@ Options _parseArgs(List arguments, CoverageOptions defaultOptions) { IOSink out; final outPath = args['out'] as String?; if (outPath == 'stdout' || - (outPath == null && defaultOptions.output == null)) { + (outPath == null && defaultOptions.outputDirectory == null)) { out = stdout; } else { - final outFilePath = p.absolute( - p.normalize(outPath ?? '${defaultOptions.output}/coverage.json')); + final outFilePath = p.canonicalize( + outPath ?? '${defaultOptions.outputDirectory}/coverage.json'); final outFile = File(outFilePath)..createSync(recursive: true); out = outFile.openWrite(); } diff --git a/pkgs/coverage/bin/format_coverage.dart b/pkgs/coverage/bin/format_coverage.dart index d4a0ec7eb..a4537111e 100644 --- a/pkgs/coverage/bin/format_coverage.dart +++ b/pkgs/coverage/bin/format_coverage.dart @@ -146,7 +146,7 @@ Environment parseArgs(List arguments, CoverageOptions defaultOptions) { ) ..addOption('package', help: 'root directory of the package', - defaultsTo: defaultOptions.packagePath) + defaultsTo: defaultOptions.packageDirectory) ..addOption( 'in', abbr: 'i', @@ -239,11 +239,11 @@ Environment parseArgs(List arguments, CoverageOptions defaultOptions) { fail('Package spec "${args["package"]}" not found, or not a directory.'); } - if (args['in'] == null && defaultOptions.output == null) { + if (args['in'] == null && defaultOptions.outputDirectory == null) { fail('No input files given.'); } - final inputPath = - args['in'] as String? ?? '${defaultOptions.output}/coverage.json'; + final inputPath = args['in'] as String? ?? + '${defaultOptions.outputDirectory}/coverage.json'; final input = p.absolute(p.normalize(inputPath)); if (!FileSystemEntity.isDirectorySync(input) && !FileSystemEntity.isFileSync(input)) { @@ -253,11 +253,11 @@ Environment parseArgs(List arguments, CoverageOptions defaultOptions) { IOSink output; final outPath = args['out'] as String?; if (outPath == 'stdout' || - (outPath == null && defaultOptions.output == null)) { + (outPath == null && defaultOptions.outputDirectory == null)) { output = stdout; } else { final outFilePath = p - .absolute(p.normalize(outPath ?? '${defaultOptions.output}/lcov.info')); + .canonicalize(outPath ?? '${defaultOptions.outputDirectory}/lcov.info'); final outfile = File(outFilePath)..createSync(recursive: true); output = outfile.openWrite(); } diff --git a/pkgs/coverage/bin/test_with_coverage.dart b/pkgs/coverage/bin/test_with_coverage.dart index 046db9786..72111a52c 100644 --- a/pkgs/coverage/bin/test_with_coverage.dart +++ b/pkgs/coverage/bin/test_with_coverage.dart @@ -55,7 +55,7 @@ ArgParser _createArgParser(CoverageOptions defaultOptions) => ArgParser() ..addOption( 'package', help: 'Root directory of the package to test.', - defaultsTo: defaultOptions.packagePath, + defaultsTo: defaultOptions.packageDirectory, ) ..addOption( 'package-name', @@ -66,7 +66,7 @@ ArgParser _createArgParser(CoverageOptions defaultOptions) => ArgParser() ..addOption('port', help: 'VM service port.', defaultsTo: '8181') ..addOption( 'out', - defaultsTo: defaultOptions.output, + defaultsTo: defaultOptions.outputDirectory, abbr: 'o', help: 'Output directory. Defaults to /coverage.', ) @@ -174,19 +174,6 @@ ${parser.usage} Future main(List arguments) async { final defaultOptions = CoverageOptionsProvider().coverageOptions; final flags = await _parseArgs(arguments, defaultOptions); - - print('Flags: '); - print(' packageDir: ${flags.packageDir}'); - print(' packageName: ${flags.packageName}'); - print(' outDir: ${flags.outDir}'); - print(' port: ${flags.port}'); - print(' testScript: ${flags.testScript}'); - print(' functionCoverage: ${flags.functionCoverage}'); - print(' branchCoverage: ${flags.branchCoverage}'); - print(' scopeOutput: ${flags.scopeOutput}'); - print(' rest: ${flags.rest}'); - print(''); - 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 index 180455ae5..b981d7dd2 100644 --- a/pkgs/coverage/lib/src/coverage_options.dart +++ b/pkgs/coverage/lib/src/coverage_options.dart @@ -1,40 +1,55 @@ import 'dart:io'; import 'package:cli_config/cli_config.dart'; +import 'package:path/path.dart' as path; class CoverageOptions { const CoverageOptions({ - required this.output, + this.outputDirectory, required this.scopeOutput, required this.functionCoverage, required this.branchCoverage, - required this.packagePath, + required this.packageDirectory, this.packageName, required this.testScript, }); factory CoverageOptions.fromConfig( - Config options, CoverageOptions defaultOptions) { + Config options, CoverageOptions defaultOptions, String optionsFilePath) { + var outputDirectory = options.optionalString('output_directory') ?? + defaultOptions.outputDirectory; + var packageDirectory = options.optionalString('package_directory') ?? + defaultOptions.packageDirectory; + + if (outputDirectory != null && !path.isAbsolute(outputDirectory)) { + outputDirectory = path.canonicalize( + path.join(path.dirname(optionsFilePath), outputDirectory)); + } + if (!path.isAbsolute(packageDirectory)) { + packageDirectory = path.canonicalize( + path.join(path.dirname(optionsFilePath), packageDirectory)); + } + return CoverageOptions( - output: options.optionalString('out') ?? defaultOptions.output, + outputDirectory: outputDirectory, scopeOutput: options.optionalStringList('scope_output') ?? defaultOptions.scopeOutput, functionCoverage: options.optionalBool('function_coverage') ?? defaultOptions.functionCoverage, branchCoverage: options.optionalBool('branch_coverage') ?? defaultOptions.branchCoverage, - packagePath: - options.optionalString('package') ?? defaultOptions.packagePath, + packageDirectory: packageDirectory, packageName: options.optionalString('package_name') ?? defaultOptions.packageName, - testScript: options.optionalString('test') ?? defaultOptions.testScript, + testScript: + options.optionalString('test_script') ?? defaultOptions.testScript, ); } - final String? output; + final String? outputDirectory; final List scopeOutput; final bool functionCoverage; final bool branchCoverage; - final String packagePath; + final String packageDirectory; final String? packageName; final String testScript; } @@ -43,7 +58,7 @@ class CoverageOptionsProvider { CoverageOptionsProvider({ String? filePath, }) { - final file = _getOptionsFile(filePath ?? CoverageOptionsProvider.filePath); + final file = _getOptionsFile(filePath); final fileContents = file?.readAsStringSync(); final isFileEmpty = fileContents?.isEmpty ?? false; @@ -54,24 +69,48 @@ class CoverageOptionsProvider { fileSourceUri: file?.uri, ); - coverageOptions = CoverageOptions.fromConfig(options, defaultOptions); + 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(); + if (filePath == null) { + throw StateError('Could not find a pubspec.yaml file.'); + } - static const filePath = 'coverage.yaml'; + optionsFilePath = path.canonicalize(filePath); - static File? _getOptionsFile(String filePath) { - final file = File(filePath); + final file = File(optionsFilePath); return file.existsSync() ? file : null; } + String? _findOptionsFilePath() { + var currentDir = Directory.current; + + while (true) { + final pubSpecFilePath = path.join(currentDir.path, 'pubspec.yaml'); + if (File(pubSpecFilePath).existsSync()) { + return path.join(currentDir.path, defaultFilePath); + } + final parentDir = currentDir.parent; + if (parentDir.path == currentDir.path) { + return null; + } + currentDir = parentDir; + } + } + static const defaultOptions = CoverageOptions( - output: null, + outputDirectory: null, scopeOutput: [], functionCoverage: false, branchCoverage: false, - packagePath: '.', + packageDirectory: '.', packageName: null, testScript: 'test', ); diff --git a/pkgs/coverage/pubspec.yaml b/pkgs/coverage/pubspec.yaml index 6bd452470..af9790c1e 100644 --- a/pkgs/coverage/pubspec.yaml +++ b/pkgs/coverage/pubspec.yaml @@ -1,5 +1,5 @@ name: coverage -version: 1.12.0 +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 diff --git a/pkgs/coverage/test/collect_coverage_config_test.dart b/pkgs/coverage/test/collect_coverage_config_test.dart index 8c64240c3..b634d4e44 100644 --- a/pkgs/coverage/test/collect_coverage_config_test.dart +++ b/pkgs/coverage/test/collect_coverage_config_test.dart @@ -1,4 +1,5 @@ import 'package:coverage/src/coverage_options.dart'; +import 'package:path/path.dart' as path; import 'package:test/test.dart'; import 'test_util.dart'; @@ -6,7 +7,7 @@ import 'test_util.dart'; void main() { group('defaults when no yaml or command-line args provided', () { // Setup - final defaults = CoverageOptionsProvider.defaultOptions; + final defaults = CoverageOptionsProvider().coverageOptions; test('collect coverage', () { final collectedCoverage = parseArgsCollectCoverage([], defaults); @@ -14,23 +15,23 @@ void main() { expect(collectedCoverage.scopeOutput, defaults.scopeOutput); expect(collectedCoverage.functionCoverage, defaults.functionCoverage); expect(collectedCoverage.branchCoverage, defaults.branchCoverage); - expect(collectedCoverage.output, 'stdout'); + expect(collectedCoverage.outputDirectory, 'stdout'); }); test('format coverage', () { - final formattedCoverage = - parseArgsFormatCoverage([], defaults); + final formattedCoverage = parseArgsFormatCoverage([], defaults); - expect(formattedCoverage.output, 'stdout'); - expect(formattedCoverage.packagePath, defaults.packagePath); + expect(formattedCoverage.outputDirectory, 'stdout'); + expect(formattedCoverage.packageDirectory, defaults.packageDirectory); }); test('test with coverage', () async { final testCoverage = await parseArgsTestWithCoverage([], defaults); - expect(testCoverage.packagePath, getPackageDir(defaults.packagePath)); + expect(testCoverage.packageDirectory, + getAbsolutePath(defaults.packageDirectory)); expect(testCoverage.packageName, 'coverage'); - expect(testCoverage.output, 'coverage'); + expect(testCoverage.outputDirectory, 'coverage'); expect(testCoverage.testScript, defaults.testScript); expect(testCoverage.functionCoverage, defaults.functionCoverage); expect(testCoverage.branchCoverage, defaults.branchCoverage); @@ -45,25 +46,33 @@ void main() { // Parse arguments with empty command line args final collectedCoverage = parseArgsCollectCoverage([], configuredOptions); - final formattedCoverage = - parseArgsFormatCoverage([], configuredOptions); + final formattedCoverage = parseArgsFormatCoverage([], configuredOptions); final testCoverage = await parseArgsTestWithCoverage([], configuredOptions); // Verify collect coverage yaml values expect(collectedCoverage.scopeOutput, ['lib', 'src']); expect(collectedCoverage.functionCoverage, isTrue); expect(collectedCoverage.branchCoverage, isFalse); - expect(collectedCoverage.output, 'coverage/coverage.json'); + expect(collectedCoverage.outputDirectory, + getAbsolutePath('test/test_coverage_options/coverage/coverage.json')); // Verify format coverage yaml values - expect(formattedCoverage.output, 'coverage/lcov.info'); - expect(formattedCoverage.packagePath, '.'); + expect(formattedCoverage.outputDirectory, + getAbsolutePath('test/test_coverage_options/coverage/lcov.info')); + expect( + formattedCoverage.packageDirectory, + getAbsolutePath( + path.join('test/test_coverage_options', '../code_builder'))); // Verify test with coverage yaml values - expect(testCoverage.packagePath, getPackageDir('.')); + expect( + testCoverage.packageDirectory, + getAbsolutePath( + path.join('test/test_coverage_options', '../code_builder'))); expect(testCoverage.packageName, 'My Dart Package'); - expect(testCoverage.output, 'coverage'); - expect(testCoverage.testScript, 'test'); + expect(testCoverage.outputDirectory, + getAbsolutePath('test/test_coverage_options/coverage')); + expect(testCoverage.testScript, 'test1'); expect(testCoverage.functionCoverage, isTrue); expect(testCoverage.branchCoverage, isFalse); expect(testCoverage.scopeOutput, ['lib', 'src']); @@ -81,10 +90,16 @@ void main() { await parseArgsTestWithCoverage([], configuredOptions); final formattedCoverage = parseArgsFormatCoverage([], configuredOptions); - expect(collectedCoverage.output, 'custom_coverage/coverage.json'); + expect( + collectedCoverage.outputDirectory, + getAbsolutePath( + 'test/test_coverage_options/custom_coverage/coverage.json')); expect(collectedCoverage.scopeOutput, ['lib', 'test']); expect(collectedCoverage.functionCoverage, isFalse); - expect(formattedCoverage.output, 'custom_coverage/lcov.info'); + expect( + formattedCoverage.outputDirectory, + getAbsolutePath( + 'test/test_coverage_options/custom_coverage/lcov.info')); expect(testCoverage.packageName, 'Custom Dart Package'); expect(testCoverage.scopeOutput, ['lib', 'test']); }); @@ -104,15 +119,21 @@ void main() { expect(collectedCoverage.scopeOutput, ['lib', 'tools']); expect(collectedCoverage.branchCoverage, isFalse); expect(collectedCoverage.functionCoverage, isTrue); - expect(collectedCoverage.output, 'custom_lcov/coverage.json'); + expect( + collectedCoverage.outputDirectory, + getAbsolutePath( + 'test/test_coverage_options/custom_lcov/coverage.json')); // Verify format coverage yaml values - expect(formattedCoverage.output, 'custom_lcov/lcov.info'); - expect(formattedCoverage.packagePath, '.'); + expect(formattedCoverage.outputDirectory, + getAbsolutePath('test/test_coverage_options/custom_lcov/lcov.info')); + expect(formattedCoverage.packageDirectory, + getAbsolutePath('test/test_coverage_options')); // Verify test with coverage yaml values expect(testCoverage.packageName, 'coverage'); - expect(testCoverage.output, 'custom_lcov'); + expect(testCoverage.outputDirectory, + getAbsolutePath('test/test_coverage_options/custom_lcov')); expect(testCoverage.testScript, 'custom_test'); expect(testCoverage.functionCoverage, isTrue); }); @@ -142,18 +163,18 @@ void main() { ], configuredOptions); // Verify collect coverage command line args - expect(collectedCoverage.output, 'coverage.json'); + expect(collectedCoverage.outputDirectory, 'coverage.json'); expect(collectedCoverage.scopeOutput, ['lib']); expect(collectedCoverage.functionCoverage, isFalse); expect(collectedCoverage.branchCoverage, isTrue); // Verify format coverage command line args - expect(formattedCoverage.output, 'out_test.info'); - expect(formattedCoverage.packagePath, 'code_builder'); + expect(formattedCoverage.outputDirectory, 'out_test.info'); + expect(formattedCoverage.packageDirectory, 'code_builder'); // Verify test with coverage command line args expect(testCoverage.packageName, 'test'); - expect(testCoverage.output, 'test_coverage.json'); + expect(testCoverage.outputDirectory, 'test_coverage.json'); expect(testCoverage.testScript, 'test_test.dart'); expect(testCoverage.functionCoverage, isTrue); }); @@ -165,24 +186,25 @@ void main() { // Parse arguments with empty command line args final collectedCoverage = parseArgsCollectCoverage([], configuredOptions); - final formattedCoverage = - parseArgsFormatCoverage([], configuredOptions); + final formattedCoverage = parseArgsFormatCoverage([], configuredOptions); final testCoverage = await parseArgsTestWithCoverage([], configuredOptions); // Verify collect coverage defaults expect(collectedCoverage.scopeOutput, isEmpty); expect(collectedCoverage.functionCoverage, isFalse); expect(collectedCoverage.branchCoverage, isFalse); - expect(collectedCoverage.output, 'stdout'); + expect(collectedCoverage.outputDirectory, 'stdout'); // Verify format coverage defaults - expect(formattedCoverage.output, 'stdout'); - expect(formattedCoverage.packagePath, '.'); + expect(formattedCoverage.outputDirectory, 'stdout'); + expect(formattedCoverage.packageDirectory, + getAbsolutePath(path.join('test/test_coverage_options', '.'))); // Verify test with coverage defaults - expect(testCoverage.packagePath, getPackageDir('.')); + expect(testCoverage.packageDirectory, + getAbsolutePath(path.join('test/test_coverage_options', '.'))); expect(testCoverage.packageName, 'coverage'); - expect(testCoverage.output, 'coverage'); + expect(testCoverage.outputDirectory, 'coverage'); expect(testCoverage.testScript, 'test'); expect(testCoverage.functionCoverage, isFalse); expect(testCoverage.branchCoverage, isFalse); diff --git a/pkgs/coverage/test/test_coverage_options/all_field.yaml b/pkgs/coverage/test/test_coverage_options/all_field.yaml index 57f1cd41a..4e9e9ecd3 100644 --- a/pkgs/coverage/test/test_coverage_options/all_field.yaml +++ b/pkgs/coverage/test/test_coverage_options/all_field.yaml @@ -1,7 +1,7 @@ -out: "coverage" +output-directory: "coverage" scope-output: ["lib", "src"] function-coverage: true branch-coverage: false -package: "." +package-directory: "../code_builder" package-name: "My Dart Package" -test: "test" +test_script: "test1" diff --git a/pkgs/coverage/test/test_coverage_options/partial_fields1.yaml b/pkgs/coverage/test/test_coverage_options/partial_fields1.yaml index 137c0b355..5a2036aae 100644 --- a/pkgs/coverage/test/test_coverage_options/partial_fields1.yaml +++ b/pkgs/coverage/test/test_coverage_options/partial_fields1.yaml @@ -1,4 +1,4 @@ -out: 'custom_coverage' +output-directory: 'custom_coverage' scope-output: - 'lib' - 'test' diff --git a/pkgs/coverage/test/test_coverage_options/partial_fields2.yaml b/pkgs/coverage/test/test_coverage_options/partial_fields2.yaml index 1df32004d..01b956efc 100644 --- a/pkgs/coverage/test/test_coverage_options/partial_fields2.yaml +++ b/pkgs/coverage/test/test_coverage_options/partial_fields2.yaml @@ -2,7 +2,7 @@ scope-output: - 'lib' - 'tools' branch-coverage: false -out: 'custom_lcov' -package: '.' -test: 'custom_test' +output-directory: 'custom_lcov' +package-directory: '.' +test_script: 'custom_test' function-coverage: true \ No newline at end of file diff --git a/pkgs/coverage/test/test_util.dart b/pkgs/coverage/test/test_util.dart index 271e45727..5e76b0af2 100644 --- a/pkgs/coverage/test/test_util.dart +++ b/pkgs/coverage/test/test_util.dart @@ -107,18 +107,18 @@ CoverageOptions parseArgsCollectCoverage( String out; final outPath = args['out'] as String?; - if (outPath == null && defaultOptions.output == null) { + if (outPath == null && defaultOptions.outputDirectory == null) { out = 'stdout'; } else { - out = outPath ?? '${defaultOptions.output}/coverage.json'; + out = outPath ?? p.join(defaultOptions.outputDirectory!, 'coverage.json'); } return CoverageOptions( - output: out, + outputDirectory: out, scopeOutput: args['scope-output'] as List, functionCoverage: args['function-coverage'] as bool, branchCoverage: args['branch-coverage'] as bool, - packagePath: defaultOptions.packagePath, + packageDirectory: defaultOptions.packageDirectory, packageName: defaultOptions.packageName, testScript: defaultOptions.testScript, ); @@ -127,22 +127,22 @@ CoverageOptions parseArgsCollectCoverage( CoverageOptions parseArgsFormatCoverage( List arguments, CoverageOptions defaultOptions) { final parser = ArgParser() - ..addOption('package', defaultsTo: defaultOptions.packagePath) + ..addOption('package', defaultsTo: defaultOptions.packageDirectory) ..addOption('out', abbr: 'o'); final args = parser.parse(arguments); String out; final outPath = args['out'] as String?; - if (outPath == null && defaultOptions.output == null) { + if (outPath == null && defaultOptions.outputDirectory == null) { out = 'stdout'; } else { - out = outPath ?? '${defaultOptions.output}/lcov.info'; + out = outPath ?? p.join(defaultOptions.outputDirectory!, 'lcov.info'); } return CoverageOptions( - output: out, - packagePath: args['package'] as String, + outputDirectory: out, + packageDirectory: args['package'] as String, branchCoverage: defaultOptions.branchCoverage, functionCoverage: defaultOptions.functionCoverage, packageName: defaultOptions.packageName, @@ -156,7 +156,7 @@ Future parseArgsTestWithCoverage( final parser = ArgParser() ..addOption( 'package', - defaultsTo: defaultOptions.packagePath, + defaultsTo: defaultOptions.packageDirectory, ) ..addOption( 'package-name', @@ -164,7 +164,7 @@ Future parseArgsTestWithCoverage( ) ..addOption( 'out', - defaultsTo: defaultOptions.output, + defaultsTo: defaultOptions.outputDirectory, abbr: 'o', ) ..addOption('test', defaultsTo: defaultOptions.testScript) @@ -194,9 +194,9 @@ Future parseArgsTestWithCoverage( } return CoverageOptions( - packagePath: packageDir, + packageDirectory: packageDir, packageName: packageName as String, - output: (args['out'] as String?) ?? 'coverage', + outputDirectory: (args['out'] as String?) ?? 'coverage', testScript: args['test'] as String, functionCoverage: args['function-coverage'] as bool, branchCoverage: args['branch-coverage'] as bool, @@ -209,6 +209,6 @@ Future _packageNameFromConfig(String packageDir) async { return config?.packageOf(Uri.directory(packageDir))?.name; } -String getPackageDir(final String package) { +String getAbsolutePath(final String package) { return p.canonicalize(package); } From beac649f0afd709821814e73fb023d660c397474 Mon Sep 17 00:00:00 2001 From: Dhruv Maradiya Date: Mon, 20 Jan 2025 17:57:30 +0530 Subject: [PATCH 12/12] refactor: use absolute and normalize instead of canonicalize for path handling --- pkgs/coverage/bin/collect_coverage.dart | 4 +-- pkgs/coverage/bin/format_coverage.dart | 9 +++--- pkgs/coverage/lib/src/coverage_options.dart | 31 ++++++++++--------- .../partial_fields1.yaml | 2 +- .../partial_fields2.yaml | 2 +- 5 files changed, 25 insertions(+), 23 deletions(-) diff --git a/pkgs/coverage/bin/collect_coverage.dart b/pkgs/coverage/bin/collect_coverage.dart index e25cd0049..f8d5bcc29 100644 --- a/pkgs/coverage/bin/collect_coverage.dart +++ b/pkgs/coverage/bin/collect_coverage.dart @@ -136,8 +136,8 @@ Options _parseArgs(List arguments, CoverageOptions defaultOptions) { (outPath == null && defaultOptions.outputDirectory == null)) { out = stdout; } else { - final outFilePath = p.canonicalize( - outPath ?? '${defaultOptions.outputDirectory}/coverage.json'); + final outFilePath = p.normalize(outPath ?? + p.absolute(defaultOptions.outputDirectory!, 'coverage.json')); final outFile = File(outFilePath)..createSync(recursive: true); out = outFile.openWrite(); } diff --git a/pkgs/coverage/bin/format_coverage.dart b/pkgs/coverage/bin/format_coverage.dart index a4537111e..44f06d491 100644 --- a/pkgs/coverage/bin/format_coverage.dart +++ b/pkgs/coverage/bin/format_coverage.dart @@ -242,9 +242,8 @@ Environment parseArgs(List arguments, CoverageOptions defaultOptions) { if (args['in'] == null && defaultOptions.outputDirectory == null) { fail('No input files given.'); } - final inputPath = args['in'] as String? ?? - '${defaultOptions.outputDirectory}/coverage.json'; - final input = p.absolute(p.normalize(inputPath)); + 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.'); @@ -256,8 +255,8 @@ Environment parseArgs(List arguments, CoverageOptions defaultOptions) { (outPath == null && defaultOptions.outputDirectory == null)) { output = stdout; } else { - final outFilePath = p - .canonicalize(outPath ?? '${defaultOptions.outputDirectory}/lcov.info'); + final outFilePath = p.normalize( + outPath ?? p.absolute(defaultOptions.outputDirectory!, 'lcov.info')); final outfile = File(outFilePath)..createSync(recursive: true); output = outfile.openWrite(); } diff --git a/pkgs/coverage/lib/src/coverage_options.dart b/pkgs/coverage/lib/src/coverage_options.dart index b981d7dd2..2ee7b5337 100644 --- a/pkgs/coverage/lib/src/coverage_options.dart +++ b/pkgs/coverage/lib/src/coverage_options.dart @@ -14,19 +14,21 @@ class CoverageOptions { }); factory CoverageOptions.fromConfig( - Config options, CoverageOptions defaultOptions, String optionsFilePath) { + Config options, CoverageOptions defaultOptions, String? optionsFilePath) { var outputDirectory = options.optionalString('output_directory') ?? defaultOptions.outputDirectory; var packageDirectory = options.optionalString('package_directory') ?? defaultOptions.packageDirectory; - if (outputDirectory != null && !path.isAbsolute(outputDirectory)) { - outputDirectory = path.canonicalize( - path.join(path.dirname(optionsFilePath), outputDirectory)); - } - if (!path.isAbsolute(packageDirectory)) { - packageDirectory = path.canonicalize( - path.join(path.dirname(optionsFilePath), 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( @@ -74,18 +76,19 @@ class CoverageOptionsProvider { } late final CoverageOptions coverageOptions; - late final String optionsFilePath; + late final String? optionsFilePath; static const defaultFilePath = 'coverage_options.yaml'; File? _getOptionsFile(String? filePath) { filePath ??= _findOptionsFilePath(); - if (filePath == null) { - throw StateError('Could not find a pubspec.yaml file.'); - } - optionsFilePath = path.canonicalize(filePath); + optionsFilePath = filePath != null ? path.canonicalize(filePath) : null; + + if (optionsFilePath == null) { + return null; + } - final file = File(optionsFilePath); + final file = File(optionsFilePath!); return file.existsSync() ? file : null; } diff --git a/pkgs/coverage/test/test_coverage_options/partial_fields1.yaml b/pkgs/coverage/test/test_coverage_options/partial_fields1.yaml index 5a2036aae..47401c716 100644 --- a/pkgs/coverage/test/test_coverage_options/partial_fields1.yaml +++ b/pkgs/coverage/test/test_coverage_options/partial_fields1.yaml @@ -2,4 +2,4 @@ output-directory: 'custom_coverage' scope-output: - 'lib' - 'test' -package-name: 'Custom Dart Package' \ No newline at end of file +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 index 01b956efc..0905871ab 100644 --- a/pkgs/coverage/test/test_coverage_options/partial_fields2.yaml +++ b/pkgs/coverage/test/test_coverage_options/partial_fields2.yaml @@ -5,4 +5,4 @@ branch-coverage: false output-directory: 'custom_lcov' package-directory: '.' test_script: 'custom_test' -function-coverage: true \ No newline at end of file +function-coverage: true