-
Notifications
You must be signed in to change notification settings - Fork 224
Add .fromEnvironment flags to the global declarer #2561
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,6 @@ | ||
| ## 0.6.14 | ||
| * Support environment flags for configuring the global declarer. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This reads like we support reading from I wonder, should we be trying to do that instead? For the VM case it's probably little consequence either way, for AOT native we'd probably prefer Would there be value in supporting a |
||
|
|
||
| ## 0.6.13 | ||
|
|
||
| * Require Dart 3.7 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,7 @@ import 'package:test_api/src/backend/invoker.dart'; // ignore: implementation_im | |
|
|
||
| import 'runner/engine.dart'; | ||
| import 'runner/plugin/environment.dart'; | ||
| import 'runner/reporter/compact.dart'; | ||
| import 'runner/reporter/expanded.dart'; | ||
| import 'runner/runner_suite.dart'; | ||
| import 'runner/suite.dart'; | ||
|
|
@@ -60,13 +61,32 @@ Declarer get _declarer { | |
| var engine = Engine(); | ||
| engine.suiteSink.add(suite); | ||
| engine.suiteSink.close(); | ||
| ExpandedReporter.watch( | ||
| engine, | ||
| PrintSink(), | ||
| color: true, | ||
| printPath: false, | ||
| printPlatform: false, | ||
| var useCompactReporter = const bool.fromEnvironment( | ||
| 'test_core.compactReporter', | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it would be better to match the args format for consistency. We can't import the |
||
| ); | ||
| var colors = const bool.fromEnvironment( | ||
| 'test_core.colors', | ||
| defaultValue: true, | ||
| ); | ||
| var printPath = const bool.fromEnvironment('test_core.printPath'); | ||
| var printPlatform = const bool.fromEnvironment('test_core.printPlatform'); | ||
| if (useCompactReporter) { | ||
| CompactReporter.watch( | ||
| engine, | ||
| PrintSink(), | ||
| color: colors, | ||
| printPath: printPath, | ||
| printPlatform: printPlatform, | ||
| ); | ||
| } else { | ||
| ExpandedReporter.watch( | ||
| engine, | ||
| PrintSink(), | ||
| color: colors, | ||
| printPath: printPath, | ||
| printPlatform: printPlatform, | ||
| ); | ||
| } | ||
|
|
||
| var success = await runZoned( | ||
| () => Invoker.guard(engine.run), | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| // Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the purpose of this file? Is there a particular reason is it written so non-idiomatically for a Dart unit test? Can we get by with a String in the file that needs this? For historical reasons most of the test for |
||
| // for details. All rights reserved. Use of this source code is governed by a | ||
| // BSD-style license that can be found in the LICENSE file. | ||
|
|
||
| import 'package:test/test.dart' as test_package; | ||
|
|
||
| typedef TestFunction = dynamic Function(); | ||
|
|
||
| class Test { | ||
| final String name; | ||
| final TestFunction function; | ||
|
|
||
| Test(this.name, this.function); | ||
| } | ||
|
|
||
| void main() { | ||
| var tests = [ | ||
| Test('a', () {}), | ||
| Test('b', () => throw test_package.TestFailure('b')), | ||
| Test('c', () {}), | ||
| Test('d', () => throw test_package.TestFailure('d')), | ||
| Test('e', () {}), | ||
| ]; | ||
|
|
||
| for (var test in tests) { | ||
| test_package.test(test.name, () async { | ||
| try { | ||
| await test.function(); | ||
| } finally {} | ||
| }); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,240 @@ | ||
| // Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file | ||
| // for details. All rights reserved. Use of this source code is governed by a | ||
| // BSD-style license that can be found in the LICENSE file. | ||
|
|
||
| import 'dart:io'; | ||
| import 'dart:typed_data'; | ||
|
|
||
| import 'package:test/test.dart'; | ||
|
|
||
| Future<void> main() async { | ||
| group('environment', () { | ||
| test('default', () async { | ||
| await _run([], contains: _generateExpectedOutput()); | ||
| }); | ||
|
|
||
| group('colors', () { | ||
| test('true', () async { | ||
| await _run([ | ||
| '-Dtest_core.colors=true', | ||
| ], contains: _generateExpectedOutput()); | ||
| }); | ||
| test('false', () async { | ||
| await _run([ | ||
| '-Dtest_core.colors=false', | ||
| ], contains: _generateExpectedOutput(colors: false)); | ||
| }); | ||
| }); | ||
|
|
||
| group('compactReporter', () { | ||
| test('false', () async { | ||
| await _run([ | ||
| '-Dtest_core.compactReporter=false', | ||
| ], contains: _generateExpectedOutput()); | ||
| }); | ||
| test('true', () async { | ||
| await _run([ | ||
| '-Dtest_core.compactReporter=true', | ||
| ], contains: _generateExpectedOutput(compactReporter: true)); | ||
| }); | ||
| }); | ||
|
|
||
| group('printPath', () { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we have a specific use case in mind for the I think it's preferable to only add configuration support for the specific values we need (I think that's control over the reporter and use colors IIRC?) and we can add further support later if we need to. |
||
| test('false', () async { | ||
| await _run([ | ||
| '-Dtest_core.printPath=false', | ||
| ], contains: _generateExpectedOutput()); | ||
| }); | ||
| test('true', () async { | ||
| await _run([ | ||
| '-Dtest_core.printPath=true', | ||
| ], contains: _generateExpectedOutput(path: ': .')); | ||
| }); | ||
| }); | ||
|
|
||
| group('printPlatform', () { | ||
| test('false', () async { | ||
| await _run([ | ||
| '-Dtest_core.printPlatform=false', | ||
| ], contains: _generateExpectedOutput()); | ||
| }); | ||
| test('true', () async { | ||
| await _run([ | ||
| '-Dtest_core.printPlatform=true', | ||
| ], contains: _generateExpectedOutput(platform: ' [VM, Kernel]')); | ||
| }); | ||
| }); | ||
|
|
||
| group('mixed', () { | ||
| test('compactReporter, no colors', () async { | ||
| await _run( | ||
| ['-Dtest_core.compactReporter=true', '-Dtest_core.colors=false'], | ||
| contains: _generateExpectedOutput( | ||
| compactReporter: true, | ||
| colors: false, | ||
| ), | ||
| ); | ||
| }); | ||
| }); | ||
| }); | ||
| } | ||
|
|
||
| const boldCode = '\x1B[1m'; | ||
| const cyanCode = '\x1B[36m'; | ||
| const debugPrint = false; | ||
| const greenCode = '\x1B[32m'; | ||
| const noColorCode = '\x1B[0m'; | ||
| const redCode = '\x1B[31m'; | ||
|
|
||
| String _decode(dynamic stdout) { | ||
| if (stdout is String) { | ||
| return stdout; | ||
| } else { | ||
| return systemEncoding.decoder.convert(stdout as Uint8List); | ||
| } | ||
| } | ||
|
|
||
| List<Pattern> _generateExpectedOutput({ | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [optional] I don't think we need to verify every character of the output. It might be easier to test that the compact output does contain a carriage return and the expanded output doesn't. We already test the particular details of the reporters in other tests, we just need to test for any sign that the config is plumbed through correctly. |
||
| bool colors = true, | ||
| bool compactReporter = false, | ||
| String path = '', | ||
| String platform = '', | ||
| }) { | ||
| String green(String text) => colors ? '$greenCode$text$noColorCode' : text; | ||
|
|
||
| String red(String text) => colors ? '$redCode$text$noColorCode' : text; | ||
|
|
||
| String bold(String text) => colors ? '$boldCode$text$noColorCode' : text; | ||
|
|
||
| String cyan(String text) => colors ? '$cyanCode$text$noColorCode' : text; | ||
|
|
||
| Pattern hidden(String text) => RegExp( | ||
| '${RegExp.escape(text)}${colors ? RegExp.escape(noColorCode) : ''} +\\r', | ||
| ); | ||
|
|
||
| Pattern line( | ||
| int success, | ||
| int fail, | ||
| String text, { | ||
| bool hide = false, | ||
| bool failure = false, | ||
| bool end = false, | ||
| }) { | ||
| var sb = StringBuffer(); | ||
| sb.write(green('+$success')); | ||
| if (fail != 0) { | ||
| sb.write(red(' $fail')); | ||
| } | ||
| if (end) { | ||
| sb.write(': ${red(text)}'); | ||
| } else { | ||
| sb.write('$path:$platform $text'); | ||
| if (failure) { | ||
| sb.write(' ${bold(red('[E]'))}'); | ||
| } | ||
| } | ||
| var result = sb.toString(); | ||
| if (hide) { | ||
| return hidden(result); | ||
| } else { | ||
| return result; | ||
| } | ||
| } | ||
|
|
||
| return [ | ||
| line(0, 0, 'a', hide: compactReporter), | ||
| line(1, 0, 'b', hide: compactReporter), | ||
| line(1, -1, 'b', failure: true), | ||
| if (compactReporter) cyan('To run this test again:'), | ||
| line(1, -1, 'c', hide: compactReporter), | ||
| line(2, -1, 'd', hide: compactReporter), | ||
| line(2, -2, 'd', failure: true), | ||
| if (compactReporter) cyan('To run this test again:'), | ||
| line(2, -2, 'e', hide: compactReporter), | ||
| line(3, -2, 'Some tests failed.', end: true), | ||
| ]; | ||
| } | ||
|
|
||
| Future<void> _run( | ||
| List<String> args, { | ||
| List<Pattern> contains = const [], | ||
| List<String>? doesNotContain, | ||
| }) async { | ||
| if (debugPrint) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The typical way we'd handle this is with |
||
| print( | ||
| '====================================================================', | ||
| ); | ||
| print('args: $args'); | ||
| } | ||
| var tester = File('test/scaffolding/fixtures/tester.dart'); | ||
| var result = await Process.run(Platform.executable, [ | ||
| ...args, | ||
| tester.absolute.path, | ||
| ]); | ||
|
|
||
| var stdout = _decode(result.stdout); | ||
| if (debugPrint) { | ||
| print(stdout); | ||
| } | ||
|
|
||
| expect(stdout, _stringMatchesInOrder(contains)); | ||
| if (doesNotContain != null) { | ||
| for (var text in doesNotContain) { | ||
| expect(stdout, isNot(stringContainsInOrder([text]))); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| Matcher _stringMatchesInOrder(List<Pattern> substrings) => | ||
| _StringMatchesInOrder(substrings); | ||
|
|
||
| class _StringMatchesInOrder implements Matcher { | ||
| final List<Pattern> _patterns; | ||
|
|
||
| const _StringMatchesInOrder(this._patterns); | ||
|
|
||
| @override | ||
| Description describe(Description description) => | ||
| description.addAll('a string containing ', ', ', ' in order', _patterns); | ||
|
|
||
| @override | ||
| Description describeMismatch( | ||
| dynamic item, | ||
| Description mismatchDescription, | ||
| Map<dynamic, dynamic> matchState, | ||
| bool verbose, | ||
| ) { | ||
| var patternIndex = _matches(item, matchState); | ||
| var pattern = _patterns[patternIndex!]; | ||
| return mismatchDescription.add( | ||
| 'Pattern #$patternIndex, $pattern, not found.', | ||
| ); | ||
| } | ||
|
|
||
| @override | ||
| bool matches(dynamic item, Map<dynamic, dynamic> matchState) { | ||
| return _matches(item, matchState) == null; | ||
| } | ||
|
|
||
| int? _matches(dynamic item, Map<dynamic, dynamic> matchState) { | ||
| item as String; | ||
| var fromIndex = 0; | ||
| for ( | ||
| var patternIndex = 0; | ||
| patternIndex < _patterns.length; | ||
| patternIndex++ | ||
| ) { | ||
| var s = _patterns[patternIndex]; | ||
| if (s is String) { | ||
| var index = item.indexOf(s, fromIndex); | ||
| if (index < 0) return patternIndex; | ||
| fromIndex = index + s.length; | ||
| } else { | ||
| var matches = s.allMatches(item, fromIndex); | ||
| if (matches.isEmpty) return patternIndex; | ||
| fromIndex = matches.first.end; | ||
| } | ||
| } | ||
| return null; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be a bit more explicit here?
It'd also be nice to update this section of the readme – so this feature isn't lost.
https://github.com/dart-lang/test/tree/master/pkgs/test#selecting-a-test-reporter
I have a few packages where I'd use this, too!