diff --git a/ci/bin/format.dart b/ci/bin/format.dart index 207c152a6f2b9..a307ea522ab58 100644 --- a/ci/bin/format.dart +++ b/ci/bin/format.dart @@ -269,7 +269,9 @@ abstract class FormatChecker { ...types, ]); } - return output.split('\n').where((String line) => line.isNotEmpty).toList(); + return output.split('\n').where( + (String line) => line.isNotEmpty && !line.contains('third_party') + ).toList(); } /// Generates a reporting function to supply to ProcessRunner to use instead @@ -285,7 +287,7 @@ abstract class FormatChecker { final String pendingStr = pending.toString().padLeft(3); final String failedStr = failed.toString().padLeft(3); - stderr.write('$name Jobs: $percent% done, ' + stdout.write('$name Jobs: $percent% done, ' '$completedStr/$totalStr completed, ' '$inProgressStr in progress, ' '$pendingStr pending, ' @@ -296,7 +298,7 @@ abstract class FormatChecker { /// Clears the last printed report line so garbage isn't left on the terminal. @protected void reportDone() { - stderr.write('\r${' ' * 100}\r'); + stdout.write('\r${' ' * 100}\r'); } } @@ -436,7 +438,7 @@ class ClangFormatChecker extends FormatChecker { } else { error('Found ${failed.length} C++/ObjC/Shader file${plural ? 's' : ''}' ' which ${plural ? 'were' : 'was'} formatted incorrectly.'); - stdout.writeln('To fix, run:'); + stdout.writeln('To fix, run `et format` or:'); stdout.writeln(); stdout.writeln('git apply < main(List arguments) async { message ??= ''; switch (type) { case MessageType.message: - stderr.writeln(message); + stdout.writeln(message); case MessageType.error: stderr.writeln('ERROR: $message'); case MessageType.warning: @@ -1160,11 +1167,7 @@ Future main(List arguments) async { message('Unable to apply $humanCheckName format fixes.'); } } else { - message('Performing $humanCheckName format check'); stepResult = await checker.checkFormatting(); - if (!stepResult) { - message('Found $humanCheckName format problems.'); - } } result = result && stepResult; } diff --git a/tools/engine_tool/README.md b/tools/engine_tool/README.md index 2a36eb9c24101..ecf2d13fd8425 100644 --- a/tools/engine_tool/README.md +++ b/tools/engine_tool/README.md @@ -14,6 +14,8 @@ before it will work. The tool has the following commands. * `help` - Prints helpful information about commands and usage. +* `format` - Formats files in the engine tree using various off-the-shelf +formatters. * `query builds` - Lists the CI builds described under `ci/builders` that the host platform is capable of executing. diff --git a/tools/engine_tool/lib/src/commands/command_runner.dart b/tools/engine_tool/lib/src/commands/command_runner.dart index 48e306d5098ab..a4faa2ea853bf 100644 --- a/tools/engine_tool/lib/src/commands/command_runner.dart +++ b/tools/engine_tool/lib/src/commands/command_runner.dart @@ -7,6 +7,8 @@ import 'package:args/command_runner.dart'; import 'package:engine_build_configs/engine_build_configs.dart'; import '../environment.dart'; +import 'command.dart'; +import 'format_command.dart'; import 'query_command.dart'; /// The root command runner. @@ -17,10 +19,16 @@ final class ToolCommandRunner extends CommandRunner { required this.environment, required this.configs, }) : super(toolName, toolDescription) { - addCommand(QueryCommand( - environment: environment, - configs: configs, - )); + final List commands = [ + FormatCommand( + environment: environment, + ), + QueryCommand( + environment: environment, + configs: configs, + ), + ]; + commands.forEach(addCommand); } /// The name of the tool as reported in the tool's usage and help diff --git a/tools/engine_tool/lib/src/commands/flags.dart b/tools/engine_tool/lib/src/commands/flags.dart new file mode 100644 index 0000000000000..d08514b174797 --- /dev/null +++ b/tools/engine_tool/lib/src/commands/flags.dart @@ -0,0 +1,18 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// ignore_for_file: public_member_api_docs + +// The purpose of this list of flags in a file separate from the command +// definitions is to ensure that flags are named consistently across +// subcommands. For example, unless there's a compelling reason to have both, +// we'd avoid having one subcommand define an --all flag while another defines +// an --everything flag. + +// Keep this list alphabetized. +const String allFlag = 'all'; +const String builderFlag = 'builder'; +const String dryRunFlag = 'dry-run'; +const String quietFlag = 'quiet'; +const String verboseFlag = 'verbose'; diff --git a/tools/engine_tool/lib/src/commands/format_command.dart b/tools/engine_tool/lib/src/commands/format_command.dart new file mode 100644 index 0000000000000..6e7fcee3a6976 --- /dev/null +++ b/tools/engine_tool/lib/src/commands/format_command.dart @@ -0,0 +1,165 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:convert'; +import 'dart:io' as io; + +import 'package:path/path.dart' as p; + +import '../logger.dart'; +import 'command.dart'; +import 'flags.dart'; + +/// The 'format' command. +/// +/// The format command implementation below works by spawning another Dart VM to +/// run the program under ci/bin/format.dart. +/// +// TODO(team-engine): Part of https://github.com/flutter/flutter/issues/132807. +// Instead, format.dart should be moved under the engine_tool package and +// invoked by a function call. The file ci/bin/format.dart should be split up so +// that each of its `FormatCheckers` is in a separate file under src/formatters, +// and they should be unit-tested. +final class FormatCommand extends CommandBase { + // ignore: public_member_api_docs + FormatCommand({ + required super.environment, + }) { + argParser + ..addFlag( + allFlag, + abbr: 'a', + help: 'By default only dirty files are checked. This flag causes all ' + 'files to be checked. (Slow)', + negatable: false, + ) + ..addFlag( + dryRunFlag, + abbr: 'd', + help: 'Do not fix formatting in-place. Instead, print file diffs to ' + 'the logs.', + negatable: false, + ) + ..addFlag( + quietFlag, + abbr: 'q', + help: 'Silences all log messages except for errors and warnings', + negatable: false, + ) + ..addFlag( + verboseFlag, + abbr: 'v', + help: 'Prints verbose output', + negatable: false, + ); + } + + @override + String get name => 'format'; + + @override + String get description => 'Formats files using standard formatters and styles.'; + + @override + Future run() async { + final bool all = argResults![allFlag]! as bool; + final bool dryRun = argResults![dryRunFlag]! as bool; + final bool quiet = argResults![quietFlag]! as bool; + final bool verbose = argResults![verboseFlag]! as bool; + final String formatPath = p.join( + environment.engine.flutterDir.path, 'ci', 'bin', 'format.dart', + ); + + final io.Process process = await environment.processRunner.processManager.start( + [ + environment.platform.resolvedExecutable, + formatPath, + if (all) '--all-files', + if (!dryRun) '--fix', + if (verbose) '--verbose', + ], + workingDirectory: environment.engine.flutterDir.path, + ); + final Completer stdoutComplete = Completer(); + final Completer stderrComplete = Completer(); + + final _FormatStreamer streamer = _FormatStreamer( + environment.logger, + dryRun, + quiet, + ); + process.stdout + .transform(const Utf8Decoder()) + .transform(const LineSplitter()) + .listen( + streamer.nextStdout, + onDone: () async => stdoutComplete.complete(), + ); + process.stderr + .transform(const Utf8Decoder()) + .transform(const LineSplitter()) + .listen( + streamer.nextStderr, + onDone: () async => stderrComplete.complete(), + ); + + await Future.wait(>[ + stdoutComplete.future, stderrComplete.future, + ]); + final int exitCode = await process.exitCode; + + return exitCode; + } +} + +class _FormatStreamer { + _FormatStreamer(this.logger, this.dryRun, this.quiet); + + final Logger logger; + final bool dryRun; + final bool quiet; + + bool inADiff = false; + bool inProgress = false; + + void nextStdout(String line) { + if (quiet) { + return; + } + final String l = line.trim(); + if (l == 'To fix, run `et format` or:') { + inADiff = true; + } + if (l.isNotEmpty && (!inADiff || dryRun)) { + if (_isProgressLine(l)) { + inProgress = true; + logger.clearLine(); + logger.status('$l\r', newline: false); + } else { + if (inProgress) { + logger.clearLine(); + inProgress = false; + } + logger.status(l); + } + } + if (l == 'DONE') { + inADiff = false; + } + } + + void nextStderr(String line) { + final String l = line.trim(); + if (l.isEmpty) { + return; + } + logger.error(l); + } + + bool _isProgressLine(String l) { + final List words = l.split(','); + return words.isNotEmpty && words[0].endsWith('% done'); + } +} diff --git a/tools/engine_tool/lib/src/commands/query_command.dart b/tools/engine_tool/lib/src/commands/query_command.dart index a9de5a8a0c727..24aa8ae1f46de 100644 --- a/tools/engine_tool/lib/src/commands/query_command.dart +++ b/tools/engine_tool/lib/src/commands/query_command.dart @@ -5,14 +5,11 @@ import 'package:engine_build_configs/engine_build_configs.dart'; import 'command.dart'; +import 'flags.dart'; -const String _allFlag = 'all'; -const String _builderFlag = 'builder'; -const String _verboseFlag = 'verbose'; - -/// The root 'query' command. +// ignore: public_member_api_docs final class QueryCommand extends CommandBase { - /// Constructs the 'query' command. + // ignore: public_member_api_docs QueryCommand({ required super.environment, required this.configs, @@ -20,13 +17,13 @@ final class QueryCommand extends CommandBase { // Add options here that are common to all queries. argParser ..addFlag( - _allFlag, + allFlag, abbr: 'a', help: 'List all results, even when not relevant on this platform', negatable: false, ) ..addOption( - _builderFlag, + builderFlag, abbr: 'b', help: 'Restrict the query to a single builder.', allowed: [ @@ -42,7 +39,7 @@ final class QueryCommand extends CommandBase { }, ) ..addFlag( - _verboseFlag, + verboseFlag, abbr: 'v', help: 'Respond to queries with extra information', negatable: false, @@ -65,9 +62,9 @@ final class QueryCommand extends CommandBase { 'and tests.'; } -/// The 'query builds' command. +// ignore: public_member_api_docs final class QueryBuildsCommand extends CommandBase { - /// Constructs the 'query build' command. + // ignore: public_member_api_docs QueryBuildsCommand({ required super.environment, required this.configs, @@ -87,9 +84,9 @@ final class QueryBuildsCommand extends CommandBase { Future run() async { // Loop through all configs, and log those that are compatible with the // current platform. - final bool all = parent!.argResults![_allFlag]! as bool; - final String? builderName = parent!.argResults![_builderFlag] as String?; - final bool verbose = parent!.argResults![_verboseFlag] as bool; + final bool all = parent!.argResults![allFlag]! as bool; + final String? builderName = parent!.argResults![builderFlag] as String?; + final bool verbose = parent!.argResults![verboseFlag] as bool; if (!verbose) { environment.logger.status( 'Add --verbose to see detailed information about each builder', diff --git a/tools/engine_tool/lib/src/logger.dart b/tools/engine_tool/lib/src/logger.dart index 97b9478b06f9f..3cb7c60a7e110 100644 --- a/tools/engine_tool/lib/src/logger.dart +++ b/tools/engine_tool/lib/src/logger.dart @@ -76,7 +76,7 @@ class Logger { } runZoned(() { try { - sink.writeln(message); + sink.write(message); } catch (_) { // ignore: avoid_catches_without_on_clauses _stdioDone = true; } @@ -104,27 +104,38 @@ class Logger { } /// Record a log message at level [Logger.error]. - void error(Object? message, {int indent = 0}) { - _emitLog(errorLevel, message, indent); + void error(Object? message, {int indent = 0, bool newline = true}) { + _emitLog(errorLevel, message, indent, newline); } /// Record a log message at level [Logger.warning]. - void warning(Object? message, {int indent = 0}) { - _emitLog(warningLevel, message, indent); + void warning(Object? message, {int indent = 0, bool newline = true}) { + _emitLog(warningLevel, message, indent, newline); } /// Record a log message at level [Logger.warning]. - void status(Object? message, {int indent = 0}) { - _emitLog(statusLevel, message, indent); + void status(Object? message, {int indent = 0, bool newline = true}) { + _emitLog(statusLevel, message, indent, newline); } /// Record a log message at level [Logger.info]. - void info(Object? message, {int indent = 0}) { - _emitLog(infoLevel, message, indent); + void info(Object? message, {int indent = 0, bool newline = true}) { + _emitLog(infoLevel, message, indent, newline); } - void _emitLog(log.Level level, Object? message, int indent) { - final String m = '${' ' * indent}$message'; + /// Writes a number of spaces to stdout equal to the width of the terminal + /// and emits a carriage return. + void clearLine() { + if (!io.stdout.hasTerminal) { + return; + } + final int width = io.stdout.terminalColumns; + final String spaces = ' ' * width; + _ioSinkWrite(io.stdout, '$spaces\r'); + } + + void _emitLog(log.Level level, Object? message, int indent, bool newline) { + final String m = '${' ' * indent}$message${newline ? '\n' : ''}'; _logger.log(level, m); } diff --git a/tools/engine_tool/test/format_command_test.dart b/tools/engine_tool/test/format_command_test.dart new file mode 100644 index 0000000000000..04dfaedc20251 --- /dev/null +++ b/tools/engine_tool/test/format_command_test.dart @@ -0,0 +1,250 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:ffi' as ffi show Abi; +import 'dart:io' as io; + +import 'package:engine_build_configs/engine_build_configs.dart'; +import 'package:engine_repo_tools/engine_repo_tools.dart'; +import 'package:engine_tool/src/commands/command_runner.dart'; +import 'package:engine_tool/src/commands/flags.dart'; +import 'package:engine_tool/src/environment.dart'; +import 'package:engine_tool/src/logger.dart'; +import 'package:litetest/litetest.dart'; +import 'package:logging/logging.dart' as log; +import 'package:platform/platform.dart'; +import 'package:process_fakes/process_fakes.dart'; +import 'package:process_runner/process_runner.dart'; + +void main() { + final Engine engine; + try { + engine = Engine.findWithin(); + } catch (e) { + io.stderr.writeln(e); + io.exitCode = 1; + return; + } + + Environment linuxEnv(Logger logger, FakeProcessManager processManager) { + return Environment( + abi: ffi.Abi.linuxX64, + engine: engine, + platform: FakePlatform( + resolvedExecutable: '/dart', + operatingSystem: Platform.linux, + ), + processRunner: ProcessRunner( + processManager: processManager, + ), + logger: logger, + ); + } + + List stringsFromLogs(List logs) { + return logs.map((log.LogRecord r) => r.message).toList(); + } + + test('--fix is passed to ci/bin/format.dart by default', () async { + final Logger logger = Logger.test(); + final FakeProcessManager manager = _formatProcessManager( + expectedFlags: ['--fix'], + ); + final Environment env = linuxEnv(logger, manager); + final ToolCommandRunner runner = ToolCommandRunner( + environment: env, + configs: {}, + ); + final int result = await runner.run([ + 'format', + ]); + expect(result, equals(0)); + }); + + test('--fix is not passed to ci/bin/format.dart with --dry-run', () async { + final Logger logger = Logger.test(); + final FakeProcessManager manager = _formatProcessManager( + expectedFlags: [], + ); + final Environment env = linuxEnv(logger, manager); + final ToolCommandRunner runner = ToolCommandRunner( + environment: env, + configs: {}, + ); + final int result = await runner.run([ + 'format', '--$dryRunFlag', + ]); + expect(result, equals(0)); + }); + + test('exit code is non-zero when ci/bin/format.dart exit code was non zero', () async { + final Logger logger = Logger.test(); + final FakeProcessManager manager = _formatProcessManager( + expectedFlags: ['--fix'], + exitCode: 1, + ); + final Environment env = linuxEnv(logger, manager); + final ToolCommandRunner runner = ToolCommandRunner( + environment: env, + configs: {}, + ); + final int result = await runner.run([ + 'format', + ]); + expect(result, equals(1)); + }); + + test('--all-files is passed to ci/bin/format.dart correctly', () async { + final Logger logger = Logger.test(); + final FakeProcessManager manager = _formatProcessManager( + expectedFlags: ['--fix', '--all-files'], + ); + final Environment env = linuxEnv(logger, manager); + final ToolCommandRunner runner = ToolCommandRunner( + environment: env, + configs: {}, + ); + final int result = await runner.run([ + 'format', '--$allFlag', + ]); + expect(result, equals(0)); + }); + + test('--verbose is passed to ci/bin/format.dart correctly', () async { + final Logger logger = Logger.test(); + final FakeProcessManager manager = _formatProcessManager( + expectedFlags: ['--fix', '--verbose'], + ); + final Environment env = linuxEnv(logger, manager); + final ToolCommandRunner runner = ToolCommandRunner( + environment: env, + configs: {}, + ); + final int result = await runner.run([ + 'format', '--$verboseFlag', + ]); + expect(result, equals(0)); + }); + + test('--quiet suppresses non-error output', () async { + final Logger logger = Logger.test(); + final FakeProcessManager manager = _formatProcessManager( + expectedFlags: ['--fix'], + stdout: ['many', 'lines', 'of', 'output'].join('\n'), + stderr: 'error\n', + ); + final Environment env = linuxEnv(logger, manager); + final ToolCommandRunner runner = ToolCommandRunner( + environment: env, + configs: {}, + ); + final int result = await runner.run([ + 'format', '--$quietFlag', + ]); + expect(result, equals(0)); + expect(stringsFromLogs(logger.testLogs), equals(['error\n'])); + }); + + test('Diffs are suppressed by default', () async { + final Logger logger = Logger.test(); + final FakeProcessManager manager = _formatProcessManager( + expectedFlags: ['--fix'], + stdout: [ + 'To fix, run `et format` or:', + 'many', 'lines', 'of', 'output', + 'DONE', + ].join('\n'), + ); + final Environment env = linuxEnv(logger, manager); + final ToolCommandRunner runner = ToolCommandRunner( + environment: env, + configs: {}, + ); + final int result = await runner.run([ + 'format', + ]); + expect(result, equals(0)); + expect(stringsFromLogs(logger.testLogs), isEmpty); + }); + + test('--dry-run disables --fix and prints diffs', () async { + final Logger logger = Logger.test(); + final FakeProcessManager manager = _formatProcessManager( + expectedFlags: [], + stdout: [ + 'To fix, run `et format` or:', + 'many', 'lines', 'of', 'output', + 'DONE', + ].join('\n'), + ); + final Environment env = linuxEnv(logger, manager); + final ToolCommandRunner runner = ToolCommandRunner( + environment: env, + configs: {}, + ); + final int result = await runner.run([ + 'format', '--$dryRunFlag', + ]); + expect(result, equals(0)); + expect( + stringsFromLogs(logger.testLogs), + equals([ + 'To fix, run `et format` or:\n', + 'many\n', 'lines\n', 'of\n', 'output\n', + 'DONE\n', + ])); + }); + + test('progress lines are followed by a carriage return', () async { + final Logger logger = Logger.test(); + const String progressLine = 'diff Jobs: 46% done, 1528/3301 completed, ' + '7 in progress, 1753 pending, 13 failed.'; + final FakeProcessManager manager = _formatProcessManager( + expectedFlags: ['--fix'], + stdout: progressLine, + ); + final Environment env = linuxEnv(logger, manager); + final ToolCommandRunner runner = ToolCommandRunner( + environment: env, + configs: {}, + ); + final int result = await runner.run([ + 'format', + ]); + expect(result, equals(0)); + expect( + stringsFromLogs(logger.testLogs), + equals(['$progressLine\r'])); + }); +} + +FakeProcessManager _formatProcessManager({ + required List expectedFlags, + int exitCode = 0, + String stdout = '', + String stderr = '', + bool Function(Object?, {String? workingDirectory})? canRun, + bool failUnknown = true, +}) { + final io.ProcessResult success = io.ProcessResult(1, 0, '', ''); + final FakeProcess formatProcess = FakeProcess( + exitCode: exitCode, + stdout: stdout, + stderr: stderr, + ); + return FakeProcessManager( + canRun: canRun ?? (Object? exe, {String? workingDirectory}) => true, + onRun: (List cmd) => switch (cmd) { + _ => failUnknown ? io.ProcessResult(1, 1, '', '') : success, + }, + onStart: (List cmd) => switch (cmd) { + [final String exe, final String fmt, ... final List rest] + when exe.endsWith('dart') && + fmt.endsWith('ci/bin/format.dart') && + rest.length == expectedFlags.length && + expectedFlags.every(rest.contains) => formatProcess, + _ => failUnknown ? FakeProcess(exitCode: 1) : FakeProcess(), + }, + ); +} diff --git a/tools/engine_tool/test/logger_test.dart b/tools/engine_tool/test/logger_test.dart index 48eb78706012f..5170f4d7cd077 100644 --- a/tools/engine_tool/test/logger_test.dart +++ b/tools/engine_tool/test/logger_test.dart @@ -20,19 +20,19 @@ void main() { test('error messages are recorded at the default log level', () { final Logger logger = Logger.test(); logger.error('Error'); - expect(stringsFromLogs(logger.testLogs), equals(['Error'])); + expect(stringsFromLogs(logger.testLogs), equals(['Error\n'])); }); test('warning messages are recorded at the default log level', () { final Logger logger = Logger.test(); logger.warning('Warning'); - expect(stringsFromLogs(logger.testLogs), equals(['Warning'])); + expect(stringsFromLogs(logger.testLogs), equals(['Warning\n'])); }); test('status messages are recorded at the default log level', () { final Logger logger = Logger.test(); logger.status('Status'); - expect(stringsFromLogs(logger.testLogs), equals(['Status'])); + expect(stringsFromLogs(logger.testLogs), equals(['Status\n'])); }); test('info messages are not recorded at the default log level', () { @@ -45,12 +45,37 @@ void main() { final Logger logger = Logger.test(); logger.level = Logger.infoLevel; logger.info('info'); - expect(stringsFromLogs(logger.testLogs), equals(['info'])); + expect(stringsFromLogs(logger.testLogs), equals(['info\n'])); }); test('indent indents the message', () { final Logger logger = Logger.test(); logger.status('Status', indent: 1); - expect(stringsFromLogs(logger.testLogs), equals([' Status'])); + expect(stringsFromLogs(logger.testLogs), equals([' Status\n'])); + }); + + test('newlines in error() can be disabled', () { + final Logger logger = Logger.test(); + logger.error('Error', newline: false); + expect(stringsFromLogs(logger.testLogs), equals(['Error'])); + }); + + test('newlines in warning() can be disabled', () { + final Logger logger = Logger.test(); + logger.warning('Warning', newline: false); + expect(stringsFromLogs(logger.testLogs), equals(['Warning'])); + }); + + test('newlines in status() can be disabled', () { + final Logger logger = Logger.test(); + logger.status('Status', newline: false); + expect(stringsFromLogs(logger.testLogs), equals(['Status'])); + }); + + test('newlines in info() can be disabled', () { + final Logger logger = Logger.test(); + logger.level = Logger.infoLevel; + logger.info('info', newline: false); + expect(stringsFromLogs(logger.testLogs), equals(['info'])); }); } diff --git a/tools/engine_tool/test/query_command_test.dart b/tools/engine_tool/test/query_command_test.dart index 9661be4e9303f..1ad2620353f8d 100644 --- a/tools/engine_tool/test/query_command_test.dart +++ b/tools/engine_tool/test/query_command_test.dart @@ -9,6 +9,7 @@ import 'dart:io' as io; import 'package:engine_build_configs/engine_build_configs.dart'; import 'package:engine_repo_tools/engine_repo_tools.dart'; import 'package:engine_tool/src/commands/command_runner.dart'; +import 'package:engine_tool/src/commands/flags.dart'; import 'package:engine_tool/src/environment.dart'; import 'package:engine_tool/src/logger.dart'; import 'package:litetest/litetest.dart'; @@ -81,12 +82,12 @@ void main() { expect( stringsFromLogs(logger.testLogs), equals([ - 'Add --verbose to see detailed information about each builder', - '', - '"linux_test_config" builder:', - ' "build_name" config', - '"linux_test_config2" builder:', - ' "build_name" config', + 'Add --verbose to see detailed information about each builder\n', + '\n', + '"linux_test_config" builder:\n', + ' "build_name" config\n', + '"linux_test_config2" builder:\n', + ' "build_name" config\n', ]), ); }); @@ -99,16 +100,16 @@ void main() { configs: configs, ); final int result = await runner.run([ - 'query', 'builds', '--builder', 'linux_test_config', + 'query', 'builds', '--$builderFlag', 'linux_test_config', ]); expect(result, equals(0)); expect( stringsFromLogs(logger.testLogs), equals([ - 'Add --verbose to see detailed information about each builder', - '', - '"linux_test_config" builder:', - ' "build_name" config', + 'Add --verbose to see detailed information about each builder\n', + '\n', + '"linux_test_config" builder:\n', + ' "build_name" config\n', ]), ); }); @@ -121,7 +122,7 @@ void main() { configs: configs, ); final int result = await runner.run([ - 'query', 'builds', '--all', + 'query', 'builds', '--$allFlag', ]); expect(result, equals(0)); expect(