Skip to content
Merged
6 changes: 4 additions & 2 deletions lib/src/cli/dart_cli.dart
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,12 @@ class Dart {
required Logger logger,
String cwd = '.',
bool recursive = false,
bool checkIgnore = true,
bool showUncovered = false,
bool collectCoverage = false,
bool optimizePerformance = false,
Set<String> ignore = const {},
double? minCoverage,
bool showUncovered = false,
String? excludeFromCoverage,
CoverageCollectionMode collectCoverageFrom = CoverageCollectionMode.imports,
String? randomSeed,
Expand All @@ -119,11 +120,12 @@ class Dart {
testType: TestRunType.dart,
cwd: cwd,
recursive: recursive,
checkIgnore: checkIgnore,
showUncovered: showUncovered,
collectCoverage: collectCoverage,
optimizePerformance: optimizePerformance,
ignore: ignore,
minCoverage: minCoverage,
showUncovered: showUncovered,
excludeFromCoverage: excludeFromCoverage,
collectCoverageFrom: collectCoverageFrom,
randomSeed: randomSeed,
Expand Down
2 changes: 2 additions & 0 deletions lib/src/cli/test_cli_runner.dart
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ class TestCLIRunner {
void Function(String)? stderr,
GeneratorBuilder buildGenerator = MasonGenerator.fromBundle,
String? reportOn,
bool checkIgnore = false,
@visibleForTesting VeryGoodTestRunner? overrideTestRunner,
}) async {
final initialCwd = cwd;
Expand Down Expand Up @@ -206,6 +207,7 @@ class TestCLIRunner {
final hitmap = await coverage.HitMap.parseFiles(
files,
packagePath: packagesPath,
checkIgnoredLines: checkIgnore,
);

final resolver = await coverage.Resolver.create(
Expand Down
27 changes: 27 additions & 0 deletions lib/src/commands/dart/commands/dart_test_command.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ class DartTestOptions {
required this.platform,
required this.rest,
required this.reportOn,
required this.runSkipped,
required this.checkIgnore,
});

/// Parses [ArgResults] into a [DartTestOptions] instance.
Expand Down Expand Up @@ -54,6 +56,8 @@ class DartTestOptions {
final forceAnsi = argResults['force-ansi'] as bool?;
final platform = argResults['platform'] as String?;
final reportOn = argResults['report-on'] as String?;
final runSkipped = argResults['run-skipped'] as bool;
final checkIgnore = argResults['check-ignore'] as bool;
final rest = argResults.rest;

return DartTestOptions._(
Expand All @@ -71,6 +75,8 @@ class DartTestOptions {
forceAnsi: forceAnsi,
platform: platform,
reportOn: reportOn,
runSkipped: runSkipped,
checkIgnore: checkIgnore,
rest: rest,
);
}
Expand Down Expand Up @@ -118,6 +124,12 @@ class DartTestOptions {
/// An optional file path to report coverage information to.
final String? reportOn;

/// Whether to run skipped tests instead of skipping them.
final bool runSkipped;

/// Whether to check for and respect coverage ignore comments.
final bool checkIgnore;

/// The remaining arguments passed to the `dart test` command.
final List<String> rest;
}
Expand All @@ -143,6 +155,7 @@ typedef DartTestCommandCall =
void Function(String)? stdout,
void Function(String)? stderr,
String? reportOn,
bool checkIgnore,
});

/// {@template dart_test_command}
Expand Down Expand Up @@ -253,6 +266,18 @@ class DartTestCommand extends Command<int> {
'platform',
help: 'The platform to run tests on. ',
valueHelp: 'chrome|vm',
)
..addFlag(
'run-skipped',
help: 'Run skipped tests instead of skipping them.',
negatable: false,
)
..addFlag(
'check-ignore',
Comment thread
marcossevilla marked this conversation as resolved.
defaultsTo: true,
help:
'Whether to check for and respect coverage ignore comments '
'(e.g. // coverage:ignore-line).',
);
}

Expand Down Expand Up @@ -316,11 +341,13 @@ This command should be run from the root of your Dart project.''');
if (options.excludeTags != null) ...['-x', options.excludeTags!],
if (options.tags != null) ...['-t', options.tags!],
if (options.failFast) '--fail-fast',
if (options.runSkipped) '--run-skipped',
if (options.platform != null) ...['--platform', options.platform!],
if (options.platform == null) ...['-j', options.concurrency],
...options.rest,
],
reportOn: options.reportOn,
checkIgnore: options.checkIgnore,
);
if (results.any((code) => code != ExitCode.success.code)) {
return ExitCode.unavailable.code;
Expand Down
17 changes: 17 additions & 0 deletions lib/src/mcp/mcp_server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,17 @@ The available values are: chrome, vm, android, ios.
Only one value can be selected.
''',
),
'run_skipped': BooleanSchema(
description:
'Run skipped tests instead of skipping them. '
'Only applies to Dart tests (dart: true).',
),
'check_ignore': BooleanSchema(
description:
'Whether to check for and respect coverage ignore comments '
'(e.g. // coverage:ignore-line). '
'Only applies to Dart tests (dart: true).',
),
},
),
),
Expand Down Expand Up @@ -359,6 +370,12 @@ Only one value can be selected.
if (args['platform'] != null) {
cliArgs.addAll(['--platform', args['platform']! as String]);
}
if (args['run_skipped'] == true) {
cliArgs.add('--run-skipped');
}
if (args['check_ignore'] == true) {
cliArgs.add('--check-ignore');
}

return cliArgs;
}
Expand Down
50 changes: 42 additions & 8 deletions test/src/cli/test_cli_runner_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -780,12 +780,10 @@ void main() {
stdout: stdoutLogs.add,
stderr: stderrLogs.add,
overrideTestRunner: testRunner(
Stream.fromIterable(
[
const DoneTestEvent(success: true, time: 0),
const ExitTestEvent(exitCode: 0, time: 0),
],
),
Stream.fromIterable([
const DoneTestEvent(success: true, time: 0),
const ExitTestEvent(exitCode: 0, time: 0),
]),
onStart: () {
expect(lcovFile.existsSync(), isFalse);
lcovFile.createSync(recursive: true);
Expand All @@ -805,6 +803,42 @@ void main() {
expect(testRunnerArgs, equals(['--coverage=coverage']));
});

test('runs dart tests w/coverage and checkIgnore', () async {
final tempDirectory = Directory.systemTemp.createTempSync();
addTearDown(() => tempDirectory.deleteSync(recursive: true));

final lcovFile = File(
p.join(tempDirectory.path, 'coverage', 'lcov.info'),
);
File(p.join(tempDirectory.path, 'pubspec.yaml')).createSync();
Directory(p.join(tempDirectory.path, 'test')).createSync();
lcovFile.createSync(recursive: true);

await expectLater(
TestCLIRunner.test(
testType: TestRunType.dart,
cwd: tempDirectory.path,
logger: logger,
checkIgnore: true,
collectCoverage: true,
stdout: stdoutLogs.add,
stderr: stderrLogs.add,
overrideTestRunner: testRunner(
Stream.fromIterable([
const DoneTestEvent(success: true, time: 0),
const ExitTestEvent(exitCode: 0, time: 0),
]),
onStart: () {
expect(lcovFile.existsSync(), isFalse);
lcovFile.createSync(recursive: true);
},
),
),
completion(equals([ExitCode.success.code])),
);
expect(testRunnerArgs, equals(['--coverage=coverage']));
});

test('runs tests w/coverage + min-coverage 100 (pass)', () async {
final tempDirectory = Directory.systemTemp.createTempSync();
addTearDown(() => tempDirectory.deleteSync(recursive: true));
Expand All @@ -816,8 +850,9 @@ void main() {
TestCLIRunner.test(
testType: TestRunType.flutter,
cwd: tempDirectory.path,
collectCoverage: true,
logger: logger,
minCoverage: 100,
collectCoverage: true,
stdout: stdoutLogs.add,
stderr: stderrLogs.add,
overrideTestRunner: testRunner(
Expand All @@ -833,7 +868,6 @@ void main() {
..writeAsStringSync(lcov100);
},
),
logger: logger,
),
completion(equals([ExitCode.success.code])),
);
Expand Down
47 changes: 45 additions & 2 deletions test/src/commands/dart/commands/dart_test_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ const expectedTestUsage = [
' --force-ansi Whether to force ansi output. If not specified, it will maintain the default behavior based on stdout and stderr.\n'
' --report-on=<lib/> An optional file path to report coverage information to. This should be a path relative to the current working directory.\n'
' --platform=<chrome|vm> The platform to run tests on. \n'
' --run-skipped Run skipped tests instead of skipping them.\n'
' --[no-]check-ignore Whether to check for and respect coverage ignore comments (e.g. // coverage:ignore-line).\n'
' (defaults to on)\n'
'\n'
'Run "very_good help" to see global options.',
];
Expand All @@ -56,10 +59,11 @@ abstract class DartTestCommandCall {
Future<List<int>> call({
String cwd = '.',
bool recursive = false,
bool checkIgnore = true,
bool showUncovered = false,
bool collectCoverage = false,
bool optimizePerformance = false,
double? minCoverage,
bool showUncovered = false,
String? excludeFromCoverage,
CoverageCollectionMode collectCoverageFrom = CoverageCollectionMode.imports,
String? randomSeed,
Expand Down Expand Up @@ -102,10 +106,11 @@ void main() {
() => dartTest(
cwd: any(named: 'cwd'),
recursive: any(named: 'recursive'),
checkIgnore: any(named: 'checkIgnore'),
showUncovered: any(named: 'showUncovered'),
collectCoverage: any(named: 'collectCoverage'),
optimizePerformance: any(named: 'optimizePerformance'),
minCoverage: any(named: 'minCoverage'),
showUncovered: any(named: 'showUncovered'),
excludeFromCoverage: any(named: 'excludeFromCoverage'),
collectCoverageFrom: any(named: 'collectCoverageFrom'),
randomSeed: any(named: 'randomSeed'),
Expand All @@ -120,8 +125,10 @@ void main() {
when<dynamic>(() => argResults['concurrency']).thenReturn(concurrency);
when<dynamic>(() => argResults['recursive']).thenReturn(false);
when<dynamic>(() => argResults['coverage']).thenReturn(false);
when<dynamic>(() => argResults['check-ignore']).thenReturn(true);
when<dynamic>(() => argResults['show-uncovered']).thenReturn(false);
when<dynamic>(() => argResults['fail-fast']).thenReturn(false);
when<dynamic>(() => argResults['run-skipped']).thenReturn(false);
when<dynamic>(() => argResults['optimization']).thenReturn(true);
when<dynamic>(() => argResults['platform']).thenReturn(null);
when<dynamic>(
Expand Down Expand Up @@ -222,6 +229,7 @@ void main() {
logger: any(named: 'logger'),
stdout: any(named: 'stdout'),
stderr: any(named: 'stderr'),
checkIgnore: any(named: 'checkIgnore'),
),
).thenAnswer(
(_) async => [ExitCode.success.code, ExitCode.unavailable.code],
Expand Down Expand Up @@ -495,6 +503,7 @@ void main() {
logger: any(named: 'logger'),
stdout: any(named: 'stdout'),
stderr: any(named: 'stderr'),
checkIgnore: any(named: 'checkIgnore'),
),
).thenThrow(exception);
final result = await testCommand.run();
Expand Down Expand Up @@ -540,6 +549,7 @@ void main() {
logger: any(named: 'logger'),
stdout: any(named: 'stdout'),
stderr: any(named: 'stderr'),
checkIgnore: any(named: 'checkIgnore'),
),
).thenThrow(exception);
final result = await testCommand.run();
Expand Down Expand Up @@ -574,6 +584,7 @@ void main() {
logger: any(named: 'logger'),
stdout: any(named: 'stdout'),
stderr: any(named: 'stderr'),
checkIgnore: any(named: 'checkIgnore'),
),
).thenThrow(exception);
final result = await testCommand.run();
Expand Down Expand Up @@ -654,6 +665,7 @@ void main() {
logger: any(named: 'logger'),
stdout: any(named: 'stdout'),
stderr: any(named: 'stderr'),
checkIgnore: any(named: 'checkIgnore'),
),
).thenThrow(exception);
final result = await testCommand.run();
Expand Down Expand Up @@ -732,5 +744,36 @@ void main() {
).called(1);
},
);

test('completes normally --run-skipped', () async {
when<dynamic>(() => argResults['run-skipped']).thenReturn(true);
final result = await testCommand.run();
expect(result, equals(ExitCode.success.code));
verify(
() => dartTest(
optimizePerformance: true,
arguments: ['--run-skipped', ...defaultArguments],
logger: logger,
stdout: logger.write,
stderr: logger.err,
),
).called(1);
});

test('completes normally --no-check-ignore', () async {
when<dynamic>(() => argResults['check-ignore']).thenReturn(false);
final result = await testCommand.run();
expect(result, equals(ExitCode.success.code));
verify(
() => dartTest(
optimizePerformance: true,
arguments: defaultArguments,
logger: logger,
stdout: logger.write,
stderr: logger.err,
checkIgnore: false,
),
).called(1);
});
});
}
4 changes: 4 additions & 0 deletions test/src/mcp/mcp_server_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,8 @@ void main() {
'dart-define': 'foo=bar',
'dart-define-from-file': 'my_file.json',
'platform': 'chrome',
'run_skipped': true,
'check_ignore': true,
},
),
),
Expand Down Expand Up @@ -368,6 +370,8 @@ void main() {
'my_file.json',
'--platform',
'chrome',
'--run-skipped',
'--check-ignore',
]);
});

Expand Down