From bffe499b74f83a4b8358d003b7b2e79348461cd8 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Fri, 21 Aug 2026 15:09:17 -0700 Subject: [PATCH 1/4] Reformat with the latest Dart SDK --- lib/cli_script.dart | 298 ++++++++------ lib/src/buffered_script.dart | 61 +-- lib/src/cli_arguments.dart | 14 +- lib/src/config.dart | 20 +- lib/src/environment.dart | 7 +- lib/src/extensions/byte_stream.dart | 26 +- lib/src/extensions/line_and_span_stream.dart | 97 +++-- lib/src/extensions/line_stream.dart | 167 +++++--- lib/src/script.dart | 408 +++++++++++-------- lib/src/stdio.dart | 66 +-- lib/src/stdio_group.dart | 6 +- lib/src/temp.dart | 65 ++- lib/src/util.dart | 16 +- lib/src/util/entangled_controllers.dart | 13 +- lib/src/util/named_stream_transformer.dart | 29 +- lib/src/util/sink_base.dart | 12 +- pubspec.yaml | 2 +- test/buffered_script_test.dart | 37 +- test/capture_test.dart | 330 ++++++++------- test/cli_arguments_test.dart | 32 +- test/environment_test.dart | 90 ++-- test/ls_test.dart | 22 +- test/pipe_test.dart | 189 +++++---- test/script_wrapper_test.dart | 26 +- test/signal_test.dart | 164 ++++---- test/stdio_test.dart | 298 ++++++++------ test/sub_process_test.dart | 143 ++++--- test/temp_test.dart | 128 ++++-- test/transform_test.dart | 277 +++++++------ test/util.dart | 63 +-- test/util/delayed_completer_test.dart | 12 +- test/util/entangled_controllers_test.dart | 150 ++++--- 32 files changed, 1914 insertions(+), 1354 deletions(-) diff --git a/lib/cli_script.dart b/lib/cli_script.dart index 9d748d1..1936d04 100644 --- a/lib/cli_script.dart +++ b/lib/cli_script.dart @@ -64,27 +64,37 @@ export 'src/temp.dart'; /// If [debug] is `true`, extra information about [Script]s' lifecycles will be /// printed directly to stderr. As the name suggests, this is intended for use /// only when debugging. -void wrapMain(FutureOr Function() callback, - {bool chainStackTraces = true, - bool? printScriptException, - bool verboseTrace = false, - bool debug = false}) { - withConfig(() { - Chain.capture(callback, onError: (error, chain) { - if (error is! ScriptException) { - stderr.writeln(error); - stderr.writeln(terseChain(chain)); - // Use the same exit code that Dart does for unhandled exceptions. - exit(254); - } +void wrapMain( + FutureOr Function() callback, { + bool chainStackTraces = true, + bool? printScriptException, + bool verboseTrace = false, + bool debug = false, +}) { + withConfig( + () { + Chain.capture( + callback, + onError: (error, chain) { + if (error is! ScriptException) { + stderr.writeln(error); + stderr.writeln(terseChain(chain)); + // Use the same exit code that Dart does for unhandled exceptions. + exit(254); + } - if (printScriptException ?? debug) { - stderr.writeln(error); - stderr.writeln(terseChain(chain)); - } - exit(error.exitCode); - }, when: chainStackTraces); - }, verboseTrace: verboseTrace, debug: debug); + if (printScriptException ?? debug) { + stderr.writeln(error); + stderr.writeln(terseChain(chain)); + } + exit(error.exitCode); + }, + when: chainStackTraces, + ); + }, + verboseTrace: verboseTrace, + debug: debug, + ); } /// Runs an executable for its side effects. @@ -98,21 +108,23 @@ void wrapMain(FutureOr Function() callback, /// All other arguments are forwarded to [Process.start]. /// /// [the README]: https://github.com/google/dart_cli_script/blob/main/README.md#argument-parsing -Future run(String executableAndArgs, - {Iterable? args, - String? name, - String? workingDirectory, - Map? environment, - bool includeParentEnvironment = true, - bool runInShell = false}) => - Script(executableAndArgs, - args: args, - name: name, - workingDirectory: workingDirectory, - environment: environment, - includeParentEnvironment: includeParentEnvironment, - runInShell: runInShell) - .done; +Future run( + String executableAndArgs, { + Iterable? args, + String? name, + String? workingDirectory, + Map? environment, + bool includeParentEnvironment = true, + bool runInShell = false, +}) => Script( + executableAndArgs, + args: args, + name: name, + workingDirectory: workingDirectory, + environment: environment, + includeParentEnvironment: includeParentEnvironment, + runInShell: runInShell, +).done; /// Runs an executable and returns its stdout, with trailing newlines removed. /// @@ -126,21 +138,23 @@ Future run(String executableAndArgs, /// [the README]: https://github.com/google/dart_cli_script/blob/main/README.md#argument-parsing /// /// See also [Script.output]. -Future output(String executableAndArgs, - {Iterable? args, - String? name, - String? workingDirectory, - Map? environment, - bool includeParentEnvironment = true, - bool runInShell = false}) => - Script(executableAndArgs, - args: args, - name: name, - workingDirectory: workingDirectory, - environment: environment, - includeParentEnvironment: includeParentEnvironment, - runInShell: runInShell) - .output; +Future output( + String executableAndArgs, { + Iterable? args, + String? name, + String? workingDirectory, + Map? environment, + bool includeParentEnvironment = true, + bool runInShell = false, +}) => Script( + executableAndArgs, + args: args, + name: name, + workingDirectory: workingDirectory, + environment: environment, + includeParentEnvironment: includeParentEnvironment, + runInShell: runInShell, +).output; /// Runs an executable and returns a stream of lines it prints to stdout. /// @@ -155,21 +169,23 @@ Future output(String executableAndArgs, /// [the README]: https://github.com/google/dart_cli_script/blob/main/README.md#argument-parsing /// /// See also [Script.lines]. -Stream lines(String executableAndArgs, - {Iterable? args, - String? name, - String? workingDirectory, - Map? environment, - bool includeParentEnvironment = true, - bool runInShell = false}) => - Script(executableAndArgs, - args: args, - name: name, - workingDirectory: workingDirectory, - environment: environment, - includeParentEnvironment: includeParentEnvironment, - runInShell: runInShell) - .lines; +Stream lines( + String executableAndArgs, { + Iterable? args, + String? name, + String? workingDirectory, + Map? environment, + bool includeParentEnvironment = true, + bool runInShell = false, +}) => Script( + executableAndArgs, + args: args, + name: name, + workingDirectory: workingDirectory, + environment: environment, + includeParentEnvironment: includeParentEnvironment, + runInShell: runInShell, +).lines; /// Runs an executable and returns whether it returns exit code 0. /// @@ -179,21 +195,23 @@ Stream lines(String executableAndArgs, /// All other arguments are forwarded to [Process.start]. /// /// [the README]: https://github.com/google/dart_cli_script/blob/main/README.md#argument-parsing -Future check(String executableAndArgs, - {Iterable? args, - String? name, - String? workingDirectory, - Map? environment, - bool includeParentEnvironment = true, - bool runInShell = false}) => - Script(executableAndArgs, - args: args, - name: name, - workingDirectory: workingDirectory, - environment: environment, - includeParentEnvironment: includeParentEnvironment, - runInShell: runInShell) - .success; +Future check( + String executableAndArgs, { + Iterable? args, + String? name, + String? workingDirectory, + Map? environment, + bool includeParentEnvironment = true, + bool runInShell = false, +}) => Script( + executableAndArgs, + args: args, + name: name, + workingDirectory: workingDirectory, + environment: environment, + includeParentEnvironment: includeParentEnvironment, + runInShell: runInShell, +).success; /// Prints [message] to stderr and exits the current script. /// @@ -226,24 +244,29 @@ Never fail(String message, {int exitCode = 1}) { /// /// The [caseSensitive], [unicode], and [dotAll] flags are the same as for /// [new RegExp]. -StreamTransformer grep(String regexp, - {bool exclude = false, - bool onlyMatching = false, - bool caseSensitive = true, - bool unicode = false, - bool dotAll = false}) { +StreamTransformer grep( + String regexp, { + bool exclude = false, + bool onlyMatching = false, + bool caseSensitive = true, + bool unicode = false, + bool dotAll = false, +}) { if (exclude && onlyMatching) { throw ArgumentError("The exclude and onlyMatching flags can't both be set"); } return NamedStreamTransformer.fromBind( - "grep", - (stream) => stream.grep(regexp, - exclude: exclude, - onlyMatching: onlyMatching, - caseSensitive: caseSensitive, - unicode: unicode, - dotAll: dotAll)); + "grep", + (stream) => stream.grep( + regexp, + exclude: exclude, + onlyMatching: onlyMatching, + caseSensitive: caseSensitive, + unicode: unicode, + dotAll: dotAll, + ), + ); } /// Returns a transformer that replaces matches of [regexp] with [replacement]. @@ -257,18 +280,24 @@ StreamTransformer grep(String regexp, /// /// The [caseSensitive], [unicode], and [dotAll] flags are the same as for /// [new RegExp]. -StreamTransformer replace(String regexp, String replacement, - {bool all = false, - bool caseSensitive = true, - bool unicode = false, - bool dotAll = false}) => - NamedStreamTransformer.fromBind( - "replace", - (stream) => stream.replace(regexp, replacement, - all: all, - caseSensitive: caseSensitive, - unicode: unicode, - dotAll: dotAll)); +StreamTransformer replace( + String regexp, + String replacement, { + bool all = false, + bool caseSensitive = true, + bool unicode = false, + bool dotAll = false, +}) => NamedStreamTransformer.fromBind( + "replace", + (stream) => stream.replace( + regexp, + replacement, + all: all, + caseSensitive: caseSensitive, + unicode: unicode, + dotAll: dotAll, + ), +); /// Returns a transformer that replaces matches of [regexp] with the result of /// calling [replace]. @@ -279,25 +308,32 @@ StreamTransformer replace(String regexp, String replacement, /// The [caseSensitive], [unicode], and [dotAll] flags are the same as for /// [new RegExp]. StreamTransformer replaceMapped( - String regexp, String Function(Match match) replace, - {bool all = false, - bool caseSensitive = true, - bool unicode = false, - bool dotAll = false}) => - NamedStreamTransformer.fromBind( - "replaceMapped", - (stream) => stream.replaceMapped(regexp, replace, - all: all, - caseSensitive: caseSensitive, - unicode: unicode, - dotAll: dotAll)); + String regexp, + String Function(Match match) replace, { + bool all = false, + bool caseSensitive = true, + bool unicode = false, + bool dotAll = false, +}) => NamedStreamTransformer.fromBind( + "replaceMapped", + (stream) => stream.replaceMapped( + regexp, + replace, + all: all, + caseSensitive: caseSensitive, + unicode: unicode, + dotAll: dotAll, + ), +); /// A transformer that emits each string exactly as it was received, but also /// prints each string to [currentStderr]. /// /// This is primarily intended for debugging. final teeToStderr = NamedStreamTransformer.fromBind( - "teeToStderr", (stream) => stream.teeToStderr); + "teeToStderr", + (stream) => stream.teeToStderr, +); /// A shorthand for opening the file at [path] as a stream. /// @@ -408,18 +444,24 @@ IOSink append(String path) => File(path).openWrite(mode: FileMode.append); /// /// See also [LineStreamExtensions.xargs], which takes arguments directly from /// an existing string stream rather than [stdin]. -Script xargs(FutureOr Function(List args) callback, - {int? maxArgs, - String? name, - void Function(ProcessSignal signal)? onSignal}) { +Script xargs( + FutureOr Function(List args) callback, { + int? maxArgs, + String? name, + void Function(ProcessSignal signal)? onSignal, +}) { if (maxArgs != null && maxArgs < 1) { throw RangeError.range(maxArgs, 1, null, 'maxArgs'); } late Script script; return Script.capture((stdin) async { - script = stdin.lines - .xargs(callback, maxArgs: maxArgs, name: name, onSignal: onSignal); + script = stdin.lines.xargs( + callback, + maxArgs: maxArgs, + name: name, + onSignal: onSignal, + ); await script.done; }, onSignal: (signal) => script.kill(signal)); } @@ -431,6 +473,10 @@ Script xargs(FutureOr Function(List args) callback, /// If [root] is passed, it's used as the root directory for relative globs. Stream ls(String glob, {String? root}) { var absolute = p.isAbsolute(glob); - return Glob(glob).list(root: root).map( - (entity) => absolute ? entity.path : p.relative(entity.path, from: root)); + return Glob(glob) + .list(root: root) + .map( + (entity) => + absolute ? entity.path : p.relative(entity.path, from: root), + ); } diff --git a/lib/src/buffered_script.dart b/lib/src/buffered_script.dart index e60100c..4dadf4d 100644 --- a/lib/src/buffered_script.dart +++ b/lib/src/buffered_script.dart @@ -98,12 +98,16 @@ class BufferedScript extends Script { /// appropriately. When no [onSignal] handler was set, calling [kill] will do /// nothing and return `false`. factory BufferedScript.capture( - FutureOr Function(Stream> stdin) callback, - {String? name, - bool Function(ProcessSignal signal)? onSignal, - bool stderrOnly = false}) { - var inner = Script.capture(callback, - name: name ?? "BufferedScript.capture", onSignal: onSignal); + FutureOr Function(Stream> stdin) callback, { + String? name, + bool Function(ProcessSignal signal)? onSignal, + bool stderrOnly = false, + }) { + var inner = Script.capture( + callback, + name: name ?? "BufferedScript.capture", + onSignal: onSignal, + ); if (stderrOnly) { return BufferedScript._(inner, null, StreamController>()); @@ -117,17 +121,20 @@ class BufferedScript extends Script { /// [_stdoutBuffer] and [_stderrBuffer] from a single call to /// [createEntangledControllers]. BufferedScript._(Script script, this._stdoutBuffer, this._stderrBuffer) - : _stdoutCompleter = - _stdoutBuffer == null ? null : StreamCompleter>(), - super.fromComponentsInternal( - script.name, - () => ScriptComponents( - script.stdin, - _stdoutBuffer == null ? script.stdout : Stream.empty(), - Stream.empty(), - script.exitCode), - script.kill, - silenceStartMessage: true) { + : _stdoutCompleter = _stdoutBuffer == null + ? null + : StreamCompleter>(), + super.fromComponentsInternal( + script.name, + () => ScriptComponents( + script.stdin, + _stdoutBuffer == null ? script.stdout : Stream.empty(), + Stream.empty(), + script.exitCode, + ), + script.kill, + silenceStartMessage: true, + ) { var stdoutBuffer = _stdoutBuffer; if (stdoutBuffer != null) script.stdout.pipe(stdoutBuffer); script.stderr.pipe(_stderrBuffer); @@ -145,15 +152,17 @@ class BufferedScript extends Script { /// However, unlike [done], the returned future will *not* emit an error even /// if the script fails. Future release() => _releaseMemo.runOnce(() async { - _stdoutCompleter?.setSourceStream(_stdoutBuffer!.stream); - _stderrCompleter.setSourceStream(_stderrBuffer.stream); + _stdoutCompleter?.setSourceStream(_stdoutBuffer!.stream); + _stderrCompleter.setSourceStream(_stderrBuffer.stream); - var stdoutBuffer = _stdoutBuffer; - await Future.wait( - [if (stdoutBuffer != null) stdoutBuffer.done, _stderrBuffer.done]); - - // Give outer stdio listeners a chance to handle the IO. - await Future.delayed(Duration.zero); - }); + var stdoutBuffer = _stdoutBuffer; + await Future.wait([ + if (stdoutBuffer != null) stdoutBuffer.done, + _stderrBuffer.done, + ]); + + // Give outer stdio listeners a chance to handle the IO. + await Future.delayed(Duration.zero); + }); final _releaseMemo = AsyncMemoizer(); } diff --git a/lib/src/cli_arguments.dart b/lib/src/cli_arguments.dart index fc15abe..ed23070 100644 --- a/lib/src/cli_arguments.dart +++ b/lib/src/cli_arguments.dart @@ -74,7 +74,9 @@ class CliArguments { if (next == $space || next == null) { var glob = isGlobActive ? globBuffer?.toString() : null; return _Argument( - plainBuffer.toString(), glob == null ? null : Glob(glob)); + plainBuffer.toString(), + glob == null ? null : Glob(glob), + ); } else if (next == $double_quote || next == $single_quote) { scanner.readChar(); @@ -94,7 +96,8 @@ class CliArguments { var char = scanner.readChar(); plainBuffer.writeCharCode(char); globBuffer?.writeCharCode(char); - isGlobActive = glob && + isGlobActive = + glob && (isGlobActive || char == $asterisk || char == $question || @@ -131,8 +134,9 @@ class CliArguments { /// If the arguments include [Glob]s, they will be resolved to concrete file /// paths (relative to [root], which defaults to the current directory) before /// being returned. - Future> arguments({String? root}) async => - [for (var argument in _arguments) ...await argument.resolve(root: root)]; + Future> arguments({String? root}) async => [ + for (var argument in _arguments) ...await argument.resolve(root: root), + ]; } /// An argument parsed from a `executableAndArgs` string. @@ -159,7 +163,7 @@ class _Argument { var absolute = p.isAbsolute(glob.pattern); var globbed = [ await for (var entity in glob.list(root: root)) - absolute ? entity.path : p.relative(entity.path, from: root) + absolute ? entity.path : p.relative(entity.path, from: root), ]; if (globbed.isNotEmpty) return globbed; } diff --git a/lib/src/config.dart b/lib/src/config.dart index a1ec8b5..f798f90 100644 --- a/lib/src/config.dart +++ b/lib/src/config.dart @@ -24,7 +24,7 @@ const _packagesToFold = { 'collection', 'glob', 'path', - 'string_scanner' + 'string_scanner', }; /// Returns whether debug mode is currently active. @@ -40,14 +40,20 @@ void debug(String message) { /// mode. Chain terseChain(Chain chain) => Zone.current[#_verboseTrace] == true ? chain - : chain.foldFrames((frame) => _packagesToFold.contains(frame.package), - terse: true); + : chain.foldFrames( + (frame) => _packagesToFold.contains(frame.package), + terse: true, + ); /// Runs [callback] with the given configuration values set. /// /// If [verboseTrace] is `true`, full stack traces will be printed for /// exceptions. If [debug] is `true`, extra information will be printed. -T withConfig(T Function() callback, - {bool verboseTrace = false, bool debug = false}) => - runZoned(callback, - zoneValues: {#_debug: debug, #_verboseTrace: verboseTrace}); +T withConfig( + T Function() callback, { + bool verboseTrace = false, + bool debug = false, +}) => runZoned( + callback, + zoneValues: {#_debug: debug, #_verboseTrace: verboseTrace}, +); diff --git a/lib/src/environment.dart b/lib/src/environment.dart index 751eed0..a7083a3 100644 --- a/lib/src/environment.dart +++ b/lib/src/environment.dart @@ -41,8 +41,11 @@ final _defaultEnvironment = _newMap()..addAll(Platform.environment); /// remove the corresponding keys from the parent [env]. If /// [includeParentEnvironment] is `false`, [environment] is used as the *entire* /// child environment instead. -T withEnv(T Function() callback, Map environment, - {bool includeParentEnvironment = true}) { +T withEnv( + T Function() callback, + Map environment, { + bool includeParentEnvironment = true, +}) { var newEnvironment = _newMap(); if (includeParentEnvironment) newEnvironment.addAll(env); diff --git a/lib/src/extensions/byte_stream.dart b/lib/src/extensions/byte_stream.dart index 239ead9..25b39cf 100644 --- a/lib/src/extensions/byte_stream.dart +++ b/lib/src/extensions/byte_stream.dart @@ -67,18 +67,24 @@ extension ByteStreamExtensions on Stream> { /// closed. Script get _asScript { var signalCloser = StreamCloser>(); - return Script.fromComponents("stream", () { - var exitCodeCompleter = Completer.sync(); - return ScriptComponents( + return Script.fromComponents( + "stream", + () { + var exitCodeCompleter = Completer.sync(); + return ScriptComponents( NullStreamSink(), - transform(signalCloser).onDone(() => - exitCodeCompleter.complete(signalCloser.isClosed ? 143 : 0)), + transform(signalCloser).onDone( + () => exitCodeCompleter.complete(signalCloser.isClosed ? 143 : 0), + ), Stream.empty(), - exitCodeCompleter.future); - }, onSignal: (_) { - signalCloser.close(); - return true; - }); + exitCodeCompleter.future, + ); + }, + onSignal: (_) { + signalCloser.close(); + return true; + }, + ); } /// Shorthand for [Stream.pipe]. diff --git a/lib/src/extensions/line_and_span_stream.dart b/lib/src/extensions/line_and_span_stream.dart index 40e89c8..0ac63ed 100644 --- a/lib/src/extensions/line_and_span_stream.dart +++ b/lib/src/extensions/line_and_span_stream.dart @@ -39,26 +39,39 @@ extension LineAndSpanStreamExtensions /// /// The [caseSensitive], [unicode], and [dotAll] flags are the same as for /// [new RegExp]. - Stream> grep(String regexp, - {bool exclude = false, - bool onlyMatching = false, - bool caseSensitive = true, - bool unicode = false, - bool dotAll = false}) { + Stream> grep( + String regexp, { + bool exclude = false, + bool onlyMatching = false, + bool caseSensitive = true, + bool unicode = false, + bool dotAll = false, + }) { if (exclude && onlyMatching) { throw ArgumentError( - "The exclude and onlyMatching flags can't both be set"); + "The exclude and onlyMatching flags can't both be set", + ); } - var pattern = RegExp(regexp, - caseSensitive: caseSensitive, unicode: unicode, dotAll: dotAll); + var pattern = RegExp( + regexp, + caseSensitive: caseSensitive, + unicode: unicode, + dotAll: dotAll, + ); return onlyMatching - ? expand((tuple) => pattern - .allMatches(tuple.item1) - .map((match) => Tuple2( - match.group(0)!, tuple.item2.subspan(match.start, match.end))) - .where((tuple) => tuple.item1.isNotEmpty)) + ? expand( + (tuple) => pattern + .allMatches(tuple.item1) + .map( + (match) => Tuple2( + match.group(0)!, + tuple.item2.subspan(match.start, match.end), + ), + ) + .where((tuple) => tuple.item1.isNotEmpty), + ) : where((tuple) => pattern.hasMatch(tuple.item1) != exclude); } @@ -74,16 +87,20 @@ extension LineAndSpanStreamExtensions /// The [caseSensitive], [unicode], and [dotAll] flags are the same as for /// [new RegExp]. Stream> replace( - String regexp, String replacement, - {bool all = false, - bool caseSensitive = true, - bool unicode = false, - bool dotAll = false}) => - replaceMapped(regexp, (match) => replaceMatch(match, replacement), - all: all, - caseSensitive: caseSensitive, - unicode: unicode, - dotAll: dotAll); + String regexp, + String replacement, { + bool all = false, + bool caseSensitive = true, + bool unicode = false, + bool dotAll = false, + }) => replaceMapped( + regexp, + (match) => replaceMatch(match, replacement), + all: all, + caseSensitive: caseSensitive, + unicode: unicode, + dotAll: dotAll, + ); /// Replaces matches of [regexp] with the result of calling [replace]. /// @@ -93,16 +110,24 @@ extension LineAndSpanStreamExtensions /// The [caseSensitive], [unicode], and [dotAll] flags are the same as for /// [new RegExp]. Stream> replaceMapped( - String regexp, String Function(Match match) replace, - {bool all = false, - bool caseSensitive = true, - bool unicode = false, - bool dotAll = false}) { - var pattern = RegExp(regexp, - caseSensitive: caseSensitive, unicode: unicode, dotAll: dotAll); - return mapLines((line) => all - ? line.replaceAllMapped(pattern, replace) - : line.replaceFirstMapped(pattern, replace)); + String regexp, + String Function(Match match) replace, { + bool all = false, + bool caseSensitive = true, + bool unicode = false, + bool dotAll = false, + }) { + var pattern = RegExp( + regexp, + caseSensitive: caseSensitive, + unicode: unicode, + dotAll: dotAll, + ); + return mapLines( + (line) => all + ? line.replaceAllMapped(pattern, replace) + : line.replaceFirstMapped(pattern, replace), + ); } /// Returns a stream that emits the same events as this one, but also prints @@ -118,6 +143,6 @@ extension LineAndSpanStreamExtensions /// Like [Stream.map], but only applies the callback to the lines and not the /// spans. Stream> mapLines( - String Function(String line) callback) => - map((tuple) => Tuple2(callback(tuple.item1), tuple.item2)); + String Function(String line) callback, + ) => map((tuple) => Tuple2(callback(tuple.item1), tuple.item2)); } diff --git a/lib/src/extensions/line_stream.dart b/lib/src/extensions/line_stream.dart index 5c7bcc9..458dfc8 100644 --- a/lib/src/extensions/line_stream.dart +++ b/lib/src/extensions/line_stream.dart @@ -53,8 +53,10 @@ extension LineStreamExtensions on Stream { /// not both be passed at once. /// /// See [LineAndSpanStreamExtensions]. - Stream> withSpans( - {Uri? sourceUrl, String? sourcePath}) { + Stream> withSpans({ + Uri? sourceUrl, + String? sourcePath, + }) { if (sourcePath != null) { if (sourceUrl != null) { throw ArgumentError("Only one of url and path may be passed."); @@ -66,12 +68,21 @@ extension LineStreamExtensions on Stream { var offset = 0; return map((line) { var span = SourceSpanWithContext( - SourceLocation(offset, - sourceUrl: sourceUrl, line: lineNumber, column: 0), - SourceLocation(offset + line.length, - sourceUrl: sourceUrl, line: lineNumber, column: line.length), - line, - line); + SourceLocation( + offset, + sourceUrl: sourceUrl, + line: lineNumber, + column: 0, + ), + SourceLocation( + offset + line.length, + sourceUrl: sourceUrl, + line: lineNumber, + column: line.length, + ), + line, + line, + ); lineNumber++; offset += line.length + 1; return Tuple2(line, span); @@ -89,25 +100,34 @@ extension LineStreamExtensions on Stream { /// /// The [caseSensitive], [unicode], and [dotAll] flags are the same as for /// [new RegExp]. - Stream grep(String regexp, - {bool exclude = false, - bool onlyMatching = false, - bool caseSensitive = true, - bool unicode = false, - bool dotAll = false}) { + Stream grep( + String regexp, { + bool exclude = false, + bool onlyMatching = false, + bool caseSensitive = true, + bool unicode = false, + bool dotAll = false, + }) { if (exclude && onlyMatching) { throw ArgumentError( - "The exclude and onlyMatching flags can't both be set"); + "The exclude and onlyMatching flags can't both be set", + ); } - var pattern = RegExp(regexp, - caseSensitive: caseSensitive, unicode: unicode, dotAll: dotAll); + var pattern = RegExp( + regexp, + caseSensitive: caseSensitive, + unicode: unicode, + dotAll: dotAll, + ); return onlyMatching - ? expand((line) => pattern - .allMatches(line) - .map((match) => match.group(0)!) - .where((match) => match.isNotEmpty)) + ? expand( + (line) => pattern + .allMatches(line) + .map((match) => match.group(0)!) + .where((match) => match.isNotEmpty), + ) : where((line) => pattern.hasMatch(line) != exclude); } @@ -122,16 +142,21 @@ extension LineStreamExtensions on Stream { /// /// The [caseSensitive], [unicode], and [dotAll] flags are the same as for /// [new RegExp]. - Stream replace(String regexp, String replacement, - {bool all = false, - bool caseSensitive = true, - bool unicode = false, - bool dotAll = false}) => - replaceMapped(regexp, (match) => replaceMatch(match, replacement), - all: all, - caseSensitive: caseSensitive, - unicode: unicode, - dotAll: dotAll); + Stream replace( + String regexp, + String replacement, { + bool all = false, + bool caseSensitive = true, + bool unicode = false, + bool dotAll = false, + }) => replaceMapped( + regexp, + (match) => replaceMatch(match, replacement), + all: all, + caseSensitive: caseSensitive, + unicode: unicode, + dotAll: dotAll, + ); /// Replaces matches of [regexp] with the result of calling [replace]. /// @@ -141,16 +166,24 @@ extension LineStreamExtensions on Stream { /// The [caseSensitive], [unicode], and [dotAll] flags are the same as for /// [new RegExp]. Stream replaceMapped( - String regexp, String Function(Match match) replace, - {bool all = false, - bool caseSensitive = true, - bool unicode = false, - bool dotAll = false}) { - var pattern = RegExp(regexp, - caseSensitive: caseSensitive, unicode: unicode, dotAll: dotAll); - return map((line) => all - ? line.replaceAllMapped(pattern, replace) - : line.replaceFirstMapped(pattern, replace)); + String regexp, + String Function(Match match) replace, { + bool all = false, + bool caseSensitive = true, + bool unicode = false, + bool dotAll = false, + }) { + var pattern = RegExp( + regexp, + caseSensitive: caseSensitive, + unicode: unicode, + dotAll: dotAll, + ); + return map( + (line) => all + ? line.replaceAllMapped(pattern, replace) + : line.replaceFirstMapped(pattern, replace), + ); } /// Returns a stream that emits the same events as this one, but also prints @@ -158,9 +191,9 @@ extension LineStreamExtensions on Stream { /// /// This is primarily intended for debugging. Stream get teeToStderr => map((line) { - currentStderr.writeln(line); - return line; - }); + currentStderr.writeln(line); + return line; + }); /// Passes the strings emitted by this stream as arguments to [callback]. /// @@ -193,34 +226,38 @@ extension LineStreamExtensions on Stream { /// /// See also `xargs` in `package:cli_script/cli_script.dart`, which takes /// arguments from [stdin] rather than from this string stream. - Script xargs(FutureOr Function(List args) callback, - {int? maxArgs, - String? name, - void Function(ProcessSignal signal)? onSignal}) { + Script xargs( + FutureOr Function(List args) callback, { + int? maxArgs, + String? name, + void Function(ProcessSignal signal)? onSignal, + }) { if (maxArgs != null && maxArgs < 1) { throw RangeError.range(maxArgs, 1, null, 'maxArgs'); } var signalCloser = StreamCloser(); var self = transform(signalCloser); - var chunks = - maxArgs != null ? self.slices(maxArgs) : self.toList().asStream(); + var chunks = maxArgs != null + ? self.slices(maxArgs) + : self.toList().asStream(); return Script.capture( - (_) async { - await for (var chunk in chunks) { - if (signalCloser.isClosed) break; - await callback(chunk); - } - if (signalCloser.isClosed) { - throw ScriptException(name ?? 'xargs', 143); - } - }, - name: name, - onSignal: (signal) { - signalCloser.close(); - if (onSignal != null) onSignal(signal); - return true; - }); + (_) async { + await for (var chunk in chunks) { + if (signalCloser.isClosed) break; + await callback(chunk); + } + if (signalCloser.isClosed) { + throw ScriptException(name ?? 'xargs', 143); + } + }, + name: name, + onSignal: (signal) { + signalCloser.close(); + if (onSignal != null) onSignal(signal); + return true; + }, + ); } } diff --git a/lib/src/script.dart b/lib/src/script.dart index d09cc41..0b12307 100644 --- a/lib/src/script.dart +++ b/lib/src/script.dart @@ -117,10 +117,13 @@ class Script { /// /// See also [success], which should be preferred when simply checking whether /// the script has succeeded. - Future get exitCode => done.then((_) => 0, onError: (Object error, _) { - if (error is! ScriptException) throw error; - return error.exitCode; - }); + Future get exitCode => done.then( + (_) => 0, + onError: (Object error, _) { + if (error is! ScriptException) throw error; + return error.exitCode; + }, + ); /// Whether the script succeeded or failed (that is, whether it emitted an /// exit code of `0`). @@ -187,57 +190,73 @@ class Script { /// executable name. All other arguments are forwarded to [Process.start]. /// /// [the README]: https://github.com/google/dart_cli_script/blob/main/README.md#argument-parsing - factory Script(String executableAndArgs, - {Iterable? args, - String? name, - String? workingDirectory, - Map? environment, - bool includeParentEnvironment = true, - bool runInShell = false}) { + factory Script( + String executableAndArgs, { + Iterable? args, + String? name, + String? workingDirectory, + Map? environment, + bool includeParentEnvironment = true, + bool runInShell = false, + }) { var parsedExecutableAndArgs = CliArguments.parse(executableAndArgs); name ??= p.basename(parsedExecutableAndArgs.executable); ProcessSignal? capturedSignal; Process? process; - return Script.fromComponentsInternal(name, () async { - if (includeParentEnvironment) { - environment = environment == null - ? env - // Use [withEnv] to ensure that the copied environment correctly - // overrides the parent [env], including handling case-insensitive - // keys on Windows. - : withEnv(() => env, environment!); - } - - var allArgs = [ - ...await parsedExecutableAndArgs.arguments(root: workingDirectory), - ...?args - ]; - - if (inDebugMode) { - // dart-lang/language#1536 - debug("[${name!}] ${parsedExecutableAndArgs.executable} " - "${allArgs.join(' ')}"); - } - - process = await Process.start(parsedExecutableAndArgs.executable, allArgs, + return Script.fromComponentsInternal( + name, + () async { + if (includeParentEnvironment) { + environment = environment == null + ? env + // Use [withEnv] to ensure that the copied environment correctly + // overrides the parent [env], including handling case-insensitive + // keys on Windows. + : withEnv(() => env, environment!); + } + + var allArgs = [ + ...await parsedExecutableAndArgs.arguments(root: workingDirectory), + ...?args, + ]; + + if (inDebugMode) { + // dart-lang/language#1536 + debug( + "[${name!}] ${parsedExecutableAndArgs.executable} " + "${allArgs.join(' ')}", + ); + } + + process = await Process.start( + parsedExecutableAndArgs.executable, + allArgs, workingDirectory: workingDirectory, environment: environment, includeParentEnvironment: false, - runInShell: runInShell); - - // Passes the [capturedSignal] received by the [Script.kill] function to - // the [Process.kill] function if the signal was received before the - // process started. - if (capturedSignal != null) process!.kill(capturedSignal!); - - return ScriptComponents( - process!.stdin, process!.stdout, process!.stderr, process!.exitCode); - }, (signal) { - if (process != null) return process!.kill(signal); - capturedSignal = signal; - return true; - }, silenceStartMessage: true); + runInShell: runInShell, + ); + + // Passes the [capturedSignal] received by the [Script.kill] function to + // the [Process.kill] function if the signal was received before the + // process started. + if (capturedSignal != null) process!.kill(capturedSignal!); + + return ScriptComponents( + process!.stdin, + process!.stdout, + process!.stderr, + process!.exitCode, + ); + }, + (signal) { + if (process != null) return process!.kill(signal); + capturedSignal = signal; + return true; + }, + silenceStartMessage: true, + ); } /// Runs [callback] and captures the output of [Script]s created within it. @@ -280,9 +299,10 @@ class Script { /// appropriately. When no [onSignal] handler was set, calling [kill] will do /// nothing and return `false`. factory Script.capture( - FutureOr Function(Stream> stdin) callback, - {String? name, - bool Function(ProcessSignal signal)? onSignal}) { + FutureOr Function(Stream> stdin) callback, { + String? name, + bool Function(ProcessSignal signal)? onSignal, + }) { _checkCapture(); var scriptName = name ?? "capture"; @@ -294,50 +314,59 @@ class Script { var exitCodeCompleter = Completer(); runZonedGuarded( - () async { - if (onSignal != null) { - onSignal = Zone.current.bindUnaryCallback(onSignal!); - } + () async { + if (onSignal != null) { + onSignal = Zone.current.bindUnaryCallback(onSignal!); + } - await callback(stdinController.stream); - - // Once there are no child scripts still spawning or running, mark - // this script as done. - void checkIdle() { - if (childScripts.isIdle && !exitCodeCompleter.isCompleted) { - stdoutGroup.close(); - stderrGroup.close(); - childScripts.close(); - exitCodeCompleter.complete(0); - } - } + await callback(stdinController.stream); - checkIdle(); - childScripts.onIdle.listen((_) => Timer.run(checkIdle)); - }, - (error, stackTrace) { - if (!exitCodeCompleter.isCompleted) { + // Once there are no child scripts still spawning or running, mark + // this script as done. + void checkIdle() { + if (childScripts.isIdle && !exitCodeCompleter.isCompleted) { stdoutGroup.close(); stderrGroup.close(); childScripts.close(); - exitCodeCompleter.completeError(error, stackTrace); + exitCodeCompleter.complete(0); } - }, - zoneValues: { - #_childScripts: childScripts, - scriptNameKey: scriptName, - stdoutKey: stdoutGroup, - stderrKey: stderrGroup - }, - zoneSpecification: ZoneSpecification(print: (_, parent, zone, line) { + } + + checkIdle(); + childScripts.onIdle.listen((_) => Timer.run(checkIdle)); + }, + (error, stackTrace) { + if (!exitCodeCompleter.isCompleted) { + stdoutGroup.close(); + stderrGroup.close(); + childScripts.close(); + exitCodeCompleter.completeError(error, stackTrace); + } + }, + zoneValues: { + #_childScripts: childScripts, + scriptNameKey: scriptName, + stdoutKey: stdoutGroup, + stderrKey: stderrGroup, + }, + zoneSpecification: ZoneSpecification( + print: (_, parent, zone, line) { if (!exitCodeCompleter.isCompleted) stdoutGroup.writeln(line); - })); + }, + ), + ); - return Script._(scriptName, stdinController.sink, stdoutGroup.stream, - stderrGroup.stream, exitCodeCompleter.future, (signal) { - if (onSignal == null) return false; - return onSignal!(signal); - }); + return Script._( + scriptName, + stdinController.sink, + stdoutGroup.stream, + stderrGroup.stream, + exitCodeCompleter.future, + (signal) { + if (onSignal == null) return false; + return onSignal!(signal); + }, + ); } /// Creates a [Script] from a [StreamTransformer] on byte streams. @@ -350,8 +379,9 @@ class Script { /// exits with [Script.exitCode] `143`, or `false` if the stream was already /// closed. factory Script.fromByteTransformer( - StreamTransformer, List> transformer, - {String? name}) { + StreamTransformer, List> transformer, { + String? name, + }) { _checkCapture(); var controller = StreamController>(); var exitCodeCompleter = Completer.sync(); @@ -360,8 +390,12 @@ class Script { return Script._( name ?? transformer.toString(), controller.sink, - controller.stream.transform(signalCloser).transform(transformer).onDone( - () => exitCodeCompleter.complete(signalCloser.isClosed ? 143 : 0)), + controller.stream + .transform(signalCloser) + .transform(transformer) + .onDone( + () => exitCodeCompleter.complete(signalCloser.isClosed ? 143 : 0), + ), Stream.empty(), exitCodeCompleter.future, (_) { @@ -381,23 +415,28 @@ class Script { /// exits with [Script.exitCode] `143`, or `false` if the stream was already /// closed. factory Script.fromLineTransformer( - StreamTransformer transformer, - {String? name}) => - Script.fromByteTransformer( - StreamTransformer.fromBind((stream) => stream.lines - .transform(transformer) - .map>((line) => utf8.encode("$line\n"))), - name: name ?? transformer.toString()); + StreamTransformer transformer, { + String? name, + }) => Script.fromByteTransformer( + StreamTransformer.fromBind( + (stream) => stream.lines + .transform(transformer) + .map>((line) => utf8.encode("$line\n")), + ), + name: name ?? transformer.toString(), + ); /// Creates a [Script] from a function that maps strings to strings. /// /// This script passes each line of stdin to [mapper] and emits the result via /// stdout. - factory Script.mapLines(String Function(String line) mapper, - {String? name}) => - Script.fromLineTransformer( - StreamTransformer.fromBind((stream) => stream.map(mapper)), - name: name ?? mapper.toString()); + factory Script.mapLines( + String Function(String line) mapper, { + String? name, + }) => Script.fromLineTransformer( + StreamTransformer.fromBind((stream) => stream.map(mapper)), + name: name ?? mapper.toString(), + ); /// Pipes each script's [stdout] into the next script's [stdin]. /// @@ -436,16 +475,19 @@ class Script { } return Script._( - name ?? list.map((script) => script.name).join(" | "), - list.first.stdin, - // Wrap the final script's stdout and stderr in [SubscriptionStream]s so - // that the inner scripts will see that someone's listening and not try - // to top-level the streams' output. - SubscriptionStream(list.last.stdout.listen(null)), - SubscriptionStream(list.last.stderr.listen(null)), - Future.wait(list.map((script) => script.exitCode)).then((exitCodes) => - exitCodes.lastWhere((code) => code != 0, orElse: () => 0)), - (signal) => list.any((script) => script.kill(signal))); + name ?? list.map((script) => script.name).join(" | "), + list.first.stdin, + // Wrap the final script's stdout and stderr in [SubscriptionStream]s so + // that the inner scripts will see that someone's listening and not try + // to top-level the streams' output. + SubscriptionStream(list.last.stdout.listen(null)), + SubscriptionStream(list.last.stderr.listen(null)), + Future.wait(list.map((script) => script.exitCode)).then( + (exitCodes) => + exitCodes.lastWhere((code) => code != 0, orElse: () => 0), + ), + (signal) => list.any((script) => script.kill(signal)), + ); } /// Converts [scriptlike] into a [Script], or throws an [ArgumentError] if it @@ -461,7 +503,8 @@ class Script { return Script.mapLines(scriptlike); } else { throw ArgumentError( - "$scriptlike is not a Script and can't be converted to one."); + "$scriptlike is not a Script and can't be converted to one.", + ); } } @@ -486,10 +529,15 @@ class Script { /// appropriately. When no [onSignal] handler was set, calling [kill] will do /// nothing and return `false`. Script.fromComponents( - String name, FutureOr Function() callback, - {bool Function(ProcessSignal signal)? onSignal}) - : this.fromComponentsInternal(name, callback, onSignal ?? (_) => false, - silenceStartMessage: false); + String name, + FutureOr Function() callback, { + bool Function(ProcessSignal signal)? onSignal, + }) : this.fromComponentsInternal( + name, + callback, + onSignal ?? (_) => false, + silenceStartMessage: false, + ); /// Like [Script.fromComponents], but with an internal [silenceStartMessage] /// option that's forwarded to [Script._]. @@ -497,19 +545,20 @@ class Script { /// @nodoc @internal Script.fromComponentsInternal( - String name, - FutureOr Function() callback, - bool Function(ProcessSignal signal) signalHandler, - {required bool silenceStartMessage}) - : this._fromComponentsInternal( - _checkCapture(), - name, - callback, - StreamCompleter(), - StreamCompleter(), - StreamSinkCompleter(), - signalHandler, - silenceStartMessage: silenceStartMessage); + String name, + FutureOr Function() callback, + bool Function(ProcessSignal signal) signalHandler, { + required bool silenceStartMessage, + }) : this._fromComponentsInternal( + _checkCapture(), + name, + callback, + StreamCompleter(), + StreamCompleter(), + StreamSinkCompleter(), + signalHandler, + silenceStartMessage: silenceStartMessage, + ); /// A helper method for [Script.fromComponentsInternal] that takes a bunch of /// intermediate values as parameters so it can refer to them multiple times @@ -519,34 +568,38 @@ class Script { /// factory constructor, but then it and [Script.fromComponents] couldn't be /// invoked by subclasses. Script._fromComponentsInternal( - // A void parameter is pretty nasty, but it allows us to throw an error if - // the surrounding capture is closed before scheduling [callback]. - void checkCapture, - String name, - FutureOr Function() callback, - StreamCompleter> stdoutCompleter, - StreamCompleter> stderrCompleter, - StreamSinkCompleter> stdinCompleter, - bool Function(ProcessSignal signal) signalHandler, - {required bool silenceStartMessage}) - : this._( - name, - stdinCompleter.sink.rejectErrors(), - stdoutCompleter.stream, - stderrCompleter.stream, - Future.sync(callback).then((components) { - stdinCompleter.setDestinationSink(components.stdin); - stdoutCompleter.setSourceStream(components.stdout); - stderrCompleter.setSourceStream(components.stderr); - return components.exitCode; - }), - signalHandler, - silenceStartMessage: silenceStartMessage); + // A void parameter is pretty nasty, but it allows us to throw an error if + // the surrounding capture is closed before scheduling [callback]. + void checkCapture, + String name, + FutureOr Function() callback, + StreamCompleter> stdoutCompleter, + StreamCompleter> stderrCompleter, + StreamSinkCompleter> stdinCompleter, + bool Function(ProcessSignal signal) signalHandler, { + required bool silenceStartMessage, + }) : this._( + name, + stdinCompleter.sink.rejectErrors(), + stdoutCompleter.stream, + stderrCompleter.stream, + Future.sync(callback).then((components) { + stdinCompleter.setDestinationSink(components.stdin); + stdoutCompleter.setSourceStream(components.stdout); + stderrCompleter.setSourceStream(components.stderr); + return components.exitCode; + }), + signalHandler, + silenceStartMessage: silenceStartMessage, + ); /// Pipes [stream]'s events to a [StdioGroup] indexed by [key] in the current /// zone if it exists, or else to [defaultConsumer] if it doesn't. static void _pipeUnlistenedStream( - Stream> stream, Object key, Sink> defaultConsumer) { + Stream> stream, + Object key, + Sink> defaultConsumer, + ) { var group = Zone.current[key]; if (group is StdioGroup) { group.add(stream); @@ -559,11 +612,20 @@ class Script { /// /// If [silenceStartMessage] is `false` (the default), this prints a message /// in debug mode indicating that the script has started running. - Script._(this.name, StreamSink> stdin, Stream> stdout, - Stream> stderr, Future exitCode, this._signalHandler, - {bool silenceStartMessage = false}) { - this.stdin = IOSink(stdin - .transform(StreamSinkTransformer.fromStreamTransformer(_stdinCloser))); + Script._( + this.name, + StreamSink> stdin, + Stream> stdout, + Stream> stderr, + Future exitCode, + this._signalHandler, { + bool silenceStartMessage = false, + }) { + this.stdin = IOSink( + stdin.transform( + StreamSinkTransformer.fromStreamTransformer(_stdinCloser), + ), + ); if (!silenceStartMessage) debug("[$name] starting"); _stdout = stdout.handleError(_handleError).transform(_outputCloser); @@ -585,7 +647,9 @@ class Script { } else { debug("[$name] exited with exit code $code"); _doneCompleter.completeError( - ScriptException(name, code), StackTrace.current); + ScriptException(name, code), + StackTrace.current, + ); } _closeOutputStreams(); @@ -641,8 +705,10 @@ class Script { if (childScripts.isClosed) { // The FutureGroup is closed, indicating that the surrounding capture // group has already exited. - throw StateError("Can't create a Script within a Script.capture() block " - "that already exited."); + throw StateError( + "Can't create a Script within a Script.capture() block " + "that already exited.", + ); } return childScripts; @@ -665,20 +731,28 @@ class Script { .split("\n") .map((line) => "| $line") .join("\n"); - debug("[$name] exited with Dart exception:\n" - "$exception"); + debug( + "[$name] exited with Dart exception:\n" + "$exception", + ); } // Otherwise, if this is an unexpected Dart error, print information about // it to stderr and exit with code 257. That code is higher than actual // subprocesses can emit, so it can be used as a sentinel to detect // Dart-based failures. - _extraStderrController.add(utf8.encode("Error in $name:\n" + _extraStderrController.add( + utf8.encode( + "Error in $name:\n" "$error\n" - "$chain\n")); + "$chain\n", + ), + ); _extraStderrController.close(); _doneCompleter.completeError( - ScriptException(name, 257), StackTrace.current); + ScriptException(name, 257), + StackTrace.current, + ); } _closeOutputStreams(); diff --git a/lib/src/stdio.dart b/lib/src/stdio.dart index 552e080..1b5a163 100644 --- a/lib/src/stdio.dart +++ b/lib/src/stdio.dart @@ -59,9 +59,11 @@ IOSink get currentStderr { T silenceStdout(T Function() callback) { var group = StdioGroup(); group.stream.drain(); - return runZoned(callback, - zoneValues: {stdoutKey: group}, - zoneSpecification: ZoneSpecification(print: (_, __, ___, ____) {})); + return runZoned( + callback, + zoneValues: {stdoutKey: group}, + zoneSpecification: ZoneSpecification(print: (_, __, ___, ____) {}), + ); } /// Runs [callback] and silences all stderr emitted by [Script]s. @@ -82,9 +84,11 @@ T silenceStderr(T Function() callback) { T silenceOutput(T Function() callback) { var group = StdioGroup(); group.stream.drain(); - return runZoned(callback, - zoneValues: {stdoutKey: group, stderrKey: group}, - zoneSpecification: ZoneSpecification(print: (_, __, ___, ____) {})); + return runZoned( + callback, + zoneValues: {stdoutKey: group, stderrKey: group}, + zoneSpecification: ZoneSpecification(print: (_, __, ___, ____) {}), + ); } /// Runs [callback] in a [Script.capture] block and silences all stdout and @@ -103,31 +107,39 @@ T silenceOutput(T Function() callback) { /// appropriately. When no [onSignal] handler was set, calling [kill] will do /// nothing and return `false`. Script silenceUntilFailure( - FutureOr Function(Stream> stdin) callback, - {String? name, - bool? when, - bool stderrOnly = false, - bool Function(ProcessSignal signal)? onSignal}) { + FutureOr Function(Stream> stdin) callback, { + String? name, + bool? when, + bool stderrOnly = false, + bool Function(ProcessSignal signal)? onSignal, +}) { // Wrap this in an additional [Script.capture] so that we can both handle the // failure *and* still have it be top-leveled if it's not handled by the // caller. - return Script.capture((stdin) async { - if (when == false) { - await callback(stdin); - return; - } + return Script.capture( + (stdin) async { + if (when == false) { + await callback(stdin); + return; + } - var script = BufferedScript.capture((_) => callback(stdin), - name: name == null ? null : '$name.inner', stderrOnly: stderrOnly); + var script = BufferedScript.capture( + (_) => callback(stdin), + name: name == null ? null : '$name.inner', + stderrOnly: stderrOnly, + ); - try { - await script.done; - } catch (_) { - script.release(); + try { + await script.done; + } catch (_) { + script.release(); - // Give the new stdio a chance to propagate. - await Future.delayed(Duration.zero); - rethrow; - } - }, name: name, onSignal: onSignal); + // Give the new stdio a chance to propagate. + await Future.delayed(Duration.zero); + rethrow; + } + }, + name: name, + onSignal: onSignal, + ); } diff --git a/lib/src/stdio_group.dart b/lib/src/stdio_group.dart index e06f31e..bc559c5 100644 --- a/lib/src/stdio_group.dart +++ b/lib/src/stdio_group.dart @@ -45,13 +45,15 @@ class StdioGroup { static Tuple2 entangled() { var controllers = createEntangledControllers>(); return Tuple2( - StdioGroup._(controllers.item1), StdioGroup._(controllers.item2)); + StdioGroup._(controllers.item1), + StdioGroup._(controllers.item2), + ); } StdioGroup() : this._(StreamController(sync: true)); StdioGroup._(this._sinkController) - : sink = _StdioGroupSink(_sinkController.sink) { + : sink = _StdioGroupSink(_sinkController.sink) { _group.add(_sinkController.stream); } diff --git a/lib/src/temp.dart b/lib/src/temp.dart index 92ef2b1..dfb3299 100644 --- a/lib/src/temp.dart +++ b/lib/src/temp.dart @@ -44,8 +44,12 @@ const _slugCharacters = 16; /// basename. If [suffix] is passed, it's added to the end. If [parent] is /// passed, it's used as the parent directory for the path; it defaults to /// [Directory.systemTemp]. -T withTempPath(T Function(String path) callback, - {String? prefix, String? suffix, String? parent}) { +T withTempPath( + T Function(String path) callback, { + String? prefix, + String? suffix, + String? parent, +}) { var path = _tempPathName(prefix, suffix, parent); return tryFinally(() => callback(path), () { try { @@ -62,8 +66,12 @@ T withTempPath(T Function(String path) callback, /// Note that even [withTempPath] can safely be used with an asynchronous /// [callback]. This function is only necessary if you need the automatic /// filesystem operations to be asynchronous. -Future withTempPathAsync(FutureOr Function(String path) callback, - {String? prefix, String? suffix, String? parent}) async { +Future withTempPathAsync( + FutureOr Function(String path) callback, { + String? prefix, + String? suffix, + String? parent, +}) async { var path = _tempPathName(prefix, suffix, parent); try { return await callback(path); @@ -89,12 +97,20 @@ Future withTempPathAsync(FutureOr Function(String path) callback, /// directory's basename. If [suffix] is passed, it's added to the end. If /// [parent] is passed, the temporary directory is created within that path; /// otherwise, it's created within [Directory.systemTemp]. -T withTempDir(T Function(String dir) callback, - {String? prefix, String? suffix, String? parent}) => - withTempPath((path) { - Directory(path).createSync(); - return callback(path); - }, prefix: prefix, suffix: suffix, parent: parent); +T withTempDir( + T Function(String dir) callback, { + String? prefix, + String? suffix, + String? parent, +}) => withTempPath( + (path) { + Directory(path).createSync(); + return callback(path); + }, + prefix: prefix, + suffix: suffix, + parent: parent, +); /// Like [withTempDir], but creates and deletes the temporary directory /// asynchronously. @@ -102,20 +118,31 @@ T withTempDir(T Function(String dir) callback, /// Note that even [withTempDir] can safely be used with an asynchronous /// [callback]. This function is only necessary if you need the automatic /// filesystem operations to be asynchronous. -Future withTempDirAsync(FutureOr Function(String dir) callback, - {String? prefix, String? suffix, String? parent}) => - withTempPathAsync((path) async { - await Directory(path).create(); - return await callback(path); - }, prefix: prefix, suffix: suffix, parent: parent); +Future withTempDirAsync( + FutureOr Function(String dir) callback, { + String? prefix, + String? suffix, + String? parent, +}) => withTempPathAsync( + (path) async { + await Directory(path).create(); + return await callback(path); + }, + prefix: prefix, + suffix: suffix, + parent: parent, +); /// Returns the name of a temporary path within [parent] with the given [prefix] /// and [suffix]. String _tempPathName(String? prefix, String? suffix, String? parent) { var slug = String.fromCharCodes( - Iterable.generate(_slugCharacters, (_) => _randomAlphanumeric())); - return p.join(parent ?? Directory.systemTemp.path, - "${prefix ?? ''}$slug${suffix ?? ''}"); + Iterable.generate(_slugCharacters, (_) => _randomAlphanumeric()), + ); + return p.join( + parent ?? Directory.systemTemp.path, + "${prefix ?? ''}$slug${suffix ?? ''}", + ); } /// Returns a random alphanumeric character. diff --git a/lib/src/util.dart b/lib/src/util.dart index 7c89254..21b1386 100644 --- a/lib/src/util.dart +++ b/lib/src/util.dart @@ -52,8 +52,11 @@ String replaceMatch(Match match, String replacement) { if (next >= $0 && next <= $9) { var groupNumber = next - $0; if (groupNumber > match.groupCount) { - scanner.error("RegExp doesn't have group $groupNumber.", - position: scanner.position - 2, length: 2); + scanner.error( + "RegExp doesn't have group $groupNumber.", + position: scanner.position - 2, + length: 2, + ); } var group = match[groupNumber]; @@ -71,11 +74,14 @@ String replaceMatch(Match match, String replacement) { extension UtilStreamExtensions on Stream { /// Returns a transformation of [this] that calls [callback] immediately /// before sending a `done` event to its listeners. - Stream onDone(void Function() callback) => - transform(StreamTransformer.fromHandlers(handleDone: (sink) { + Stream onDone(void Function() callback) => transform( + StreamTransformer.fromHandlers( + handleDone: (sink) { callback(); sink.close(); - })); + }, + ), + ); /// Returns a transformation of [this] that only emits error and done events, /// not data events. diff --git a/lib/src/util/entangled_controllers.dart b/lib/src/util/entangled_controllers.dart index 1083ffa..6f620d8 100644 --- a/lib/src/util/entangled_controllers.dart +++ b/lib/src/util/entangled_controllers.dart @@ -30,7 +30,7 @@ import 'sink_base.dart'; /// Note: these controllers are effectively synchronous, and so should only have /// events added to them at the end of event loops. Tuple2, StreamController> - createEntangledControllers() { +createEntangledControllers() { var buffer = _EntangledBuffer(); var controller1 = _EntangledController(buffer, true); @@ -67,8 +67,8 @@ class _EntangledBuffer { final StreamController controller2; _EntangledBuffer() - : controller1 = StreamController(sync: true), - controller2 = StreamController(sync: true) { + : controller1 = StreamController(sync: true), + controller2 = StreamController(sync: true) { controller1.onListen = _flush; controller2.onListen = _flush; } @@ -211,8 +211,11 @@ class _EntangledController extends StreamSinkBase @override Future addStream(Stream stream, {bool? cancelOnError}) { if (cancelOnError == true) { - stream = stream.transform(StreamTransformer( - (stream, _) => stream.listen(null, cancelOnError: true))); + stream = stream.transform( + StreamTransformer( + (stream, _) => stream.listen(null, cancelOnError: true), + ), + ); } return super.addStream(stream); diff --git a/lib/src/util/named_stream_transformer.dart b/lib/src/util/named_stream_transformer.dart index 1e0f4ae..bd0b19e 100644 --- a/lib/src/util/named_stream_transformer.dart +++ b/lib/src/util/named_stream_transformer.dart @@ -24,23 +24,24 @@ class NamedStreamTransformer implements StreamTransformer { final Stream Function(Stream) _bind; NamedStreamTransformer( - this._name, - StreamSubscription Function(Stream stream, bool cancelOnError) - onListen) - : _bind = StreamTransformer(onListen).bind; + this._name, + StreamSubscription Function(Stream stream, bool cancelOnError) + onListen, + ) : _bind = StreamTransformer(onListen).bind; NamedStreamTransformer.fromBind(this._name, this._bind); - NamedStreamTransformer.fromHandlers(this._name, - {void Function(S data, EventSink sink)? handleData, - void Function(Object error, StackTrace stackTrace, EventSink sink)? - handleError, - void Function(EventSink sink)? handleDone}) - : _bind = StreamTransformer.fromHandlers( - handleData: handleData, - handleError: handleError, - handleDone: handleDone) - .bind; + NamedStreamTransformer.fromHandlers( + this._name, { + void Function(S data, EventSink sink)? handleData, + void Function(Object error, StackTrace stackTrace, EventSink sink)? + handleError, + void Function(EventSink sink)? handleDone, + }) : _bind = StreamTransformer.fromHandlers( + handleData: handleData, + handleError: handleError, + handleDone: handleDone, + ).bind; @override Stream bind(Stream stream) => _bind(stream); diff --git a/lib/src/util/sink_base.dart b/lib/src/util/sink_base.dart index ee6c353..5427d65 100644 --- a/lib/src/util/sink_base.dart +++ b/lib/src/util/sink_base.dart @@ -89,10 +89,14 @@ abstract class StreamSinkBase extends EventSinkBase _addingStream = true; var completer = Completer.sync(); - stream.listen(onAdd, onError: onError, onDone: () { - _addingStream = false; - completer.complete(); - }); + stream.listen( + onAdd, + onError: onError, + onDone: () { + _addingStream = false; + completer.complete(); + }, + ); return completer.future; } diff --git a/pubspec.yaml b/pubspec.yaml index 10a1e67..b3a5734 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -6,7 +6,7 @@ description: repository: https://github.com/google/dart_cli_script environment: - sdk: '>=2.12.0 <4.0.0' + sdk: '>=3.13.0 <4.0.0' dependencies: async: ^2.8.0 diff --git a/test/buffered_script_test.dart b/test/buffered_script_test.dart index dc49aed..5c1cdfb 100644 --- a/test/buffered_script_test.dart +++ b/test/buffered_script_test.dart @@ -85,15 +85,16 @@ void main() { }); expect( - script.combineOutput().lines, - emitsInOrder([ - "stdout 1", - "stderr 1", - "stdout 2", - "stderr 2", - "stdout 3", - "stderr 3" - ])); + script.combineOutput().lines, + emitsInOrder([ + "stdout 1", + "stderr 1", + "stdout 2", + "stderr 2", + "stdout 3", + "stderr 3", + ]), + ); await pumpEventQueue(); await script.release(); @@ -195,18 +196,20 @@ void main() { group("makes available through done", () { test("a script failure", () async { expect( - BufferedScript.capture((_) { - throw ScriptException("script", 123); - }).done, - throwsScriptException(123)); + BufferedScript.capture((_) { + throw ScriptException("script", 123); + }).done, + throwsScriptException(123), + ); }); test("a Dart exception", () async { expect( - BufferedScript.capture((_) { - throw "oh no"; - }).done, - throwsScriptException(257)); + BufferedScript.capture((_) { + throw "oh no"; + }).done, + throwsScriptException(257), + ); }); }); } diff --git a/test/capture_test.dart b/test/capture_test.dart index 7ecb3d4..1f7e236 100644 --- a/test/capture_test.dart +++ b/test/capture_test.dart @@ -25,161 +25,167 @@ import 'util.dart'; void main() { test("forwards stdout from child processes", () { expect( - Script.capture((_) async { - await mainScript("print('child 1');").done; - await mainScript("print('child 2');").done; - await mainScript("print('child 3');").done; - }).stdout.lines, - emitsInOrder(["child 1", "child 2", "child 3"])); + Script.capture((_) async { + await mainScript("print('child 1');").done; + await mainScript("print('child 2');").done; + await mainScript("print('child 3');").done; + }).stdout.lines, + emitsInOrder(["child 1", "child 2", "child 3"]), + ); }); test("forwards stderr from child processes", () { expect( - Script.capture((_) async { - await mainScript("stderr.writeln('child 1');").done; - await mainScript("stderr.writeln('child 2');").done; - await mainScript("stderr.writeln('child 3');").done; - }).stderr.lines, - emitsInOrder(["child 1", "child 2", "child 3"])); + Script.capture((_) async { + await mainScript("stderr.writeln('child 1');").done; + await mainScript("stderr.writeln('child 2');").done; + await mainScript("stderr.writeln('child 3');").done; + }).stderr.lines, + emitsInOrder(["child 1", "child 2", "child 3"]), + ); }); test("forwards prints as stdout", () { expect( - Script.capture((_) async { - await mainScript("print('child 1');").done; - print("print 1"); - await mainScript("print('child 2');").done; - print("print 2"); - }).stdout.lines, - emitsInOrder([ - "child 1", - "print 1", - "child 2", - "print 2", - ])); + Script.capture((_) async { + await mainScript("print('child 1');").done; + print("print 1"); + await mainScript("print('child 2');").done; + print("print 2"); + }).stdout.lines, + emitsInOrder(["child 1", "print 1", "child 2", "print 2"]), + ); }); test("forwards prints even if the capture closes synchronously", () { expect( - Script.capture((_) async { - print("print"); - }).stdout.lines, - emitsInOrder(["print", emitsDone])); + Script.capture((_) async { + print("print"); + }).stdout.lines, + emitsInOrder(["print", emitsDone]), + ); }); test("forwards prints even if currentStdout is closed", () { expect( - Script.capture((_) async { - currentStdout.close(); - print("print"); - }).stdout.lines, - emitsInOrder(["print", emitsDone])); + Script.capture((_) async { + currentStdout.close(); + print("print"); + }).stdout.lines, + emitsInOrder(["print", emitsDone]), + ); }); test("forwards writes to currentStdout as stdout", () { expect( - Script.capture((_) async { - await mainScript("print('child 1');").done; - currentStdout.writeln("print 1"); - await mainScript("print('child 2');").done; - currentStdout.writeln("print 2"); - }).stdout.lines, - emitsInOrder([ - "child 1", - "print 1", - "child 2", - "print 2", - ])); + Script.capture((_) async { + await mainScript("print('child 1');").done; + currentStdout.writeln("print 1"); + await mainScript("print('child 2');").done; + currentStdout.writeln("print 2"); + }).stdout.lines, + emitsInOrder(["child 1", "print 1", "child 2", "print 2"]), + ); }); test("forwards writes to currentStderr as stderr", () { expect( - Script.capture((_) async { - await mainScript("stderr.writeln('child 1');").done; - currentStderr.writeln("print 1"); - await mainScript("stderr.writeln('child 2');").done; - currentStderr.writeln("print 2"); - }).stderr.lines, - emitsInOrder([ - "child 1", - "print 1", - "child 2", - "print 2", - ])); + Script.capture((_) async { + await mainScript("stderr.writeln('child 1');").done; + currentStderr.writeln("print 1"); + await mainScript("stderr.writeln('child 2');").done; + currentStderr.writeln("print 2"); + }).stderr.lines, + emitsInOrder(["child 1", "print 1", "child 2", "print 2"]), + ); }); group("interleaves prints and currentStdout", () { test("synchronously", () { expect( - Script.capture((_) { - currentStdout.writeln("stdout 1"); - print("stdout 2"); - currentStdout.writeln("stdout 3"); - print("stdout 4"); - currentStdout.writeln("stdout 5"); - }).combineOutput().lines, - emitsInOrder( - ["stdout 1", "stdout 2", "stdout 3", "stdout 4", "stdout 5"])); + Script.capture((_) { + currentStdout.writeln("stdout 1"); + print("stdout 2"); + currentStdout.writeln("stdout 3"); + print("stdout 4"); + currentStdout.writeln("stdout 5"); + }).combineOutput().lines, + emitsInOrder([ + "stdout 1", + "stdout 2", + "stdout 3", + "stdout 4", + "stdout 5", + ]), + ); }); test("asynchronously", () { expect( - Script.capture((_) async { - await pumpEventQueue(); - currentStdout.writeln("stdout 1"); - print("stdout 2"); - currentStdout.writeln("stdout 3"); - print("stdout 4"); - currentStdout.writeln("stdout 5"); - }).combineOutput().lines, - emitsInOrder( - ["stdout 1", "stdout 2", "stdout 3", "stdout 4", "stdout 5"])); + Script.capture((_) async { + await pumpEventQueue(); + currentStdout.writeln("stdout 1"); + print("stdout 2"); + currentStdout.writeln("stdout 3"); + print("stdout 4"); + currentStdout.writeln("stdout 5"); + }).combineOutput().lines, + emitsInOrder([ + "stdout 1", + "stdout 2", + "stdout 3", + "stdout 4", + "stdout 5", + ]), + ); }); }); group("interleaves writes to stdout and stderr", () { test("synchronously", () { expect( - Script.capture((_) { - currentStdout.writeln("stdout 1"); - currentStderr.writeln("stderr 1"); - print("stdout 2"); - currentStderr.writeln("stderr 2"); - currentStdout.writeln("stdout 3"); - currentStdout.writeln("stdout 4"); - currentStderr.writeln("stderr 3"); - }).combineOutput().lines, - emitsInOrder([ - "stdout 1", - "stderr 1", - "stdout 2", - "stderr 2", - "stdout 3", - "stdout 4", - "stderr 3" - ])); + Script.capture((_) { + currentStdout.writeln("stdout 1"); + currentStderr.writeln("stderr 1"); + print("stdout 2"); + currentStderr.writeln("stderr 2"); + currentStdout.writeln("stdout 3"); + currentStdout.writeln("stdout 4"); + currentStderr.writeln("stderr 3"); + }).combineOutput().lines, + emitsInOrder([ + "stdout 1", + "stderr 1", + "stdout 2", + "stderr 2", + "stdout 3", + "stdout 4", + "stderr 3", + ]), + ); }); test("asynchronously", () { expect( - Script.capture((_) async { - await pumpEventQueue(); - currentStdout.writeln("stdout 1"); - currentStderr.writeln("stderr 1"); - print("stdout 2"); - currentStderr.writeln("stderr 2"); - currentStdout.writeln("stdout 3"); - currentStdout.writeln("stdout 4"); - currentStderr.writeln("stderr 3"); - }).combineOutput().lines, - emitsInOrder([ - "stdout 1", - "stderr 1", - "stdout 2", - "stderr 2", - "stdout 3", - "stdout 4", - "stderr 3" - ])); + Script.capture((_) async { + await pumpEventQueue(); + currentStdout.writeln("stdout 1"); + currentStderr.writeln("stderr 1"); + print("stdout 2"); + currentStderr.writeln("stderr 2"); + currentStdout.writeln("stdout 3"); + currentStdout.writeln("stdout 4"); + currentStderr.writeln("stderr 3"); + }).combineOutput().lines, + emitsInOrder([ + "stdout 1", + "stderr 1", + "stdout 2", + "stderr 2", + "stdout 3", + "stdout 4", + "stderr 3", + ]), + ); }); }); @@ -207,8 +213,9 @@ void main() { }); test("completes with the given exit code when fail() is called", () { - var script = - Script.capture((_) => cli_script.fail("oh no", exitCode: 42)); + var script = Script.capture( + (_) => cli_script.fail("oh no", exitCode: 42), + ); script.stderr.drain(); expect(script.exitCode, completion(equals(42))); }); @@ -216,57 +223,65 @@ void main() { group("forwards a child script's exit code", () { test("when the child's done future isn't listened", () { expect( - Script.capture((_) { - mainScript("exitCode = 123;"); - }).exitCode, - completion(equals(123))); + Script.capture((_) { + mainScript("exitCode = 123;"); + }).exitCode, + completion(equals(123)), + ); }); test("when the child's done future is piped through the callback", () { expect( - Script.capture((_) => mainScript("exitCode = 123;").done).exitCode, - completion(equals(123))); + Script.capture((_) => mainScript("exitCode = 123;").done).exitCode, + completion(equals(123)), + ); }); test("when the child's done future error is top-leveled", () { expect( - Script.capture((_) { - mainScript("exitCode = 123;").done.then((_) {}); - }).exitCode, - completion(equals(123))); + Script.capture((_) { + mainScript("exitCode = 123;").done.then((_) {}); + }).exitCode, + completion(equals(123)), + ); }); }); group("ignores a child script's exit code", () { test("when the child's done future exception is handled out-of-band", () { expect( - Script.capture((_) { - mainScript("exitCode = 123;").done.catchError((_) {}); - }).exitCode, - completion(equals(0))); + Script.capture((_) { + mainScript("exitCode = 123;").done.catchError((_) {}); + }).exitCode, + completion(equals(0)), + ); }); test("when the child's done future exception is handled in-band", () { expect( - Script.capture((_) => - mainScript("exitCode = 123;").done.catchError((_) {})).exitCode, - completion(equals(0))); + Script.capture( + (_) => mainScript("exitCode = 123;").done.catchError((_) {}), + ).exitCode, + completion(equals(0)), + ); }); test("when the child's success field is accessed", () { expect( - Script.capture((_) { - mainScript("exitCode = 123;").success; - }).exitCode, - completion(equals(0))); + Script.capture((_) { + mainScript("exitCode = 123;").success; + }).exitCode, + completion(equals(0)), + ); }); test("when the child's exitCode field is accessed", () { expect( - Script.capture((_) { - mainScript("exitCode = 123;").exitCode; - }).exitCode, - completion(equals(0))); + Script.capture((_) { + mainScript("exitCode = 123;").exitCode; + }).exitCode, + completion(equals(0)), + ); }); }); @@ -308,8 +323,7 @@ void main() { expect(doneComplete, isTrue); }); - test( - "completes when an error is top-leveled even if the callback isn't " + test("completes when an error is top-leveled even if the callback isn't " "done", () async { var script = Script.capture((_) { Future.error("oh no"); @@ -346,26 +360,30 @@ void main() { test("spawning a child script throws an error", () { var childSpawnedCompleter = Completer(); var captureDoneCompleter = Completer(); - captureDoneCompleter.complete(Script.capture((_) { - captureDoneCompleter.future.then((_) { - try { - mainScript(""); - childSpawnedCompleter.complete(); - } catch (error, stackTrace) { - childSpawnedCompleter.completeError(error, stackTrace); - } - }); - }).done); + captureDoneCompleter.complete( + Script.capture((_) { + captureDoneCompleter.future.then((_) { + try { + mainScript(""); + childSpawnedCompleter.complete(); + } catch (error, stackTrace) { + childSpawnedCompleter.completeError(error, stackTrace); + } + }); + }).done, + ); expect(childSpawnedCompleter.future, throwsStateError); }); test("additional errors are swallowed", () async { var captureDoneCompleter = Completer(); - captureDoneCompleter.complete(Script.capture((_) async { - await captureDoneCompleter.future; - throw "oh no"; - }).done); + captureDoneCompleter.complete( + Script.capture((_) async { + await captureDoneCompleter.future; + throw "oh no"; + }).done, + ); // Give "oh no" time to get top-leveled if it's going to. await pumpEventQueue(); diff --git a/test/cli_arguments_test.dart b/test/cli_arguments_test.dart index 71ff418..3f38fc3 100644 --- a/test/cli_arguments_test.dart +++ b/test/cli_arguments_test.dart @@ -32,8 +32,10 @@ void main() { onPosixOrWithGlobTrue((glob) { test("for a string containing an invalid glob", () { - expect(() => CliArguments.parse("a [", glob: glob), - throwsFormatException); + expect( + () => CliArguments.parse("a [", glob: glob), + throwsFormatException, + ); }); }); }); @@ -110,8 +112,10 @@ void main() { }); test("with plain text adjacent to quotes", () async { - expect(await _resolve("\"foo bar\"baz'bip bop'"), - equals(["foo barbazbip bop"])); + expect( + await _resolve("\"foo bar\"baz'bip bop'"), + equals(["foo barbazbip bop"]), + ); }); onWindowsOrWithGlobFalse((glob) { @@ -131,8 +135,10 @@ void main() { }); test("with different quoting styles", () async { - expect(await _resolve("a \"b c\" 'd e' f\\ g"), - equals(["a", "b c", "d e", "f g"])); + expect( + await _resolve("a \"b c\" 'd e' f\\ g"), + equals(["a", "b c", "d e", "f g"]), + ); }); }); }); @@ -157,8 +163,10 @@ void main() { var pattern = p.join(Glob.quote(d.sandbox), "*.txt"); var args = await _resolve("ls $pattern", glob: glob); expect(args.first, equals("ls")); - expect(args.sublist(1), - unorderedEquals([d.path("foo.txt"), d.path("bar.txt")])); + expect( + args.sublist(1), + unorderedEquals([d.path("foo.txt"), d.path("bar.txt")]), + ); }); test("ignores glob characters in quotes", () async { @@ -166,7 +174,9 @@ void main() { await d.file("bar.txt").create(); await d.file("baz.zip").create(); expect( - await _resolve("ls '*.txt'", glob: glob), equals(["ls", "*.txt"])); + await _resolve("ls '*.txt'", glob: glob), + equals(["ls", "*.txt"]), + ); }); test("ignores backslash-escaped glob characters", () async { @@ -174,7 +184,9 @@ void main() { await d.file("bar.txt").create(); await d.file("baz.zip").create(); expect( - await _resolve(r"ls \*.txt", glob: glob), equals(["ls", "*.txt"])); + await _resolve(r"ls \*.txt", glob: glob), + equals(["ls", "*.txt"]), + ); }); test("returns plain strings for globs that don't match", () async { diff --git a/test/environment_test.dart b/test/environment_test.dart index 652cb5b..1995ca8 100644 --- a/test/environment_test.dart +++ b/test/environment_test.dart @@ -32,22 +32,25 @@ void main() { expect(env, containsPair(varName, "value")); }); - group("with a non-empty environment", () { - test("can override existing variables", () { - var varName = Platform.environment.keys.first; - env[varName] = "new special fancy value"; - expect(env, containsPair(varName, "new special fancy value")); - }); - - test("can remove existing variables", () { - var varName = Platform.environment.keys.last; - env.remove(varName); - expect(env, isNot(contains(varName))); - }); - }, - skip: Platform.environment.isEmpty - ? "These tests require at least one environment variable to be set" - : null); + group( + "with a non-empty environment", + () { + test("can override existing variables", () { + var varName = Platform.environment.keys.first; + env[varName] = "new special fancy value"; + expect(env, containsPair(varName, "new special fancy value")); + }); + + test("can remove existing variables", () { + var varName = Platform.environment.keys.last; + env.remove(varName); + expect(env, isNot(contains(varName))); + }); + }, + skip: Platform.environment.isEmpty + ? "These tests require at least one environment variable to be set" + : null, + ); }); group("withEnv", () { @@ -63,43 +66,55 @@ void main() { test("inner modifications don't modify the outer environment", () { var varName = uid(); - withEnv(expectAsync0(() { - env[varName] = "value"; - expect(env, containsPair(varName, "value")); - }), {}); + withEnv( + expectAsync0(() { + env[varName] = "value"; + expect(env, containsPair(varName, "value")); + }), + {}, + ); expect(env, isNot(contains(varName))); }); - test("with includeParentEnvironment: false creates an empty environment", - () { - withEnv(expectAsync0(() => expect(env, isEmpty)), {}, - includeParentEnvironment: false); - }); + test( + "with includeParentEnvironment: false creates an empty environment", + () { + withEnv( + expectAsync0(() => expect(env, isEmpty)), + {}, + includeParentEnvironment: false, + ); + }, + ); }); test("overrides outer variables", () { var varName = uid(); env[varName] = "outer value"; withEnv( - expectAsync0(() => expect(env, containsPair(varName, "inner value"))), - {varName: "inner value"}); + expectAsync0(() => expect(env, containsPair(varName, "inner value"))), + {varName: "inner value"}, + ); expect(env, containsPair(varName, "outer value")); }); test("removes outer variables with value null", () { var varName = uid(); env[varName] = "outer value"; - withEnv(expectAsync0(() => expect(env, isNot(contains(varName)))), - {varName: null}); + withEnv(expectAsync0(() => expect(env, isNot(contains(varName)))), { + varName: null, + }); expect(env, containsPair(varName, "outer value")); }); - test("replaces the outer environment with includeParentEnvironment: false", - () { - withEnv(expectAsync0(() => expect(env, equals({"FOO": "bar"}))), - {"FOO": "bar"}, - includeParentEnvironment: false); - }); + test( + "replaces the outer environment with includeParentEnvironment: false", + () { + withEnv(expectAsync0(() => expect(env, equals({"FOO": "bar"}))), { + "FOO": "bar", + }, includeParentEnvironment: false); + }, + ); }); group("on Windows", () { @@ -122,8 +137,9 @@ void main() { env[varName] = "outer value"; env.remove(varName.toUpperCase()); withEnv( - expectAsync0(() => expect(env, containsPair(varName, "inner value"))), - {varName.toUpperCase(): "inner value"}); + expectAsync0(() => expect(env, containsPair(varName, "inner value"))), + {varName.toUpperCase(): "inner value"}, + ); }); }, testOn: 'windows'); } diff --git a/test/ls_test.dart b/test/ls_test.dart index 7f363e7..386fc9a 100644 --- a/test/ls_test.dart +++ b/test/ls_test.dart @@ -27,11 +27,12 @@ void main() { await d.file("baz.zip").create(); expect( - ls("*.txt", root: d.sandbox), - emitsInOrder([ - emitsInAnyOrder(["foo.txt", "bar.txt"]), - emitsDone - ])); + ls("*.txt", root: d.sandbox), + emitsInOrder([ + emitsInAnyOrder(["foo.txt", "bar.txt"]), + emitsDone, + ]), + ); }); test("an absolute glob expands to absolute paths", () async { @@ -40,11 +41,12 @@ void main() { await d.file("baz.zip").create(); expect( - ls(p.join(Glob.quote(d.sandbox), "*.txt"), root: d.sandbox), - emitsInOrder([ - emitsInAnyOrder([d.path("foo.txt"), d.path("bar.txt")]), - emitsDone - ])); + ls(p.join(Glob.quote(d.sandbox), "*.txt"), root: d.sandbox), + emitsInOrder([ + emitsInAnyOrder([d.path("foo.txt"), d.path("bar.txt")]), + emitsDone, + ]), + ); }); }); } diff --git a/test/pipe_test.dart b/test/pipe_test.dart index 6d210cf..c911822 100644 --- a/test/pipe_test.dart +++ b/test/pipe_test.dart @@ -24,7 +24,8 @@ import 'util.dart'; void main() { test("pipes one script's stdout into another's stdin", () { - var pipeline = mainScript('print("hello!");') | + var pipeline = + mainScript('print("hello!");') | Script.capture((stdin) async { await expectLater(stdin.lines, emits("hello!")); }); @@ -32,7 +33,8 @@ void main() { }); test("pipes the pipeline's stdin into the first script's stdin", () { - var pipeline = mainScript('print("a: " + stdin.readLineSync()!);') | + var pipeline = + mainScript('print("a: " + stdin.readLineSync()!);') | mainScript('print("b: " + stdin.readLineSync()!);'); pipeline.stdin.writeln("hello!"); expect(pipeline.stdout.lines, emits("b: a: hello!")); @@ -41,7 +43,7 @@ void main() { test("pipes a scriptlike object", () { var pipeline = mainScript('stdout.add(zlib.encode(utf8.encode("hello!")));') | - zlib.decoder; + zlib.decoder; expect(pipeline.stdout.lines, emits("hello!")); }); @@ -51,14 +53,15 @@ void main() { mainScript('print("a: " + stdin.readLineSync()!);'), mainScript('print("b: " + stdin.readLineSync()!);'), mainScript('print("c: " + stdin.readLineSync()!);'), - mainScript('print("d: " + stdin.readLineSync()!);') + mainScript('print("d: " + stdin.readLineSync()!);'), ]); pipeline.stdin.writeln("hello!"); expect(pipeline.stdout.lines, emits("d: c: b: a: hello!")); }); test("with repeated |", () { - var pipeline = mainScript('print("a: " + stdin.readLineSync()!);') | + var pipeline = + mainScript('print("a: " + stdin.readLineSync()!);') | mainScript('print("b: " + stdin.readLineSync()!);') | mainScript('print("c: " + stdin.readLineSync()!);') | mainScript('print("d: " + stdin.readLineSync()!);'); @@ -70,7 +73,8 @@ void main() { test("only includes the last script's stderr in the pipeline's", () { late Script pipeline; var captured = Script.capture((_) { - pipeline = mainScript('stderr.writeln("script 1");') | + pipeline = + mainScript('stderr.writeln("script 1");') | mainScript('stderr.writeln("script 2");'); }); @@ -114,95 +118,97 @@ void main() { }); group("if one fails", () { - group("waits for both scripts to exit and returns the failing exit code", - () { - group("if the first exits first", () { - test("and the first fails", () async { - var completer = Completer(); - var script1 = mainScript("exitCode = 123;"); - var pipeline = script1 | Script.capture((_) => completer.future); - - int? exitCode; - pipeline.exitCode.then((exitCode_) => exitCode = exitCode_); - expect(await script1.exitCode, equals(123)); - await pumpEventQueue(); - expect(exitCode, isNull); - - completer.complete(); - await pumpEventQueue(); - expect(exitCode, equals(123)); - }); + group( + "waits for both scripts to exit and returns the failing exit code", + () { + group("if the first exits first", () { + test("and the first fails", () async { + var completer = Completer(); + var script1 = mainScript("exitCode = 123;"); + var pipeline = script1 | Script.capture((_) => completer.future); + + int? exitCode; + pipeline.exitCode.then((exitCode_) => exitCode = exitCode_); + expect(await script1.exitCode, equals(123)); + await pumpEventQueue(); + expect(exitCode, isNull); + + completer.complete(); + await pumpEventQueue(); + expect(exitCode, equals(123)); + }); - test("and the second fails", () async { - var completer = Completer(); - var script1 = mainScript(""); - var pipeline = script1 | - Script.capture((_) async { - await completer.future; - throw "oh no"; - }); - - // Don't print the unhandled error. - pipeline.stderr.listen(null); - - int? exitCode; - pipeline.exitCode.then((exitCode_) => exitCode = exitCode_); - await script1.done; - await pumpEventQueue(); - expect(exitCode, isNull); - - completer.complete(); - await pumpEventQueue(); - expect(exitCode, equals(257)); + test("and the second fails", () async { + var completer = Completer(); + var script1 = mainScript(""); + var pipeline = + script1 | + Script.capture((_) async { + await completer.future; + throw "oh no"; + }); + + // Don't print the unhandled error. + pipeline.stderr.listen(null); + + int? exitCode; + pipeline.exitCode.then((exitCode_) => exitCode = exitCode_); + await script1.done; + await pumpEventQueue(); + expect(exitCode, isNull); + + completer.complete(); + await pumpEventQueue(); + expect(exitCode, equals(257)); + }); }); - }); - group("if the second exits first", () { - test("and the first fails", () async { - var completer = Completer(); + group("if the second exits first", () { + test("and the first fails", () async { + var completer = Completer(); - var capture = Script.capture((_) async { - await completer.future; - throw "oh no"; - }); + var capture = Script.capture((_) async { + await completer.future; + throw "oh no"; + }); - // Don't print the unhandled error. - capture.stderr.listen(null); + // Don't print the unhandled error. + capture.stderr.listen(null); - var script2 = mainScript(""); - var pipeline = capture | script2; + var script2 = mainScript(""); + var pipeline = capture | script2; - int? exitCode; - pipeline.exitCode.then((exitCode_) => exitCode = exitCode_); - await script2.done; - await pumpEventQueue(); - expect(exitCode, isNull); + int? exitCode; + pipeline.exitCode.then((exitCode_) => exitCode = exitCode_); + await script2.done; + await pumpEventQueue(); + expect(exitCode, isNull); - completer.complete(); - await pumpEventQueue(); - expect(exitCode, equals(257)); - }); + completer.complete(); + await pumpEventQueue(); + expect(exitCode, equals(257)); + }); - test("and the second fails", () async { - var completer = Completer(); - var script2 = mainScript("exitCode = 123;"); - var pipeline = Script.capture((_) => completer.future) | script2; + test("and the second fails", () async { + var completer = Completer(); + var script2 = mainScript("exitCode = 123;"); + var pipeline = Script.capture((_) => completer.future) | script2; - int? exitCode; - pipeline.exitCode.then((exitCode_) => exitCode = exitCode_); - expect(await script2.exitCode, equals(123)); - await pumpEventQueue(); - expect(exitCode, isNull); + int? exitCode; + pipeline.exitCode.then((exitCode_) => exitCode = exitCode_); + expect(await script2.exitCode, equals(123)); + await pumpEventQueue(); + expect(exitCode, isNull); - completer.complete(); - await pumpEventQueue(); - expect(exitCode, equals(123)); + completer.complete(); + await pumpEventQueue(); + expect(exitCode, equals(123)); + }); }); - }); - }); + }, + ); - group( - "the error isn't top-leveled if it's handled only at the pipeline " + group("the error isn't top-leveled if it's handled only at the pipeline " "level", () { test("if the first fails", () async { var pipeline = mainScript("exitCode = 1;") | mainScript(""); @@ -227,7 +233,8 @@ void main() { test("if the first exits first", () async { var completer = Completer(); var script1 = mainScript("exitCode = 123;"); - var pipeline = script1 | + var pipeline = + script1 | Script.capture((_) async { await completer.future; throw "oh no"; @@ -273,8 +280,7 @@ void main() { }); }); - test( - "the error isn't top-leveled if it's handled only at the pipeline " + test("the error isn't top-leveled if it's handled only at the pipeline " "level", () async { var pipeline = mainScript("exitCode = 1;") | mainScript("exitCode = 2;"); @@ -289,8 +295,11 @@ void main() { group("pipes in", () { group("a byte stream", () { test("without errors", () { - var pipeline = Stream>.fromIterable( - [utf8.encode("foo"), utf8.encode("bar")]) | + var pipeline = + Stream>.fromIterable([ + utf8.encode("foo"), + utf8.encode("bar"), + ]) | mainScript("stdin.pipe(stdout);"); expect(pipeline.stdout.lines, emitsInOrder(["foobar", emitsDone])); }); @@ -308,7 +317,8 @@ void main() { group("a string stream", () { test("without errors", () { - var pipeline = Stream.fromIterable(["foo", "bar"]) | + var pipeline = + Stream.fromIterable(["foo", "bar"]) | mainScript("stdin.pipe(stdout);"); expect(pipeline.stdout.lines, emitsInOrder(["foo", "bar", emitsDone])); }); @@ -324,7 +334,8 @@ void main() { }); test("a chunk list", () { - var pipeline = [utf8.encode("foo"), utf8.encode("bar")] | + var pipeline = + [utf8.encode("foo"), utf8.encode("bar")] | mainScript("stdin.pipe(stdout);"); expect(pipeline.stdout.lines, emitsInOrder(["foobar", emitsDone])); }); diff --git a/test/script_wrapper_test.dart b/test/script_wrapper_test.dart index 5e03957..e0d7d68 100644 --- a/test/script_wrapper_test.dart +++ b/test/script_wrapper_test.dart @@ -38,9 +38,12 @@ void main() { test("surfaces an error as a Script error", () { var script = Script.fromByteTransformer( - StreamTransformer.fromHandlers(handleData: (_, sink) { - sink.addError("oh no!"); - })); + StreamTransformer.fromHandlers( + handleData: (_, sink) { + sink.addError("oh no!"); + }, + ), + ); script.stdin.add([1, 2, 3]); expect(script.stderr.lines, emitsThrough(contains("oh no!"))); expect(script.exitCode, completion(equals(257))); @@ -48,9 +51,11 @@ void main() { }); group("Script.fromStringTransformer()", () { - var transformer = StreamTransformer.fromBind((stream) => - stream.map( - (string) => String.fromCharCodes(string.runes.toList().reversed))); + var transformer = StreamTransformer.fromBind( + (stream) => stream.map( + (string) => String.fromCharCodes(string.runes.toList().reversed), + ), + ); test("converts data from stdin", () { var script = Script.fromLineTransformer(transformer); @@ -67,9 +72,12 @@ void main() { test("surfaces an error as a Script error", () { var script = Script.fromByteTransformer( - StreamTransformer.fromHandlers(handleData: (_, sink) { - sink.addError("oh no!"); - })); + StreamTransformer.fromHandlers( + handleData: (_, sink) { + sink.addError("oh no!"); + }, + ), + ); script.stdin.writeln("hello!"); expect(script.stderr.lines, emitsThrough(contains("oh no!"))); expect(script.exitCode, completion(equals(257))); diff --git a/test/signal_test.dart b/test/signal_test.dart index bb6f492..60bbaed 100644 --- a/test/signal_test.dart +++ b/test/signal_test.dart @@ -91,12 +91,14 @@ void main() { test('prints from signal handler', () async { var completer = Completer(); - var script = Script.capture((_) async => await completer.future, - onSignal: (signal) { - print('stdout: $signal'); - currentStderr.writeln('stderr: $signal'); - return true; - }); + var script = Script.capture( + (_) async => await completer.future, + onSignal: (signal) { + print('stdout: $signal'); + currentStderr.writeln('stderr: $signal'); + return true; + }, + ); var lines = script.combineOutput().lines; expect(script.kill(), true); @@ -109,11 +111,13 @@ void main() { test('does not call signal handler after script exited', () async { var completer = Completer(); var killCalls = 0; - var script = Script.capture((_) async => await completer.future, - onSignal: (signal) { - killCalls++; - return true; - }); + var script = Script.capture( + (_) async => await completer.future, + onSignal: (signal) { + killCalls++; + return true; + }, + ); expect(script.kill(), true); @@ -126,8 +130,10 @@ void main() { test('catches script error from signal handler', () async { var completer = Completer(); - var script = Script.capture((_) async => await completer.future, - onSignal: (signal) => throw ScriptException('onSignal', 42)); + var script = Script.capture( + (_) async => await completer.future, + onSignal: (signal) => throw ScriptException('onSignal', 42), + ); expect(script.kill(), false); @@ -138,8 +144,10 @@ void main() { test('catches exception from signal handler', () async { var completer = Completer(); - var script = Script.capture((_) async => await completer.future, - onSignal: (signal) => throw Exception('oh no!')); + var script = Script.capture( + (_) async => await completer.future, + onSignal: (signal) => throw Exception('oh no!'), + ); expect(script.kill(), false); @@ -152,15 +160,18 @@ void main() { test('BufferedScript.capture can capture Script.kill signals', () async { var completer = Completer(); var signalStream = StreamController(); - var script = BufferedScript.capture((_) async { - signalStream.stream.listen(print); - await completer.future; - await signalStream.close(); - print('bye!'); - }, onSignal: (signal) { - signalStream.sink.add(signal); - return true; - }); + var script = BufferedScript.capture( + (_) async { + signalStream.stream.listen(print); + await completer.future; + await signalStream.close(); + print('bye!'); + }, + onSignal: (signal) { + signalStream.sink.add(signal); + return true; + }, + ); var done = false; var output = script.output.then((v) { @@ -179,9 +190,11 @@ void main() { }); test('interrupts a Script.fromLineTransformer', () async { - var script = Script.fromLineTransformer(StreamTransformer.fromHandlers( - handleData: (data, sink) => sink.add(data.toUpperCase()), - )); + var script = Script.fromLineTransformer( + StreamTransformer.fromHandlers( + handleData: (data, sink) => sink.add(data.toUpperCase()), + ), + ); var events = []; var done = false; @@ -221,11 +234,7 @@ void main() { completer.complete(); expect(script.done, throwsScriptException(143)); - expect(await lines.toList(), [ - 'from a: before', - 'b: SIGTERM', - 'b: bye!', - ]); + expect(await lines.toList(), ['from a: before', 'b: SIGTERM', 'b: bye!']); expect(script.kill(), false); }); @@ -233,7 +242,8 @@ void main() { test('cancels underlying stream', () async { var controller = StreamController(); var completer = Completer(); - var script = controller.stream | + var script = + controller.stream | Script.capture((_) async => await completer.future); var canceled = false; controller.onCancel = () => canceled = true; @@ -290,8 +300,10 @@ void main() { test('can capture Script.kill signals', () async { var signalCompleter = Completer(); var controller = StreamController(); - var script = - controller.stream.xargs(print, onSignal: signalCompleter.complete); + var script = controller.stream.xargs( + print, + onSignal: signalCompleter.complete, + ); expect(script.kill(), true); expect(await signalCompleter.future, ProcessSignal.sigterm); @@ -313,7 +325,8 @@ void main() { group('on xargs from stdin', () { test('interrupts without maxArgs', () async { var completer = Completer(); - var script = Script.capture((_) async { + var script = + Script.capture((_) async { print('before'); await completer.future; print('after'); @@ -333,7 +346,8 @@ void main() { test('interrupts with maxArgs', () async { var completer = Completer(); - var script = Script.capture((_) async { + var script = + Script.capture((_) async { print('a\nb\nc'); await completer.future; print('d'); @@ -354,7 +368,8 @@ void main() { test('can capture Script.kill signals', () async { var signalCompleter = Completer(); - var script = Script.capture((_) async => await signalCompleter.future) | + var script = + Script.capture((_) async => await signalCompleter.future) | xargs(print, onSignal: signalCompleter.complete); expect(script.kill(), true); @@ -366,15 +381,18 @@ void main() { test('on silenceUntilFailure can capture Script.kill signals', () async { var signalStream = StreamController(); var completer = Completer(); - var script = silenceUntilFailure((_) async { - signalStream.stream.listen(print); - await completer.future; - await signalStream.close(); - throw ScriptException('testerino', 1); - }, onSignal: (signal) { - signalStream.sink.add(signal); - return true; - }); + var script = silenceUntilFailure( + (_) async { + signalStream.stream.listen(print); + await completer.future; + await signalStream.close(); + throw ScriptException('testerino', 1); + }, + onSignal: (signal) { + signalStream.sink.add(signal); + return true; + }, + ); var stderr = script.stderr.text; var stdout = script.stdout.text; @@ -419,31 +437,37 @@ void main() { Script _watchSignalsAndExit(Completer completer) { var signalStream = StreamController(); - return Script.capture((_) async { - signalStream.stream.listen(print); - await completer.future; - await signalStream.close(); - print('bye!'); - }, onSignal: (signal) { - signalStream.sink.add(signal); - return true; - }); + return Script.capture( + (_) async { + signalStream.stream.listen(print); + await completer.future; + await signalStream.close(); + print('bye!'); + }, + onSignal: (signal) { + signalStream.sink.add(signal); + return true; + }, + ); } Script _watchSignalsAndStdin(Completer completer) { var signalStream = StreamController(); - return Script.capture((stdin) async { - signalStream.stream.listen((event) { - print('b: $event'); - }); - await for (var line in stdin.lines) { - print('from a: $line'); - } - await completer.future; - print('b: bye!'); - await signalStream.close(); - }, onSignal: (signal) { - signalStream.sink.add(signal); - return true; - }); + return Script.capture( + (stdin) async { + signalStream.stream.listen((event) { + print('b: $event'); + }); + await for (var line in stdin.lines) { + print('from a: $line'); + } + await completer.future; + print('b: bye!'); + await signalStream.close(); + }, + onSignal: (signal) { + signalStream.sink.add(signal); + return true; + }, + ); } diff --git a/test/stdio_test.dart b/test/stdio_test.dart index a405975..a817d30 100644 --- a/test/stdio_test.dart +++ b/test/stdio_test.dart @@ -28,56 +28,64 @@ void main() { group("scripts", () { test("started synchronously", () { expect( - Script.capture((_) { - silenceStdout(() => mainScript("print('howdy!');")); - }).stdout, - emitsDone); + Script.capture((_) { + silenceStdout(() => mainScript("print('howdy!');")); + }).stdout, + emitsDone, + ); }); test("started asynchronously", () { expect( - Script.capture((_) { - silenceStdout(() => - scheduleMicrotask(() => mainScript("print('howdy!');"))); - }).stdout, - emitsDone); + Script.capture((_) { + silenceStdout( + () => scheduleMicrotask(() => mainScript("print('howdy!');")), + ); + }).stdout, + emitsDone, + ); }); }); group("print", () { test("synchronously", () { expect( - Script.capture((_) { - silenceStdout(() => print("howdy!")); - }).stdout, - emitsDone); + Script.capture((_) { + silenceStdout(() => print("howdy!")); + }).stdout, + emitsDone, + ); }); test("asynchronously", () { expect( - Script.capture((_) { - silenceStdout(() => scheduleMicrotask(() => print('howdy!'))); - }).stdout, - emitsDone); + Script.capture((_) { + silenceStdout(() => scheduleMicrotask(() => print('howdy!'))); + }).stdout, + emitsDone, + ); }); }); group("currentStdout", () { test("synchronously", () { expect( - Script.capture((_) { - silenceStdout(() => currentStdout.writeln("howdy!")); - }).stdout, - emitsDone); + Script.capture((_) { + silenceStdout(() => currentStdout.writeln("howdy!")); + }).stdout, + emitsDone, + ); }); test("asynchronously", () { expect( - Script.capture((_) { - silenceStdout(() => - scheduleMicrotask(() => currentStdout.writeln('howdy!'))); - }).stdout, - emitsDone); + Script.capture((_) { + silenceStdout( + () => scheduleMicrotask(() => currentStdout.writeln('howdy!')), + ); + }).stdout, + emitsDone, + ); }); }); }); @@ -86,38 +94,46 @@ void main() { group("scripts", () { test("started synchronously", () { expect( - Script.capture((_) { - silenceStderr(() => mainScript("stderr.writeln('howdy!');")); - }).stderr, - emitsDone); + Script.capture((_) { + silenceStderr(() => mainScript("stderr.writeln('howdy!');")); + }).stderr, + emitsDone, + ); }); test("started asynchronously", () { expect( - Script.capture((_) { - silenceStderr(() => scheduleMicrotask( - () => mainScript("stderr.writeln('howdy!');"))); - }).stderr, - emitsDone); + Script.capture((_) { + silenceStderr( + () => scheduleMicrotask( + () => mainScript("stderr.writeln('howdy!');"), + ), + ); + }).stderr, + emitsDone, + ); }); }); group("currentStderr", () { test("synchronously", () { expect( - Script.capture((_) { - silenceStderr(() => currentStderr.writeln("howdy!")); - }).stderr, - emitsDone); + Script.capture((_) { + silenceStderr(() => currentStderr.writeln("howdy!")); + }).stderr, + emitsDone, + ); }); test("asynchronously", () { expect( - Script.capture((_) { - silenceStderr(() => - scheduleMicrotask(() => currentStderr.writeln('howdy!'))); - }).stderr, - emitsDone); + Script.capture((_) { + silenceStderr( + () => scheduleMicrotask(() => currentStderr.writeln('howdy!')), + ); + }).stderr, + emitsDone, + ); }); }); }); @@ -126,80 +142,96 @@ void main() { group("scripts", () { test("started synchronously", () { expect( - Script.capture((_) { - silenceOutput(() => mainScript(""" + Script.capture((_) { + silenceOutput( + () => mainScript(""" print('howdy!'); stderr.writeln('howdy!'); - """)); - }).combineOutput(), - emitsDone); + """), + ); + }).combineOutput(), + emitsDone, + ); }); test("started asynchronously", () { expect( - Script.capture((_) { - silenceOutput(() => scheduleMicrotask(() => mainScript(""" + Script.capture((_) { + silenceOutput( + () => scheduleMicrotask( + () => mainScript(""" print('howdy!'); stderr.writeln('howdy!'); - """))); - }).combineOutput(), - emitsDone); + """), + ), + ); + }).combineOutput(), + emitsDone, + ); }); }); group("print", () { test("synchronously", () { expect( - Script.capture((_) { - silenceOutput(() => print("howdy!")); - }).combineOutput(), - emitsDone); + Script.capture((_) { + silenceOutput(() => print("howdy!")); + }).combineOutput(), + emitsDone, + ); }); test("asynchronously", () { expect( - Script.capture((_) { - silenceOutput(() => scheduleMicrotask(() => print('howdy!'))); - }).combineOutput(), - emitsDone); + Script.capture((_) { + silenceOutput(() => scheduleMicrotask(() => print('howdy!'))); + }).combineOutput(), + emitsDone, + ); }); }); group("currentStdout", () { test("synchronously", () { expect( - Script.capture((_) { - silenceOutput(() => currentStdout.writeln("howdy!")); - }).combineOutput(), - emitsDone); + Script.capture((_) { + silenceOutput(() => currentStdout.writeln("howdy!")); + }).combineOutput(), + emitsDone, + ); }); test("asynchronously", () { expect( - Script.capture((_) { - silenceOutput(() => - scheduleMicrotask(() => currentStdout.writeln('howdy!'))); - }).combineOutput(), - emitsDone); + Script.capture((_) { + silenceOutput( + () => scheduleMicrotask(() => currentStdout.writeln('howdy!')), + ); + }).combineOutput(), + emitsDone, + ); }); }); group("currentStderr", () { test("synchronously", () { expect( - Script.capture((_) { - silenceOutput(() => currentStderr.writeln("howdy!")); - }).combineOutput(), - emitsDone); + Script.capture((_) { + silenceOutput(() => currentStderr.writeln("howdy!")); + }).combineOutput(), + emitsDone, + ); }); test("asynchronously", () { expect( - Script.capture((_) { - silenceOutput(() => - scheduleMicrotask(() => currentStderr.writeln('howdy!'))); - }).combineOutput(), - emitsDone); + Script.capture((_) { + silenceOutput( + () => scheduleMicrotask(() => currentStderr.writeln('howdy!')), + ); + }).combineOutput(), + emitsDone, + ); }); }); }); @@ -209,92 +241,109 @@ void main() { group("scripts", () { test("started synchronously", () { expect( - Script.capture((_) { - silenceUntilFailure((_) => mainScript(""" + Script.capture((_) { + silenceUntilFailure( + (_) => mainScript(""" print('howdy!'); stderr.writeln('howdy!'); - """)); - }).combineOutput(), - emitsDone); + """), + ); + }).combineOutput(), + emitsDone, + ); }); test("started asynchronously", () { expect( - Script.capture((_) { - silenceUntilFailure( - (_) => scheduleMicrotask(() => mainScript(""" + Script.capture((_) { + silenceUntilFailure( + (_) => scheduleMicrotask( + () => mainScript(""" print('howdy!'); stderr.writeln('howdy!'); - """))); - }).combineOutput(), - emitsDone); + """), + ), + ); + }).combineOutput(), + emitsDone, + ); }); }); group("print", () { test("synchronously", () { expect( - Script.capture((_) { - silenceUntilFailure((_) => print("howdy!")); - }).combineOutput(), - emitsDone); + Script.capture((_) { + silenceUntilFailure((_) => print("howdy!")); + }).combineOutput(), + emitsDone, + ); }); test("asynchronously", () { expect( - Script.capture((_) { - silenceUntilFailure( - (_) => scheduleMicrotask(() => print('howdy!'))); - }).combineOutput(), - emitsDone); + Script.capture((_) { + silenceUntilFailure( + (_) => scheduleMicrotask(() => print('howdy!')), + ); + }).combineOutput(), + emitsDone, + ); }); }); group("currentStdout", () { test("synchronously", () { expect( - Script.capture((_) { - silenceUntilFailure((_) => currentStdout.writeln("howdy!")); - }).combineOutput(), - emitsDone); + Script.capture((_) { + silenceUntilFailure((_) => currentStdout.writeln("howdy!")); + }).combineOutput(), + emitsDone, + ); }); test("asynchronously", () { expect( - Script.capture((_) { - silenceUntilFailure((_) => - scheduleMicrotask(() => currentStdout.writeln('howdy!'))); - }).combineOutput(), - emitsDone); + Script.capture((_) { + silenceUntilFailure( + (_) => scheduleMicrotask(() => currentStdout.writeln('howdy!')), + ); + }).combineOutput(), + emitsDone, + ); }); }); group("currentStderr", () { test("synchronously", () { expect( - Script.capture((_) { - silenceUntilFailure((_) => currentStderr.writeln("howdy!")); - }).combineOutput(), - emitsDone); + Script.capture((_) { + silenceUntilFailure((_) => currentStderr.writeln("howdy!")); + }).combineOutput(), + emitsDone, + ); }); test("asynchronously", () { expect( - Script.capture((_) { - silenceUntilFailure((_) => - scheduleMicrotask(() => currentStderr.writeln('howdy!'))); - }).combineOutput(), - emitsDone); + Script.capture((_) { + silenceUntilFailure( + (_) => scheduleMicrotask(() => currentStderr.writeln('howdy!')), + ); + }).combineOutput(), + emitsDone, + ); }); }); }); test("doesn't silence stdout with stderrOnly: true", () { expect( - Script.capture((_) { - silenceUntilFailure((_) => print("howdy!"), stderrOnly: true); - }).lines, - emitsInOrder([emits("howdy!"), emitsDone])); + Script.capture((_) { + silenceUntilFailure((_) => print("howdy!"), stderrOnly: true); + }).lines, + emitsInOrder([emits("howdy!"), emitsDone]), + ); }); group("releases stdio when the callback fails", () { @@ -326,8 +375,9 @@ void main() { test("a script cancels its stdin subscription when it exits", () async { var canceled = false; - var controller = - StreamController>(onCancel: () => canceled = true); + var controller = StreamController>( + onCancel: () => canceled = true, + ); var script = Script.capture((_) => Future.delayed(Duration.zero)); controller.stream | script; diff --git a/test/sub_process_test.dart b/test/sub_process_test.dart index aaf7984..bfa2450 100644 --- a/test/sub_process_test.dart +++ b/test/sub_process_test.dart @@ -58,11 +58,12 @@ void main() { var script = Script("non-existent-executable"); expect(script.exitCode, completion(equals(257))); expect( - script.stderr.lines, - emitsInOrder([ - "Error in non-existent-executable:", - "ProcessException: No such file or directory" - ])); + script.stderr.lines, + emitsInOrder([ + "Error in non-existent-executable:", + "ProcessException: No such file or directory", + ]), + ); }); group("stdin", () { @@ -90,45 +91,57 @@ void main() { // [Stream.listen] until the file is actually open. test("that waits to listen", () async { await (mainScript("print('hello!');") > - FakeStreamConsumer(expectAsync1((stream) async { - await pumpEventQueue(); - expect(stream.lines, emits("hello!")); - }))); + FakeStreamConsumer( + expectAsync1((stream) async { + await pumpEventQueue(); + expect(stream.lines, emits("hello!")); + }), + )); }); }); group("subprocess environment", () { test("defaults to the parent environment", () { - expect(_getSubprocessEnvironment(), - completion(equals(Platform.environment))); + expect( + _getSubprocessEnvironment(), + completion(equals(Platform.environment)), + ); }); test("includes modifications to env", () { var varName = uid(); env[varName] = "value"; - expect(_getSubprocessEnvironment(), - completion(containsPair(varName, "value"))); + expect( + _getSubprocessEnvironment(), + completion(containsPair(varName, "value")), + ); }); test("includes scoped modifications to env", () { var varName = uid(); withEnv(() { - expect(_getSubprocessEnvironment(), - completion(containsPair(varName, "value"))); + expect( + _getSubprocessEnvironment(), + completion(containsPair(varName, "value")), + ); }, {varName: "value"}); }); test("includes values from the environment parameter", () { var varName = uid(); - expect(_getSubprocessEnvironment(environment: {varName: "value"}), - completion(containsPair(varName, "value"))); + expect( + _getSubprocessEnvironment(environment: {varName: "value"}), + completion(containsPair(varName, "value")), + ); }); test("the environment parameter overrides env", () { var varName = uid(); env[varName] = "outer value"; - expect(_getSubprocessEnvironment(environment: {varName: "inner value"}), - completion(containsPair(varName, "inner value"))); + expect( + _getSubprocessEnvironment(environment: {varName: "inner value"}), + completion(containsPair(varName, "inner value")), + ); }); group("with includeParentEnvironment: false", () { @@ -139,17 +152,21 @@ void main() { test("ignores env", () { var varName = uid(); env[varName] = "value"; - expect(_getSubprocessEnvironment(includeParentEnvironment: false), - completion(isNot(contains(varName)))); + expect( + _getSubprocessEnvironment(includeParentEnvironment: false), + completion(isNot(contains(varName))), + ); }); test("uses the environment parameter", () { var varName = uid(); expect( - _getSubprocessEnvironment( - environment: {varName: "value"}, - includeParentEnvironment: false), - completion(containsPair(varName, "value"))); + _getSubprocessEnvironment( + environment: {varName: "value"}, + includeParentEnvironment: false, + ), + completion(containsPair(varName, "value")), + ); }); }); }); @@ -157,47 +174,63 @@ void main() { group("output", () { test("returns the script's output without a trailing newline", () { expect( - mainScript("print('hello!');").output, completion(equals("hello!"))); + mainScript("print('hello!');").output, + completion(equals("hello!")), + ); }); test("completes with a ScriptException if the script fails", () { - expect(mainScript("print('hello!'); exitCode = 12;").output, - throwsScriptException(12)); + expect( + mainScript("print('hello!'); exitCode = 12;").output, + throwsScriptException(12), + ); }); }); group("outputBytes", () { test("returns the script's output as bytes", () { - expect(mainScript("print('hello!');").outputBytes, - completion(equals(utf8.encode("hello!\n")))); + expect( + mainScript("print('hello!');").outputBytes, + completion(equals(utf8.encode("hello!\n"))), + ); }); test("completes with a ScriptException if the script fails", () { - expect(mainScript("print('hello!'); exitCode = 12;").outputBytes, - throwsScriptException(12)); + expect( + mainScript("print('hello!'); exitCode = 12;").outputBytes, + throwsScriptException(12), + ); }); }); group("lines", () { test("returns the script's stdout lines", () { - expect(mainScript(r"print('hello\nthere!');").lines, - emitsInOrder(["hello", "there!", emitsDone])); + expect( + mainScript(r"print('hello\nthere!');").lines, + emitsInOrder(["hello", "there!", emitsDone]), + ); }); test("emits a ScriptException if the script fails", () { - expect(mainScript("print('hello!'); exitCode = 12;").lines, - emitsThrough(emitsError(isScriptException(12)))); + expect( + mainScript("print('hello!'); exitCode = 12;").lines, + emitsThrough(emitsError(isScriptException(12))), + ); }); }); } /// Defines tests for either stdout or for stderr. void stdoutOrStderr( - String name, Stream> Function(Script script) stream) { + String name, + Stream> Function(Script script) stream, +) { group(name, () { test("forwards $name from the subprocess and closes", () { - expect(stream(mainScript("$name.writeln('Hello!');")).lines, - emitsInOrder(["Hello!", emitsDone])); + expect( + stream(mainScript("$name.writeln('Hello!');")).lines, + emitsInOrder(["Hello!", emitsDone]), + ); }); test("closes after emitting nothing", () { @@ -217,8 +250,10 @@ void stdoutOrStderr( test("emits non-text values", () { // Try emitting null bytes and invalid UTF8 sequences to make sure // nothing's forcing this to be interpreted as text. - expect(stream(mainScript("$name.add([0, 0, 0xC3, 0x28]);")), - emits([0, 0, 0xC3, 0x28])); + expect( + stream(mainScript("$name.add([0, 0, 0xC3, 0x28]);")), + emits([0, 0, 0xC3, 0x28]), + ); }); test("can't be listened after a macrotask has elapsed", () async { @@ -228,21 +263,23 @@ void stdoutOrStderr( // We can't use expect(..., throwsStateError) here bceause of // dart-lang/sdk#45815. - runZonedGuarded(() => stream(script).listen(null), - expectAsync2((error, stackTrace) => expect(error, isStateError))); + runZonedGuarded( + () => stream(script).listen(null), + expectAsync2((error, stackTrace) => expect(error, isStateError)), + ); }); }); } /// Runs a Dart subprocess and returns the value of `Process.environment` in /// that subprocess. -Future> _getSubprocessEnvironment( - {Map? environment, - bool includeParentEnvironment = true}) async => - (json.decode(await mainScript( - "stdout.writeln(json.encode(Platform.environment));", - environment: environment, - includeParentEnvironment: includeParentEnvironment) - .stdout - .text) as Map) - .cast(); +Future> _getSubprocessEnvironment({ + Map? environment, + bool includeParentEnvironment = true, +}) async => (json.decode( + await mainScript( + "stdout.writeln(json.encode(Platform.environment));", + environment: environment, + includeParentEnvironment: includeParentEnvironment, + ).stdout.text, +) as Map).cast(); diff --git a/test/temp_test.dart b/test/temp_test.dart index 9d61b64..c322b56 100644 --- a/test/temp_test.dart +++ b/test/temp_test.dart @@ -29,8 +29,10 @@ void main() { test("passes a path that doesn't exist", () { withTempPath((path) { - expect(FileSystemEntity.typeSync(path), - equals(FileSystemEntityType.notFound)); + expect( + FileSystemEntity.typeSync(path), + equals(FileSystemEntityType.notFound), + ); }); }); @@ -47,8 +49,10 @@ void main() { }); test("adds the prefix to the path", () { - withTempPath((path) => expect(p.basename(path), startsWith("foo-")), - prefix: "foo-"); + withTempPath( + (path) => expect(p.basename(path), startsWith("foo-")), + prefix: "foo-", + ); }); test("adds the suffix to the path", () { @@ -56,13 +60,16 @@ void main() { }); test("puts the path in Directory.systemTemp by default", () { - withTempPath((path) => - expect(p.isWithin(Directory.systemTemp.path, path), isTrue)); + withTempPath( + (path) => expect(p.isWithin(Directory.systemTemp.path, path), isTrue), + ); }); test("puts the path in parent", () { - withTempPath((path) => expect(p.isWithin(d.sandbox, path), isTrue), - parent: d.sandbox); + withTempPath( + (path) => expect(p.isWithin(d.sandbox, path), isTrue), + parent: d.sandbox, + ); }); group("returns the callback's return value", () { @@ -84,8 +91,10 @@ void main() { File(path).writeAsStringSync("hello!"); }); - expect(FileSystemEntity.typeSync(path), - equals(FileSystemEntityType.notFound)); + expect( + FileSystemEntity.typeSync(path), + equals(FileSystemEntityType.notFound), + ); }); test("if the callback throws", () { @@ -97,8 +106,10 @@ void main() { }); }, throwsA("oh no")); - expect(FileSystemEntity.typeSync(path), - equals(FileSystemEntityType.notFound)); + expect( + FileSystemEntity.typeSync(path), + equals(FileSystemEntityType.notFound), + ); }); }); @@ -115,8 +126,10 @@ void main() { completer.complete(); await future; - expect(FileSystemEntity.typeSync(path), - equals(FileSystemEntityType.notFound)); + expect( + FileSystemEntity.typeSync(path), + equals(FileSystemEntityType.notFound), + ); }); test("if the callback throws", () async { @@ -131,8 +144,10 @@ void main() { completer.completeError("oh no"); await expectLater(future, throwsA("oh no")); - expect(FileSystemEntity.typeSync(path), - equals(FileSystemEntityType.notFound)); + expect( + FileSystemEntity.typeSync(path), + equals(FileSystemEntityType.notFound), + ); }); }); }); @@ -145,8 +160,10 @@ void main() { test("passes a path that doesn't exist", () async { await withTempPathAsync((path) { - expect(FileSystemEntity.typeSync(path), - equals(FileSystemEntityType.notFound)); + expect( + FileSystemEntity.typeSync(path), + equals(FileSystemEntityType.notFound), + ); }); }); @@ -164,29 +181,36 @@ void main() { test("adds the prefix to the path", () async { await withTempPathAsync( - (path) => expect(p.basename(path), startsWith("foo-")), - prefix: "foo-"); + (path) => expect(p.basename(path), startsWith("foo-")), + prefix: "foo-", + ); }); test("adds the suffix to the path", () async { - await withTempPathAsync((path) => expect(path, endsWith(".txt")), - suffix: ".txt"); + await withTempPathAsync( + (path) => expect(path, endsWith(".txt")), + suffix: ".txt", + ); }); test("puts the path in Directory.systemTemp by default", () async { - await withTempPathAsync((path) => - expect(p.isWithin(Directory.systemTemp.path, path), isTrue)); + await withTempPathAsync( + (path) => expect(p.isWithin(Directory.systemTemp.path, path), isTrue), + ); }); test("puts the path in parent", () async { await withTempPathAsync( - (path) => expect(p.isWithin(d.sandbox, path), isTrue), - parent: d.sandbox); + (path) => expect(p.isWithin(d.sandbox, path), isTrue), + parent: d.sandbox, + ); }); test("returns the callback's return value", () { expect( - withTempPathAsync((_) => Future.value(123)), completion(equals(123))); + withTempPathAsync((_) => Future.value(123)), + completion(equals(123)), + ); }); group("deletes the path afterwards", () { @@ -206,8 +230,10 @@ void main() { callbackFinishedCompleter.complete(); await future; - expect(FileSystemEntity.typeSync(path), - equals(FileSystemEntityType.notFound)); + expect( + FileSystemEntity.typeSync(path), + equals(FileSystemEntityType.notFound), + ); }); test("if the callback throws", () async { @@ -225,8 +251,10 @@ void main() { callbackFinishedCompleter.completeError("oh no"); await expectLater(future, throwsA("oh no")); - expect(FileSystemEntity.typeSync(path), - equals(FileSystemEntityType.notFound)); + expect( + FileSystemEntity.typeSync(path), + equals(FileSystemEntityType.notFound), + ); }); }); }); @@ -255,8 +283,10 @@ void main() { }); test("adds the prefix to the directory", () { - withTempDir((dir) => expect(p.basename(dir), startsWith("foo-")), - prefix: "foo-"); + withTempDir( + (dir) => expect(p.basename(dir), startsWith("foo-")), + prefix: "foo-", + ); }); test("adds the suffix to the directory", () { @@ -265,12 +295,15 @@ void main() { test("puts the directory in Directory.systemTemp by default", () { withTempDir( - (dir) => expect(p.isWithin(Directory.systemTemp.path, dir), isTrue)); + (dir) => expect(p.isWithin(Directory.systemTemp.path, dir), isTrue), + ); }); test("puts the directory in parent", () { - withTempDir((dir) => expect(p.isWithin(d.sandbox, dir), isTrue), - parent: d.sandbox); + withTempDir( + (dir) => expect(p.isWithin(d.sandbox, dir), isTrue), + parent: d.sandbox, + ); }); group("returns the callback's return value", () { @@ -369,29 +402,36 @@ void main() { test("adds the prefix to the directory", () async { await withTempDirAsync( - (dir) => expect(p.basename(dir), startsWith("foo-")), - prefix: "foo-"); + (dir) => expect(p.basename(dir), startsWith("foo-")), + prefix: "foo-", + ); }); test("adds the suffix to the directory", () async { - await withTempDirAsync((dir) => expect(dir, endsWith(".txt")), - suffix: ".txt"); + await withTempDirAsync( + (dir) => expect(dir, endsWith(".txt")), + suffix: ".txt", + ); }); test("puts the directory in Directory.systemTemp by default", () async { await withTempDirAsync( - (dir) => expect(p.isWithin(Directory.systemTemp.path, dir), isTrue)); + (dir) => expect(p.isWithin(Directory.systemTemp.path, dir), isTrue), + ); }); test("puts the directory in parent", () async { await withTempDirAsync( - (dir) => expect(p.isWithin(d.sandbox, dir), isTrue), - parent: d.sandbox); + (dir) => expect(p.isWithin(d.sandbox, dir), isTrue), + parent: d.sandbox, + ); }); test("returns the callback's return value", () { expect( - withTempDirAsync((_) => Future.value(123)), completion(equals(123))); + withTempDirAsync((_) => Future.value(123)), + completion(equals(123)), + ); }); group("deletes the directory afterwards", () { diff --git a/test/transform_test.dart b/test/transform_test.dart index c8c13c9..c5914e3 100644 --- a/test/transform_test.dart +++ b/test/transform_test.dart @@ -25,16 +25,18 @@ import 'util.dart'; void main() { group("withSpans", () { test("emits each line", () async { - var tuples = - await Stream.fromIterable(["foo", "bar", "baz"]).withSpans().toList(); + var tuples = await Stream.fromIterable(["foo", "bar", "baz"]) + .withSpans() + .toList(); expect(tuples[0].item1, equals("foo")); expect(tuples[1].item1, equals("bar")); expect(tuples[2].item1, equals("baz")); }); test("emits spans that cover each line", () async { - var tuples = - await Stream.fromIterable(["foo", "bar", "baz"]).withSpans().toList(); + var tuples = await Stream.fromIterable(["foo", "bar", "baz"]) + .withSpans() + .toList(); expect(tuples[0].item2.start.offset, equals(0)); expect(tuples[0].item2.start.line, equals(0)); @@ -65,8 +67,9 @@ void main() { }); test("URLs default to null", () async { - var tuples = - await Stream.fromIterable(["foo", "bar", "baz"]).withSpans().toList(); + var tuples = await Stream.fromIterable(["foo", "bar", "baz"]) + .withSpans() + .toList(); expect(tuples[0].item2.sourceUrl, isNull); expect(tuples[0].item2.start.sourceUrl, isNull); @@ -122,51 +125,61 @@ void main() { test("sourcePath and sourceUrl can't both be set", () async { expect( - () => Stream.fromIterable(["foo", "bar", "baz"]) - .withSpans(sourceUrl: Uri.parse("foo"), sourcePath: "foo"), - throwsArgumentError); + () => + Stream.fromIterable(["foo", "bar", "baz"]) + .withSpans(sourceUrl: Uri.parse("foo"), sourcePath: "foo"), + throwsArgumentError, + ); }); }); group("grep", () { test("returns matching lines", () { - expect(Stream.fromIterable(["foo", "bar", "baz"]).grep(r"^b"), - emitsInOrder(["bar", "baz", emitsDone])); + expect( + Stream.fromIterable(["foo", "bar", "baz"]).grep(r"^b"), + emitsInOrder(["bar", "baz", emitsDone]), + ); }); test("returns non-matching lines with exclude: true", () { expect( - Stream.fromIterable(["foo", "bar", "baz"]).grep(r"^b", exclude: true), - emitsInOrder(["foo", emitsDone])); + Stream.fromIterable(["foo", "bar", "baz"]).grep(r"^b", exclude: true), + emitsInOrder(["foo", emitsDone]), + ); }); group("with onlyMatching: true", () { test("throws an error if exclude is also true", () { expect( - () => Stream.fromIterable(["foo", "bar", "baz"]) - .grep(r"^b", onlyMatching: true, exclude: true), - throwsArgumentError); + () => + Stream.fromIterable(["foo", "bar", "baz"]) + .grep(r"^b", onlyMatching: true, exclude: true), + throwsArgumentError, + ); }); test("prints the matching parts of lines that match", () { expect( - Stream.fromIterable(["foo", "bar", "baz"]) - .grep(r"a.", onlyMatching: true), - emitsInOrder(["ar", "az"])); + Stream.fromIterable(["foo", "bar", "baz"]) + .grep(r"a.", onlyMatching: true), + emitsInOrder(["ar", "az"]), + ); }); test("prints multiple matching parts per line", () { expect( - Stream.fromIterable(["foo bar", "baz bang bop"]) - .grep(r"[a-z]{3}", onlyMatching: true), - emitsInOrder(["foo", "bar", "baz", "ban", "bop"])); + Stream.fromIterable(["foo bar", "baz bang bop"]) + .grep(r"[a-z]{3}", onlyMatching: true), + emitsInOrder(["foo", "bar", "baz", "ban", "bop"]), + ); }); test("doesn't print empty matches", () { expect( - Stream.fromIterable(["foo", "bar", "baz"]) - .grep(r"q?", onlyMatching: true), - emitsDone); + Stream.fromIterable(["foo", "bar", "baz"]) + .grep(r"q?", onlyMatching: true), + emitsDone, + ); }); }); @@ -247,76 +260,92 @@ void main() { group("replaceMapped", () { test("replaces the first match", () { expect( - Stream.fromIterable(["foo", "bar baz", "boz bop"]) - .replaceMapped(r"b(.)", (match) => "${match[1]!}q"), - emitsInOrder(["foo", "aqr baz", "oqz bop", emitsDone])); + Stream.fromIterable(["foo", "bar baz", "boz bop"]) + .replaceMapped(r"b(.)", (match) => "${match[1]!}q"), + emitsInOrder(["foo", "aqr baz", "oqz bop", emitsDone]), + ); }); test("replaces all matches with all: true", () { expect( - Stream.fromIterable(["foo", "bar baz", "boz bop"]) - .replaceMapped(r"b(.)", (match) => "${match[1]!}q", all: true), - emitsInOrder(["foo", "aqr aqz", "oqz oqp", emitsDone])); + Stream.fromIterable(["foo", "bar baz", "boz bop"]) + .replaceMapped(r"b(.)", (match) => "${match[1]!}q", all: true), + emitsInOrder(["foo", "aqr aqz", "oqz oqp", emitsDone]), + ); }); }); group("replace", () { test("replaces the first match", () { expect( - Stream.fromIterable(["foo", "bar baz", "boz bop"]) - .replace(r"b(.)", r"\1q"), - emitsInOrder(["foo", "aqr baz", "oqz bop", emitsDone])); + Stream.fromIterable(["foo", "bar baz", "boz bop"]) + .replace(r"b(.)", r"\1q"), + emitsInOrder(["foo", "aqr baz", "oqz bop", emitsDone]), + ); }); test("replaces all matches with all: true", () { expect( - Stream.fromIterable(["foo", "bar baz", "boz bop"]) - .replace(r"b(.)", r"\1q", all: true), - emitsInOrder(["foo", "aqr aqz", "oqz oqp", emitsDone])); + Stream.fromIterable(["foo", "bar baz", "boz bop"]) + .replace(r"b(.)", r"\1q", all: true), + emitsInOrder(["foo", "aqr aqz", "oqz oqp", emitsDone]), + ); }); test("converts double backslash to single", () { expect( - Stream.fromIterable(["foo", "bar", "boz"]).replace(r"b(.)", r"\\q"), - emitsInOrder(["foo", r"\qr", r"\qz", emitsDone])); + Stream.fromIterable(["foo", "bar", "boz"]).replace(r"b(.)", r"\\q"), + emitsInOrder(["foo", r"\qr", r"\qz", emitsDone]), + ); }); test("ignores other backslash", () { - expect(Stream.fromIterable(["foo", "bar", "boz"]).replace(r"b(.)", r"\q"), - emitsInOrder(["foo", "qr", "qz", emitsDone])); + expect( + Stream.fromIterable(["foo", "bar", "boz"]).replace(r"b(.)", r"\q"), + emitsInOrder(["foo", "qr", "qz", emitsDone]), + ); }); test("allows trailing backslash", () { - expect(Stream.fromIterable(["foo", "bar", "boz"]).replace(r"b(.)", "q\\"), - emitsInOrder(["foo", "qr", "qz", emitsDone])); + expect( + Stream.fromIterable(["foo", "bar", "boz"]).replace(r"b(.)", "q\\"), + emitsInOrder(["foo", "qr", "qz", emitsDone]), + ); }); test("allows references to unmatched groups", () { expect( - Stream.fromIterable(["foo", "bar", "boz"]) - .replace(r"(zink)|(bar)", r"\1"), - emitsInOrder(["foo", "", "boz", emitsDone])); + Stream.fromIterable(["foo", "bar", "boz"]) + .replace(r"(zink)|(bar)", r"\1"), + emitsInOrder(["foo", "", "boz", emitsDone]), + ); }); test("forbids references to non-existent groups", () { expect( - Stream.fromIterable(["foo", "bar", "boz"]) - .replace(r"(zink)|(bar)", r"\3"), - emitsInOrder(["foo", emitsError(isFormatException)])); + Stream.fromIterable(["foo", "bar", "boz"]) + .replace(r"(zink)|(bar)", r"\3"), + emitsInOrder(["foo", emitsError(isFormatException)]), + ); }); }); group("teeToStderr", () { test("passes inputs through as-is", () { silenceStderr(() { - expect(Stream.fromIterable(["foo", "bar", "baz"]).teeToStderr, - emitsInOrder(["foo", "bar", "baz"])); + expect( + Stream.fromIterable(["foo", "bar", "baz"]).teeToStderr, + emitsInOrder(["foo", "bar", "baz"]), + ); }); }); test("emits inputs to sdterr", () { - var script = Script.capture((_) => - Stream.fromIterable(["foo", "bar", "baz"]).teeToStderr.drain()); + var script = Script.capture( + (_) => + Stream.fromIterable(["foo", "bar", "baz"]).teeToStderr + .drain(), + ); expect(script.stderr.lines, emitsInOrder(["foo", "bar", "baz"])); }); }); @@ -325,90 +354,101 @@ void main() { group("from a stream", () { test("passes stream entries as arguments", () { var script = Stream.fromIterable(["foo", "bar\nbaz"]).xargs( - expectAsync1((args) => expect(args, equals(["foo", "bar\nbaz"])))); + expectAsync1((args) => expect(args, equals(["foo", "bar\nbaz"]))), + ); expect(script.done, completes); }); test("only passes maxArgs per callback", () { var count = 0; var script = Stream.fromIterable(["1", "2", "3", "4", "5"]).xargs( - expectAsync1((args) { - if (count == 0) { - expect(args, equals(["1", "2", "3"])); - } else { - expect(args, equals(["4", "5"])); - } - count++; - }, count: 2), - maxArgs: 3); + expectAsync1((args) { + if (count == 0) { + expect(args, equals(["1", "2", "3"])); + } else { + expect(args, equals(["4", "5"])); + } + count++; + }, count: 2), + maxArgs: 3, + ); expect(script.done, completes); }); - test("doesn't run a future callback until a previous one returns", - () async { - var count = 0; - var completer = Completer(); - var script = Stream.fromIterable(["1", "2"]).xargs( + test( + "doesn't run a future callback until a previous one returns", + () async { + var count = 0; + var completer = Completer(); + var script = Stream.fromIterable(["1", "2"]).xargs( expectAsync1((args) { count++; return completer.future; }, count: 2), - maxArgs: 1); - expect(script.done, completes); + maxArgs: 1, + ); + expect(script.done, completes); - await pumpEventQueue(); - expect(count, equals(1)); + await pumpEventQueue(); + expect(count, equals(1)); - completer.complete(); - await pumpEventQueue(); - expect(count, equals(2)); - }); + completer.complete(); + await pumpEventQueue(); + expect(count, equals(2)); + }, + ); - test("the script doesn't complete until the callbacks are finished", - () async { - var count = 0; - var completers = List.generate(5, (_) => Completer()); - var script = Stream.fromIterable(["1", "2", "3", "4", "5"]).xargs( + test( + "the script doesn't complete until the callbacks are finished", + () async { + var count = 0; + var completers = List.generate(5, (_) => Completer()); + var script = Stream.fromIterable(["1", "2", "3", "4", "5"]).xargs( expectAsync1((args) => completers[count++].future, count: 5), - maxArgs: 1); + maxArgs: 1, + ); - var done = false; - script.done.then((_) { - done = true; - }); + var done = false; + script.done.then((_) { + done = true; + }); - for (var completer in completers) { - await pumpEventQueue(); - expect(done, isFalse); - completer.complete(); - } + for (var completer in completers) { + await pumpEventQueue(); + expect(done, isFalse); + completer.complete(); + } - await pumpEventQueue(); - expect(done, isTrue); - }); + await pumpEventQueue(); + expect(done, isTrue); + }, + ); - test("the script doesn't complete until the callbacks are finished", - () async { - var count = 0; - var completers = List.generate(5, (_) => Completer()); - var script = Stream.fromIterable(["1", "2", "3", "4", "5"]).xargs( + test( + "the script doesn't complete until the callbacks are finished", + () async { + var count = 0; + var completers = List.generate(5, (_) => Completer()); + var script = Stream.fromIterable(["1", "2", "3", "4", "5"]).xargs( expectAsync1((args) => completers[count++].future, count: 5), - maxArgs: 1); + maxArgs: 1, + ); - var done = false; - script.done.then((_) { - done = true; - }); + var done = false; + script.done.then((_) { + done = true; + }); - for (var completer in completers) { - await pumpEventQueue(); - expect(done, isFalse); - completer.complete(); - } + for (var completer in completers) { + await pumpEventQueue(); + expect(done, isFalse); + completer.complete(); + } - await pumpEventQueue(); - expect(done, isTrue); - }); + await pumpEventQueue(); + expect(done, isTrue); + }, + ); test("the script fails if the stream throws", () async { var controller = StreamController(); @@ -419,8 +459,7 @@ void main() { expect(script.done, throwsScriptException(257)); }); - test( - "the script fails preserving the exit code " + test("the script fails preserving the exit code " "if the stream throws a ScriptException", () async { var controller = StreamController(); var script = controller.stream.xargs(print); @@ -443,12 +482,16 @@ void main() { group("as a script", () { test("converts each line of stdin into an argument", () { - var script = Script.capture((_) { + var script = + Script.capture((_) { print("foo bar"); print("baz\nbang"); }) | - xargs(expectAsync1( - (args) => expect(args, equals(["foo bar", "baz", "bang"])))); + xargs( + expectAsync1( + (args) => expect(args, equals(["foo bar", "baz", "bang"])), + ), + ); expect(script.done, completes); }); }); diff --git a/test/util.dart b/test/util.dart index baa31a0..0810aa9 100644 --- a/test/util.dart +++ b/test/util.dart @@ -27,32 +27,38 @@ var _nextId = 0; String uid() => "cli_script_test_${_nextId++}"; /// Runs the Dart code [code] as a subprocess [Script]. -Script dartScript(String code, - {Iterable? args, - String? name, - String? workingDirectory, - Map? environment, - bool includeParentEnvironment = true}) { +Script dartScript( + String code, { + Iterable? args, + String? name, + String? workingDirectory, + Map? environment, + bool includeParentEnvironment = true, +}) { var script = d.path("${uid()}.dart"); File(script).writeAsStringSync(code); - return Script(arg(Platform.executable), - args: [...Platform.executableArguments, script, ...?args], - name: name, - workingDirectory: workingDirectory, - environment: environment, - includeParentEnvironment: includeParentEnvironment); + return Script( + arg(Platform.executable), + args: [...Platform.executableArguments, script, ...?args], + name: name, + workingDirectory: workingDirectory, + environment: environment, + includeParentEnvironment: includeParentEnvironment, + ); } /// A shorthand for [dartScript] that runs [code] in the body of an async /// `main()` method with access to `dart:async`, `dart:convert`, and `dart:io`. -Script mainScript(String code, - {Iterable? args, - String? name, - String? workingDirectory, - Map? environment, - bool includeParentEnvironment = true}) => - dartScript(""" +Script mainScript( + String code, { + Iterable? args, + String? name, + String? workingDirectory, + Map? environment, + bool includeParentEnvironment = true, +}) => dartScript( + """ import 'dart:async'; import 'dart:convert'; import 'dart:io'; @@ -61,19 +67,20 @@ Script mainScript(String code, $code } """, - args: args, - name: name, - workingDirectory: workingDirectory, - environment: environment, - includeParentEnvironment: includeParentEnvironment); + args: args, + name: name, + workingDirectory: workingDirectory, + environment: environment, + includeParentEnvironment: includeParentEnvironment, +); /// Returns a matcher that verifies that the object is a [ScriptException] with /// the given exit code. Matcher isScriptException(int exitCode) => predicate((error) { - expect(error, isA()); - expect((error as ScriptException).exitCode, equals(exitCode)); - return true; - }); + expect(error, isA()); + expect((error as ScriptException).exitCode, equals(exitCode)); + return true; +}); /// Returns a matcher that verifies that the object throws a [ScriptException] /// with the given exit code. diff --git a/test/util/delayed_completer_test.dart b/test/util/delayed_completer_test.dart index e354f7b..824f5e4 100644 --- a/test/util/delayed_completer_test.dart +++ b/test/util/delayed_completer_test.dart @@ -25,11 +25,13 @@ void main() { Result? result; setUp(() { result = null; - completer.future.then((value) { - result = Result.value(value); - }).onError((error, stackTrace) { - result = Result.error(error!, stackTrace); - }); + completer.future + .then((value) { + result = Result.value(value); + }) + .onError((error, stackTrace) { + result = Result.error(error!, stackTrace); + }); }); test("completes with a value", () async { diff --git a/test/util/entangled_controllers_test.dart b/test/util/entangled_controllers_test.dart index 4f4daf2..e4171c1 100644 --- a/test/util/entangled_controllers_test.dart +++ b/test/util/entangled_controllers_test.dart @@ -31,32 +31,44 @@ void main() { group("with no events buffered", () { group("both streams emit no events", () { test("when listened in the same microtask", () { - controller1.stream.listen(expectAsync1((_) {}, count: 0), - onError: expectAsync2((_, __) {}, count: 0), - onDone: expectAsync0(() {}, count: 0)); - controller2.stream.listen(expectAsync1((_) {}, count: 0), - onError: expectAsync2((_, __) {}, count: 0), - onDone: expectAsync0(() {}, count: 0)); + controller1.stream.listen( + expectAsync1((_) {}, count: 0), + onError: expectAsync2((_, __) {}, count: 0), + onDone: expectAsync0(() {}, count: 0), + ); + controller2.stream.listen( + expectAsync1((_) {}, count: 0), + onError: expectAsync2((_, __) {}, count: 0), + onDone: expectAsync0(() {}, count: 0), + ); }); test("when listened in separate microtasks", () async { - controller1.stream.listen(expectAsync1((_) {}, count: 0), - onError: expectAsync2((_, __) {}, count: 0), - onDone: expectAsync0(() {}, count: 0)); + controller1.stream.listen( + expectAsync1((_) {}, count: 0), + onError: expectAsync2((_, __) {}, count: 0), + onDone: expectAsync0(() {}, count: 0), + ); await Future.value(); - controller2.stream.listen(expectAsync1((_) {}, count: 0), - onError: expectAsync2((_, __) {}, count: 0), - onDone: expectAsync0(() {}, count: 0)); + controller2.stream.listen( + expectAsync1((_) {}, count: 0), + onError: expectAsync2((_, __) {}, count: 0), + onDone: expectAsync0(() {}, count: 0), + ); }); test("when listened in distant microtasks", () async { - controller1.stream.listen(expectAsync1((_) {}, count: 0), - onError: expectAsync2((_, __) {}, count: 0), - onDone: expectAsync0(() {}, count: 0)); + controller1.stream.listen( + expectAsync1((_) {}, count: 0), + onError: expectAsync2((_, __) {}, count: 0), + onDone: expectAsync0(() {}, count: 0), + ); await pumpEventQueue(); - controller2.stream.listen(expectAsync1((_) {}, count: 0), - onError: expectAsync2((_, __) {}, count: 0), - onDone: expectAsync0(() {}, count: 0)); + controller2.stream.listen( + expectAsync1((_) {}, count: 0), + onError: expectAsync2((_, __) {}, count: 0), + onDone: expectAsync0(() {}, count: 0), + ); }); }); @@ -141,26 +153,28 @@ void main() { } }); - test("events added during flushing are also flushed as microtasks", - () async { - var events = _collectEventsFromBoth(controller1, controller2); - - for (var i = 0; i < 4; i++) { - controller1.add("extra $i"); - await Future.value(); - } - - for (var i = 4; i < 13; i++) { - expect(events.events, hasLength(i)); - await Future.value(); - } - - expect(events.events, hasLength(12)); - expect(events.value(8), equals("extra 0")); - expect(events.value(9), equals("extra 1")); - expect(events.value(10), equals("extra 2")); - expect(events.value(11), equals("extra 3")); - }); + test( + "events added during flushing are also flushed as microtasks", + () async { + var events = _collectEventsFromBoth(controller1, controller2); + + for (var i = 0; i < 4; i++) { + controller1.add("extra $i"); + await Future.value(); + } + + for (var i = 4; i < 13; i++) { + expect(events.events, hasLength(i)); + await Future.value(); + } + + expect(events.events, hasLength(12)); + expect(events.value(8), equals("extra 0")); + expect(events.value(9), equals("extra 1")); + expect(events.value(10), equals("extra 2")); + expect(events.value(11), equals("extra 3")); + }, + ); test("both streams emit further events synchronously", () async { var events1 = _collectEvents(controller1); @@ -205,16 +219,17 @@ void main() { }); test( - "new events for the second controller are buffered until it's listened", - () async { - _collectEvents(controller1); - controller2.add("2:5"); - await Future.value(); - var events2 = _collectEvents(controller2); + "new events for the second controller are buffered until it's listened", + () async { + _collectEvents(controller1); + controller2.add("2:5"); + await Future.value(); + var events2 = _collectEvents(controller2); - await pumpEventQueue(); - expect(events2.value(4), equals("2:5")); - }); + await pumpEventQueue(); + expect(events2.value(4), equals("2:5")); + }, + ); }); group("when one controller is listened in a distant microtask", () { @@ -237,16 +252,17 @@ void main() { }); test( - "new events for the second controller are buffered until it's listened", - () async { - _collectEvents(controller1); - controller2.add("2:5"); - await pumpEventQueue(); - var events2 = _collectEvents(controller2); - - await pumpEventQueue(); - expect(events2.value(4), equals("2:5")); - }); + "new events for the second controller are buffered until it's listened", + () async { + _collectEvents(controller1); + controller2.add("2:5"); + await pumpEventQueue(); + var events2 = _collectEvents(controller2); + + await pumpEventQueue(); + expect(events2.value(4), equals("2:5")); + }, + ); }); }); } @@ -273,8 +289,10 @@ _CollectedEvents _collectEvents(StreamController controller) { /// Returns an object that's updated with events emitted by both [controller1]'s /// and [controller2]'s streams. -_CollectedEvents _collectEventsFromBoth(StreamController controller1, - StreamController controller2) { +_CollectedEvents _collectEventsFromBoth( + StreamController controller1, + StreamController controller2, +) { var events = _CollectedEvents(); _collectEventsInto(controller1, events); _collectEventsInto(controller2, events); @@ -283,9 +301,13 @@ _CollectedEvents _collectEventsFromBoth(StreamController controller1, /// Listens to [controller]'s stream and adds all events it emits to [events]. void _collectEventsInto( - StreamController controller, _CollectedEvents events) { - controller.stream.listen((value) => events.events.add(Result.value(value)), - onError: (Object error, StackTrace stackTrace) => - events.events.add(Result.error(error, stackTrace)), - onDone: () => events.done = true); + StreamController controller, + _CollectedEvents events, +) { + controller.stream.listen( + (value) => events.events.add(Result.value(value)), + onError: (Object error, StackTrace stackTrace) => + events.events.add(Result.error(error, stackTrace)), + onDone: () => events.done = true, + ); } From d9368adde3848c4c3aacb9bfd345837935978285 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Fri, 21 Aug 2026 15:10:58 -0700 Subject: [PATCH 2/4] Run automated fixes --- lib/cli_script.dart | 6 ++--- lib/src/buffered_script.dart | 4 ++-- lib/src/cli_arguments.dart | 12 +++++----- lib/src/exception.dart | 2 +- lib/src/extensions/line_and_span_stream.dart | 6 ++--- lib/src/extensions/line_stream.dart | 6 ++--- lib/src/script.dart | 24 ++++++++++---------- lib/src/stdio.dart | 4 ++-- lib/src/stdio_group.dart | 6 ++--- lib/src/util/delayed_completer.dart | 4 ++-- lib/src/util/entangled_controllers.dart | 4 ++-- lib/src/util/named_stream_transformer.dart | 6 ++--- lib/src/util/sink_base.dart | 2 +- test/fake_stream_consumer.dart | 2 +- test/util/entangled_controllers_test.dart | 12 +++++----- 15 files changed, 50 insertions(+), 50 deletions(-) diff --git a/lib/cli_script.dart b/lib/cli_script.dart index 1936d04..47bc745 100644 --- a/lib/cli_script.dart +++ b/lib/cli_script.dart @@ -243,7 +243,7 @@ Never fail(String message, {int exitCode = 1}) { /// at the same time as [exclude]. /// /// The [caseSensitive], [unicode], and [dotAll] flags are the same as for -/// [new RegExp]. +/// [RegExp.new]. StreamTransformer grep( String regexp, { bool exclude = false, @@ -279,7 +279,7 @@ StreamTransformer grep( /// followed by a number return the character immediately following them. /// /// The [caseSensitive], [unicode], and [dotAll] flags are the same as for -/// [new RegExp]. +/// [RegExp.new]. StreamTransformer replace( String regexp, String replacement, { @@ -306,7 +306,7 @@ StreamTransformer replace( /// replaces all matches in each line instead. /// /// The [caseSensitive], [unicode], and [dotAll] flags are the same as for -/// [new RegExp]. +/// [RegExp.new]. StreamTransformer replaceMapped( String regexp, String Function(Match match) replace, { diff --git a/lib/src/buffered_script.dart b/lib/src/buffered_script.dart index 4dadf4d..10ec132 100644 --- a/lib/src/buffered_script.dart +++ b/lib/src/buffered_script.dart @@ -97,7 +97,7 @@ class BufferedScript extends Script { /// callback allows capturing those signals so the callback may react /// appropriately. When no [onSignal] handler was set, calling [kill] will do /// nothing and return `false`. - factory BufferedScript.capture( + factory capture( FutureOr Function(Stream> stdin) callback, { String? name, bool Function(ProcessSignal signal)? onSignal, @@ -120,7 +120,7 @@ class BufferedScript extends Script { /// A helper constructor that allows [BufferedScript.capture] to pass in both /// [_stdoutBuffer] and [_stderrBuffer] from a single call to /// [createEntangledControllers]. - BufferedScript._(Script script, this._stdoutBuffer, this._stderrBuffer) + new _(Script script, this._stdoutBuffer, this._stderrBuffer) : _stdoutCompleter = _stdoutBuffer == null ? null : StreamCompleter>(), diff --git a/lib/src/cli_arguments.dart b/lib/src/cli_arguments.dart index ed23070..e00ead6 100644 --- a/lib/src/cli_arguments.dart +++ b/lib/src/cli_arguments.dart @@ -37,7 +37,7 @@ class CliArguments { /// handle its own glob expansion. /// /// Throws a [FormatException] if [argString] is malformed. - factory CliArguments.parse(String argString, {bool? glob}) { + factory parse(String argString, {bool? glob}) { glob ??= !Platform.isWindows; var scanner = StringScanner(argString); @@ -57,7 +57,7 @@ class CliArguments { return CliArguments._(executable, args); } - CliArguments._(this.executable, this._arguments); + new _(this.executable, this._arguments); /// Consumes zero or more spaces. static void _consumeSpaces(StringScanner scanner) { @@ -151,7 +151,7 @@ class _Argument { /// place of [_plain]. If it matches no files, [_plain] is used instead. final Glob? _glob; - _Argument(this._plain, this._glob); + new(this._plain, this._glob); /// Returns the files matched by this argument's [Glob] if it has one and if /// it matches at least one file, or the plain argument string otherwise. @@ -172,7 +172,7 @@ class _Argument { } /// Converts [argument] to a string and escapes it so it's parsed as a single -/// argument with no glob expansion by [new Script] and related functions. +/// argument with no glob expansion by [Script] and related functions. /// /// For example, `run("cp -r ${arg(source)} build/")`. String arg(Object argument) { @@ -203,8 +203,8 @@ String arg(Object argument) { } /// Converts all elements of [arguments] to strings and escapes them so they're -/// parsed as separate arguments with no glob expansion by [new Script] and -/// related functions. +/// parsed as separate arguments with no glob expansion by [Script] and related +/// functions. /// /// For example, `run("cp -r ${args(directories)} build/")`. String args(Iterable arguments) => arguments.map(arg).join(" "); diff --git a/lib/src/exception.dart b/lib/src/exception.dart index 5fb4514..26effcc 100644 --- a/lib/src/exception.dart +++ b/lib/src/exception.dart @@ -20,7 +20,7 @@ class ScriptException implements Exception { /// The exit code produced by the failing script. final int exitCode; - ScriptException(this.scriptName, this.exitCode) { + new(this.scriptName, this.exitCode) { if (exitCode == 0) { throw RangeError.value(exitCode, "exitCode", "May not be 0"); } diff --git a/lib/src/extensions/line_and_span_stream.dart b/lib/src/extensions/line_and_span_stream.dart index 0ac63ed..1d174a7 100644 --- a/lib/src/extensions/line_and_span_stream.dart +++ b/lib/src/extensions/line_and_span_stream.dart @@ -38,7 +38,7 @@ extension LineAndSpanStreamExtensions /// `true` at the same time as [exclude]. /// /// The [caseSensitive], [unicode], and [dotAll] flags are the same as for - /// [new RegExp]. + /// [RegExp.new]. Stream> grep( String regexp, { bool exclude = false, @@ -85,7 +85,7 @@ extension LineAndSpanStreamExtensions /// followed by a number return the character immediately following them. /// /// The [caseSensitive], [unicode], and [dotAll] flags are the same as for - /// [new RegExp]. + /// [RegExp.new]. Stream> replace( String regexp, String replacement, { @@ -108,7 +108,7 @@ extension LineAndSpanStreamExtensions /// replaces all matches in each line instead. /// /// The [caseSensitive], [unicode], and [dotAll] flags are the same as for - /// [new RegExp]. + /// [RegExp.new]. Stream> replaceMapped( String regexp, String Function(Match match) replace, { diff --git a/lib/src/extensions/line_stream.dart b/lib/src/extensions/line_stream.dart index 458dfc8..46a8162 100644 --- a/lib/src/extensions/line_stream.dart +++ b/lib/src/extensions/line_stream.dart @@ -99,7 +99,7 @@ extension LineStreamExtensions on Stream { /// `true` at the same time as [exclude]. /// /// The [caseSensitive], [unicode], and [dotAll] flags are the same as for - /// [new RegExp]. + /// [RegExp.new]. Stream grep( String regexp, { bool exclude = false, @@ -141,7 +141,7 @@ extension LineStreamExtensions on Stream { /// followed by a number return the character immediately following them. /// /// The [caseSensitive], [unicode], and [dotAll] flags are the same as for - /// [new RegExp]. + /// [RegExp.new]. Stream replace( String regexp, String replacement, { @@ -164,7 +164,7 @@ extension LineStreamExtensions on Stream { /// replaces all matches in each line instead. /// /// The [caseSensitive], [unicode], and [dotAll] flags are the same as for - /// [new RegExp]. + /// [RegExp.new]. Stream replaceMapped( String regexp, String Function(Match match) replace, { diff --git a/lib/src/script.dart b/lib/src/script.dart index 0b12307..33152f0 100644 --- a/lib/src/script.dart +++ b/lib/src/script.dart @@ -41,7 +41,7 @@ final scriptNameKey = #_captureName; /// [stderr] streams that ultimately produces an [exitCode] indicating success /// or failure. /// -/// This is usually a literal subprocess (created using [new Script]). However, +/// This is usually a literal subprocess (created using [Script.new]). However, /// it can also be a block of Dart code (created using [Script.capture]) or a /// user-defined custom script (created using [Script.fromComponents]). /// @@ -190,7 +190,7 @@ class Script { /// executable name. All other arguments are forwarded to [Process.start]. /// /// [the README]: https://github.com/google/dart_cli_script/blob/main/README.md#argument-parsing - factory Script( + factory( String executableAndArgs, { Iterable? args, String? name, @@ -298,7 +298,7 @@ class Script { /// callback allows capturing those signals so the callback may react /// appropriately. When no [onSignal] handler was set, calling [kill] will do /// nothing and return `false`. - factory Script.capture( + factory capture( FutureOr Function(Stream> stdin) callback, { String? name, bool Function(ProcessSignal signal)? onSignal, @@ -378,7 +378,7 @@ class Script { /// [Script.kill] returns `true` if the stream was interrupted and the script /// exits with [Script.exitCode] `143`, or `false` if the stream was already /// closed. - factory Script.fromByteTransformer( + factory fromByteTransformer( StreamTransformer, List> transformer, { String? name, }) { @@ -414,7 +414,7 @@ class Script { /// [Script.kill] returns `true` if the stream was interrupted and the script /// exits with [Script.exitCode] `143`, or `false` if the stream was already /// closed. - factory Script.fromLineTransformer( + factory fromLineTransformer( StreamTransformer transformer, { String? name, }) => Script.fromByteTransformer( @@ -430,7 +430,7 @@ class Script { /// /// This script passes each line of stdin to [mapper] and emits the result via /// stdout. - factory Script.mapLines( + factory mapLines( String Function(String line) mapper, { String? name, }) => Script.fromLineTransformer( @@ -460,7 +460,7 @@ class Script { /// /// See also [operator |], which provides a syntax for creating pipelines two /// scripts at a time. - factory Script.pipeline(Iterable scripts, {String? name}) { + factory pipeline(Iterable scripts, {String? name}) { _checkCapture(); var list = scripts.map(_toScript).toList(); @@ -528,7 +528,7 @@ class Script { /// callback allows capturing those signals so the callback may react /// appropriately. When no [onSignal] handler was set, calling [kill] will do /// nothing and return `false`. - Script.fromComponents( + new fromComponents( String name, FutureOr Function() callback, { bool Function(ProcessSignal signal)? onSignal, @@ -544,7 +544,7 @@ class Script { /// /// @nodoc @internal - Script.fromComponentsInternal( + new fromComponentsInternal( String name, FutureOr Function() callback, bool Function(ProcessSignal signal) signalHandler, { @@ -567,7 +567,7 @@ class Script { /// It would be much cleaner to just make [Script.fromComponentsInternal] a /// factory constructor, but then it and [Script.fromComponents] couldn't be /// invoked by subclasses. - Script._fromComponentsInternal( + new _fromComponentsInternal( // A void parameter is pretty nasty, but it allows us to throw an error if // the surrounding capture is closed before scheduling [callback]. void checkCapture, @@ -612,7 +612,7 @@ class Script { /// /// If [silenceStartMessage] is `false` (the default), this prints a message /// in debug mode indicating that the script has started running. - Script._( + new _( this.name, StreamSink> stdin, Stream> stdout, @@ -834,5 +834,5 @@ class ScriptComponents { /// The script's exit code, to complete once it exits. final Future exitCode; - ScriptComponents(this.stdin, this.stdout, this.stderr, this.exitCode); + new(this.stdin, this.stdout, this.stderr, this.exitCode); } diff --git a/lib/src/stdio.dart b/lib/src/stdio.dart index 1b5a163..4f39ce4 100644 --- a/lib/src/stdio.dart +++ b/lib/src/stdio.dart @@ -62,7 +62,7 @@ T silenceStdout(T Function() callback) { return runZoned( callback, zoneValues: {stdoutKey: group}, - zoneSpecification: ZoneSpecification(print: (_, __, ___, ____) {}), + zoneSpecification: ZoneSpecification(print: (_, _, _, _) {}), ); } @@ -87,7 +87,7 @@ T silenceOutput(T Function() callback) { return runZoned( callback, zoneValues: {stdoutKey: group, stderrKey: group}, - zoneSpecification: ZoneSpecification(print: (_, __, ___, ____) {}), + zoneSpecification: ZoneSpecification(print: (_, _, _, _) {}), ); } diff --git a/lib/src/stdio_group.dart b/lib/src/stdio_group.dart index bc559c5..5a36053 100644 --- a/lib/src/stdio_group.dart +++ b/lib/src/stdio_group.dart @@ -50,9 +50,9 @@ class StdioGroup { ); } - StdioGroup() : this._(StreamController(sync: true)); + new() : this._(StreamController(sync: true)); - StdioGroup._(this._sinkController) + new _(this._sinkController) : sink = _StdioGroupSink(_sinkController.sink) { _group.add(_sinkController.stream); } @@ -80,7 +80,7 @@ class _StdioGroupSink extends IOSinkBase implements IOSink { /// The underlying sink. final StreamSink> _sink; - _StdioGroupSink(this._sink) { + new(this._sink) { encoding = utf8; } diff --git a/lib/src/util/delayed_completer.dart b/lib/src/util/delayed_completer.dart index 2382185..62e35fa 100644 --- a/lib/src/util/delayed_completer.dart +++ b/lib/src/util/delayed_completer.dart @@ -41,10 +41,10 @@ class DelayedCompleter implements Completer { @override Future get future => _inner.future; - DelayedCompleter() : _inner = Completer(); + new() : _inner = Completer(); /// Like [Completer.sync]. - DelayedCompleter.sync() : _inner = Completer.sync(); + new sync() : _inner = Completer.sync(); @override void complete([FutureOr? value]) { diff --git a/lib/src/util/entangled_controllers.dart b/lib/src/util/entangled_controllers.dart index 6f620d8..4ffb2b1 100644 --- a/lib/src/util/entangled_controllers.dart +++ b/lib/src/util/entangled_controllers.dart @@ -66,7 +66,7 @@ class _EntangledBuffer { /// of [_EntangledBuffer]. final StreamController controller2; - _EntangledBuffer() + new() : controller1 = StreamController(sync: true), controller2 = StreamController(sync: true) { controller1.onListen = _flush; @@ -206,7 +206,7 @@ class _EntangledController extends StreamSinkBase @override StreamSink get sink => this; - _EntangledController(this._buffer, this._isController1); + new(this._buffer, this._isController1); @override Future addStream(Stream stream, {bool? cancelOnError}) { diff --git a/lib/src/util/named_stream_transformer.dart b/lib/src/util/named_stream_transformer.dart index bd0b19e..30819e4 100644 --- a/lib/src/util/named_stream_transformer.dart +++ b/lib/src/util/named_stream_transformer.dart @@ -23,15 +23,15 @@ class NamedStreamTransformer implements StreamTransformer { /// The implementation of the [bind] method. final Stream Function(Stream) _bind; - NamedStreamTransformer( + new( this._name, StreamSubscription Function(Stream stream, bool cancelOnError) onListen, ) : _bind = StreamTransformer(onListen).bind; - NamedStreamTransformer.fromBind(this._name, this._bind); + new fromBind(this._name, this._bind); - NamedStreamTransformer.fromHandlers( + new fromHandlers( this._name, { void Function(S data, EventSink sink)? handleData, void Function(Object error, StackTrace stackTrace, EventSink sink)? diff --git a/lib/src/util/sink_base.dart b/lib/src/util/sink_base.dart index 5427d65..d860ece 100644 --- a/lib/src/util/sink_base.dart +++ b/lib/src/util/sink_base.dart @@ -123,7 +123,7 @@ abstract class IOSinkBase extends StreamSinkBase> implements IOSink { @override Encoding encoding; - IOSinkBase([this.encoding = utf8]); + new([this.encoding = utf8]); /// See [IOSink.flush] from `dart:io`. /// diff --git a/test/fake_stream_consumer.dart b/test/fake_stream_consumer.dart index 1bde16a..bf748f8 100644 --- a/test/fake_stream_consumer.dart +++ b/test/fake_stream_consumer.dart @@ -18,7 +18,7 @@ import 'dart:async'; class FakeStreamConsumer implements StreamConsumer { final Future Function(Stream stream) _implementation; - FakeStreamConsumer(this._implementation); + new(this._implementation); @override Future addStream(Stream stream) => _implementation(stream); diff --git a/test/util/entangled_controllers_test.dart b/test/util/entangled_controllers_test.dart index e4171c1..5bb467c 100644 --- a/test/util/entangled_controllers_test.dart +++ b/test/util/entangled_controllers_test.dart @@ -33,12 +33,12 @@ void main() { test("when listened in the same microtask", () { controller1.stream.listen( expectAsync1((_) {}, count: 0), - onError: expectAsync2((_, __) {}, count: 0), + onError: expectAsync2((_, _) {}, count: 0), onDone: expectAsync0(() {}, count: 0), ); controller2.stream.listen( expectAsync1((_) {}, count: 0), - onError: expectAsync2((_, __) {}, count: 0), + onError: expectAsync2((_, _) {}, count: 0), onDone: expectAsync0(() {}, count: 0), ); }); @@ -46,13 +46,13 @@ void main() { test("when listened in separate microtasks", () async { controller1.stream.listen( expectAsync1((_) {}, count: 0), - onError: expectAsync2((_, __) {}, count: 0), + onError: expectAsync2((_, _) {}, count: 0), onDone: expectAsync0(() {}, count: 0), ); await Future.value(); controller2.stream.listen( expectAsync1((_) {}, count: 0), - onError: expectAsync2((_, __) {}, count: 0), + onError: expectAsync2((_, _) {}, count: 0), onDone: expectAsync0(() {}, count: 0), ); }); @@ -60,13 +60,13 @@ void main() { test("when listened in distant microtasks", () async { controller1.stream.listen( expectAsync1((_) {}, count: 0), - onError: expectAsync2((_, __) {}, count: 0), + onError: expectAsync2((_, _) {}, count: 0), onDone: expectAsync0(() {}, count: 0), ); await pumpEventQueue(); controller2.stream.listen( expectAsync1((_) {}, count: 0), - onError: expectAsync2((_, __) {}, count: 0), + onError: expectAsync2((_, _) {}, count: 0), onDone: expectAsync0(() {}, count: 0), ); }); From 0f78b091d88e2836dd704175d772b7824b044b28 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Fri, 21 Aug 2026 15:18:26 -0700 Subject: [PATCH 3/4] Use primary constructors --- lib/src/buffered_script.dart | 49 ++++++++++++------------ lib/src/cli_arguments.dart | 20 ++++------ lib/src/exception.dart | 10 ++--- lib/src/script.dart | 51 +++++++++++-------------- lib/src/stdio_group.dart | 21 +++++----- lib/src/util/entangled_controllers.dart | 19 ++++----- lib/src/util/sink_base.dart | 12 +++--- test/fake_stream_consumer.dart | 8 ++-- 8 files changed, 85 insertions(+), 105 deletions(-) diff --git a/lib/src/buffered_script.dart b/lib/src/buffered_script.dart index 10ec132..17b1da2 100644 --- a/lib/src/buffered_script.dart +++ b/lib/src/buffered_script.dart @@ -39,7 +39,26 @@ import 'util/entangled_controllers.dart'; /// [BufferedScript] and only [release] them once they're the only script /// running, or only if they fail. @sealed -class BufferedScript extends Script { +class BufferedScript._( + Script script, + + /// A buffer of the inner script's stdout. + /// + /// We need to buffer this ourselves rather than relying on the inner script's + /// buffer because once the inner [Script.done] completes, its stdio streams + /// will emit done events rather than replaying their buffers. + /// + /// This is null if this script is in stderr-only mode and stdout should be + /// forwarded as normal. + final StreamController>? _stdoutBuffer, + + /// A buffer of the inner script's stderr. + /// + /// We need to buffer this ourselves rather than relying on the inner script's + /// buffer because once the inner [Script.done] completes, its stdio streams + /// will emit done events rather than replaying their buffers. + final StreamController> _stderrBuffer, +) extends Script { @override Stream> get stdout { var stdoutCompleter = _stdoutCompleter; @@ -57,17 +76,9 @@ class BufferedScript extends Script { /// /// This is null if this script is in stderr-only mode and stdout should be /// forwarded as normal. - final StreamCompleter>? _stdoutCompleter; - - /// A buffer of the inner script's stdout. - /// - /// We need to buffer this ourselves rather than relying on the inner script's - /// buffer because once the inner [Script.done] completes, its stdio streams - /// will emit done events rather than replaying their buffers. - /// - /// This is null if this script is in stderr-only mode and stdout should be - /// forwarded as normal. - final StreamController>? _stdoutBuffer; + final StreamCompleter>? _stdoutCompleter = _stdoutBuffer == null + ? null + : StreamCompleter>(); @override Stream> get stderr { @@ -80,13 +91,6 @@ class BufferedScript extends Script { /// The completer that forwards [_stderrBuffer] once [release] is called. final _stderrCompleter = StreamCompleter>(); - /// A buffer of the inner script's stderr. - /// - /// We need to buffer this ourselves rather than relying on the inner script's - /// buffer because once the inner [Script.done] completes, its stdio streams - /// will emit done events rather than replaying their buffers. - final StreamController> _stderrBuffer; - /// Like [Script.capture], but all output is silently buffered until [release] /// is called. /// @@ -120,11 +124,8 @@ class BufferedScript extends Script { /// A helper constructor that allows [BufferedScript.capture] to pass in both /// [_stdoutBuffer] and [_stderrBuffer] from a single call to /// [createEntangledControllers]. - new _(Script script, this._stdoutBuffer, this._stderrBuffer) - : _stdoutCompleter = _stdoutBuffer == null - ? null - : StreamCompleter>(), - super.fromComponentsInternal( + this + : super.fromComponentsInternal( script.name, () => ScriptComponents( script.stdin, diff --git a/lib/src/cli_arguments.dart b/lib/src/cli_arguments.dart index e00ead6..4279543 100644 --- a/lib/src/cli_arguments.dart +++ b/lib/src/cli_arguments.dart @@ -21,13 +21,13 @@ import 'package:path/path.dart' as p; import 'package:string_scanner/string_scanner.dart'; /// CLI arguments parsed from a `executableAndArgs` string. -class CliArguments { +class CliArguments._( /// The executable to run. - final String executable; + final String executable, /// The arguments to the executable, with globs not yet resolved. - final List<_Argument> _arguments; - + final List<_Argument> _arguments, +) { /// Parses [argString], a shell-style string of space-separated arguments, /// into a list of separate arguments. /// @@ -57,8 +57,6 @@ class CliArguments { return CliArguments._(executable, args); } - new _(this.executable, this._arguments); - /// Consumes zero or more spaces. static void _consumeSpaces(StringScanner scanner) { while (scanner.scanChar($space)) {} @@ -140,19 +138,17 @@ class CliArguments { } /// An argument parsed from a `executableAndArgs` string. -class _Argument { +class _Argument( /// The plain text of the argument, to be used if globbing is disabled or if /// [_glob] matches no files. - final String _plain; + final String _plain, /// The glob for the argument. /// /// If this is non-`null`, the files it matches are used as the arguments in /// place of [_plain]. If it matches no files, [_plain] is used instead. - final Glob? _glob; - - new(this._plain, this._glob); - + final Glob? _glob, +) { /// Returns the files matched by this argument's [Glob] if it has one and if /// it matches at least one file, or the plain argument string otherwise. /// diff --git a/lib/src/exception.dart b/lib/src/exception.dart index 26effcc..80f5ed0 100644 --- a/lib/src/exception.dart +++ b/lib/src/exception.dart @@ -13,14 +13,14 @@ // limitations under the License. /// An exception indicating that a [Script] failed. -class ScriptException implements Exception { +class ScriptException( /// The human-readable name of the script that failed. - final String scriptName; + final String scriptName, /// The exit code produced by the failing script. - final int exitCode; - - new(this.scriptName, this.exitCode) { + final int exitCode, +) implements Exception { + this { if (exitCode == 0) { throw RangeError.value(exitCode, "exitCode", "May not be 0"); } diff --git a/lib/src/script.dart b/lib/src/script.dart index 33152f0..74aa882 100644 --- a/lib/src/script.dart +++ b/lib/src/script.dart @@ -73,10 +73,18 @@ final scriptNameKey = #_captureName; /// * Passing error events to [stdin] is not allowed. If an error is passed, /// [stdin] wil close and forward the error to [stdin.done]. @sealed -class Script { +class Script._( /// A human-readable name of the script. - final String name; + final String name, + StreamSink> stdin, + Stream> stdout, + Stream> stderr, + Future exitCode, + /// The script's signal handler to terminate the process. + final bool Function(ProcessSignal) _signalHandler, { + bool silenceStartMessage = false, +}) { /// The standard input stream that's used to pass data into the process. late final IOSink stdin; @@ -160,9 +168,6 @@ class Script { /// script exits. final _outputCloser = StreamCloser>(); - /// The script's signal handler to terminate the process. - bool Function(ProcessSignal) _signalHandler; - /// Sends a [ProcessSignal] to terminate the process. /// /// If the [Script] is a Unix-style OS process, pass the given signal to the @@ -430,13 +435,11 @@ class Script { /// /// This script passes each line of stdin to [mapper] and emits the result via /// stdout. - factory mapLines( - String Function(String line) mapper, { - String? name, - }) => Script.fromLineTransformer( - StreamTransformer.fromBind((stream) => stream.map(mapper)), - name: name ?? mapper.toString(), - ); + factory mapLines(String Function(String line) mapper, {String? name}) => + Script.fromLineTransformer( + StreamTransformer.fromBind((stream) => stream.map(mapper)), + name: name ?? mapper.toString(), + ); /// Pipes each script's [stdout] into the next script's [stdin]. /// @@ -612,15 +615,7 @@ class Script { /// /// If [silenceStartMessage] is `false` (the default), this prints a message /// in debug mode indicating that the script has started running. - new _( - this.name, - StreamSink> stdin, - Stream> stdout, - Stream> stderr, - Future exitCode, - this._signalHandler, { - bool silenceStartMessage = false, - }) { + this { this.stdin = IOSink( stdin.transform( StreamSinkTransformer.fromStreamTransformer(_stdinCloser), @@ -821,18 +816,16 @@ class Script { /// A struct containing the components needed to create a [Script]. @sealed -class ScriptComponents { +class ScriptComponents( /// The standard input sink. - final StreamSink> stdin; + final StreamSink> stdin, /// The standard output stream. - final Stream> stdout; + final Stream> stdout, /// The standard error stream. - final Stream> stderr; + final Stream> stderr, /// The script's exit code, to complete once it exits. - final Future exitCode; - - new(this.stdin, this.stdout, this.stderr, this.exitCode); -} + final Future exitCode, +); diff --git a/lib/src/stdio_group.dart b/lib/src/stdio_group.dart index 5a36053..6eb9b0c 100644 --- a/lib/src/stdio_group.dart +++ b/lib/src/stdio_group.dart @@ -28,20 +28,20 @@ import 'util/sink_base.dart'; /// data to stdout/stderr, as well as a [writeln] method that can be used for /// the same purpose but is unaffected by [sink] being closed or locked by /// [Sink.addStream]. -class StdioGroup { +class StdioGroup._( + /// The controller for [sink]. + final StreamController> _sinkController, +) { /// The inner stream group that handles all the heavy lifting of merging /// streams. final _group = StreamGroup>(); /// The sink for manually adding additional output. - final IOSink sink; + final IOSink sink = _StdioGroupSink(_sinkController.sink); /// See [StreamGroup.stream]. Stream> get stream => _group.stream; - /// The controller for [sink]. - final StreamController> _sinkController; - static Tuple2 entangled() { var controllers = createEntangledControllers>(); return Tuple2( @@ -52,8 +52,7 @@ class StdioGroup { new() : this._(StreamController(sync: true)); - new _(this._sinkController) - : sink = _StdioGroupSink(_sinkController.sink) { + this { _group.add(_sinkController.stream); } @@ -76,11 +75,11 @@ class StdioGroup { /// A custom [IOSink] that doesn't actually close the underlying sink when it's /// closed. -class _StdioGroupSink extends IOSinkBase implements IOSink { +class _StdioGroupSink( /// The underlying sink. - final StreamSink> _sink; - - new(this._sink) { + final StreamSink> _sink, +) extends IOSinkBase implements IOSink { + this { encoding = utf8; } diff --git a/lib/src/util/entangled_controllers.dart b/lib/src/util/entangled_controllers.dart index 4ffb2b1..49a7382 100644 --- a/lib/src/util/entangled_controllers.dart +++ b/lib/src/util/entangled_controllers.dart @@ -58,17 +58,15 @@ class _EntangledBuffer { /// /// The [StreamSink] methods on this controller should not be accessed outside /// of [_EntangledBuffer]. - final StreamController controller1; + final StreamController controller1 = StreamController(sync: true); /// The entangled controller that corresponds to events labeled `false`. /// /// The [StreamSink] methods on this controller should not be accessed outside /// of [_EntangledBuffer]. - final StreamController controller2; + final StreamController controller2 = StreamController(sync: true); - new() - : controller1 = StreamController(sync: true), - controller2 = StreamController(sync: true) { + new() { controller1.onListen = _flush; controller2.onListen = _flush; } @@ -159,14 +157,13 @@ class _EntangledBuffer { /// A wrapper that pipes inputs to [_EntangledBuffer] and exposes output from /// one of [_EntangledBuffer]'s controllers. -class _EntangledController extends StreamSinkBase - implements StreamController { +class _EntangledController( /// The buffer that this wraps. - final _EntangledBuffer _buffer; + final _EntangledBuffer _buffer, /// Whether this is [_buffer.controller1] or [_buffer.controller2]. - final bool _isController1; - + final bool _isController1, +) extends StreamSinkBase implements StreamController { StreamController get _outputController => _isController1 ? _buffer.controller1 : _buffer.controller2; @@ -206,8 +203,6 @@ class _EntangledController extends StreamSinkBase @override StreamSink get sink => this; - new(this._buffer, this._isController1); - @override Future addStream(Stream stream, {bool? cancelOnError}) { if (cancelOnError == true) { diff --git a/lib/src/util/sink_base.dart b/lib/src/util/sink_base.dart index d860ece..c3034b7 100644 --- a/lib/src/util/sink_base.dart +++ b/lib/src/util/sink_base.dart @@ -75,7 +75,8 @@ abstract class EventSinkBase implements EventSink { /// /// This takes care of ensuring that events can't be added after [close] is /// called or during a call to [onStream]. -abstract class StreamSinkBase extends EventSinkBase +abstract class StreamSinkBase() + extends EventSinkBase implements StreamSink { /// Whether a call to [addStream] is ongoing. bool _addingStream = false; @@ -118,13 +119,10 @@ abstract class StreamSinkBase extends EventSinkBase /// /// This takes care of ensuring that events can't be added after [close] is /// called or during a call to [onStream]. -abstract class IOSinkBase extends StreamSinkBase> implements IOSink { +abstract class IOSinkBase([ /// See [IOSink.encoding] from `dart:io`. - @override - Encoding encoding; - - new([this.encoding = utf8]); - + @override var Encoding encoding = utf8, +]) extends StreamSinkBase> implements IOSink { /// See [IOSink.flush] from `dart:io`. /// /// Because this base class doesn't do any buffering of its own, [flush] diff --git a/test/fake_stream_consumer.dart b/test/fake_stream_consumer.dart index bf748f8..c4ce17a 100644 --- a/test/fake_stream_consumer.dart +++ b/test/fake_stream_consumer.dart @@ -15,11 +15,9 @@ import 'dart:async'; /// A [StreamConsumer] whose implementation just comes from a callback. -class FakeStreamConsumer implements StreamConsumer { - final Future Function(Stream stream) _implementation; - - new(this._implementation); - +class FakeStreamConsumer( + final Future Function(Stream stream) _implementation, +) implements StreamConsumer { @override Future addStream(Stream stream) => _implementation(stream); From a79e576a802c0abaa51a612fe465fe58d91911d3 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Fri, 21 Aug 2026 15:21:43 -0700 Subject: [PATCH 4/4] Use dot shorthands --- lib/cli_script.dart | 2 +- lib/src/buffered_script.dart | 2 +- lib/src/script.dart | 12 +++--------- lib/src/stdio.dart | 2 +- test/signal_test.dart | 2 +- test/stdio_test.dart | 2 +- 6 files changed, 8 insertions(+), 14 deletions(-) diff --git a/lib/cli_script.dart b/lib/cli_script.dart index 47bc745..f95a85b 100644 --- a/lib/cli_script.dart +++ b/lib/cli_script.dart @@ -407,7 +407,7 @@ IOSink write(String path) => File(path).openWrite(); /// }); /// } /// ``` -IOSink append(String path) => File(path).openWrite(mode: FileMode.append); +IOSink append(String path) => File(path).openWrite(mode: .append); /// Executes [callback] with arguments from standard input. /// diff --git a/lib/src/buffered_script.dart b/lib/src/buffered_script.dart index 17b1da2..5d42f9e 100644 --- a/lib/src/buffered_script.dart +++ b/lib/src/buffered_script.dart @@ -163,7 +163,7 @@ class BufferedScript._( ]); // Give outer stdio listeners a chance to handle the IO. - await Future.delayed(Duration.zero); + await Future.pause(.zero); }); final _releaseMemo = AsyncMemoizer(); } diff --git a/lib/src/script.dart b/lib/src/script.dart index 74aa882..5ae35dd 100644 --- a/lib/src/script.dart +++ b/lib/src/script.dart @@ -177,7 +177,7 @@ class Script._( /// Returns `true` if the signal is successfully delivered to the [Script]. /// Otherwise the signal could not be sent, usually meaning that the process /// is already dead or the [Script] doesn't have a signal handler. - bool kill([ProcessSignal signal = ProcessSignal.sigterm]) { + bool kill([ProcessSignal signal = .sigterm]) { if (_doneCompleter.isCompleted) return false; try { return _signalHandler(signal); @@ -641,10 +641,7 @@ class Script._( _doneCompleter.complete(null); } else { debug("[$name] exited with exit code $code"); - _doneCompleter.completeError( - ScriptException(name, code), - StackTrace.current, - ); + _doneCompleter.completeError(ScriptException(name, code), .current); } _closeOutputStreams(); @@ -744,10 +741,7 @@ class Script._( ), ); _extraStderrController.close(); - _doneCompleter.completeError( - ScriptException(name, 257), - StackTrace.current, - ); + _doneCompleter.completeError(ScriptException(name, 257), .current); } _closeOutputStreams(); diff --git a/lib/src/stdio.dart b/lib/src/stdio.dart index 4f39ce4..243dc7b 100644 --- a/lib/src/stdio.dart +++ b/lib/src/stdio.dart @@ -135,7 +135,7 @@ Script silenceUntilFailure( script.release(); // Give the new stdio a chance to propagate. - await Future.delayed(Duration.zero); + await Future.pause(.zero); rethrow; } }, diff --git a/test/signal_test.dart b/test/signal_test.dart index 60bbaed..d07a824 100644 --- a/test/signal_test.dart +++ b/test/signal_test.dart @@ -28,7 +28,7 @@ void main() { var script = mainScript('while (true) {}'); await pumpEventQueue(); - expect(script.kill(ProcessSignal.sigint), true); + expect(script.kill(.sigint), true); expect(script.done, throwsScriptException(-2)); }); diff --git a/test/stdio_test.dart b/test/stdio_test.dart index a817d30..6334792 100644 --- a/test/stdio_test.dart +++ b/test/stdio_test.dart @@ -378,7 +378,7 @@ void main() { var controller = StreamController>( onCancel: () => canceled = true, ); - var script = Script.capture((_) => Future.delayed(Duration.zero)); + var script = Script.capture((_) => Future.pause(.zero)); controller.stream | script; await script.done;