diff --git a/AUTHORS b/AUTHORS index e0972f1e85..bfc7c6f01f 100644 --- a/AUTHORS +++ b/AUTHORS @@ -49,3 +49,4 @@ Robin Jespersen Jefferson Quesado Mark Diener Alek Åström +Efthymios Sarpmpanis diff --git a/dev/devicelab/bin/tasks/flutter_attach_test.dart b/dev/devicelab/bin/tasks/flutter_attach_test.dart index 11584ff866..0f80acaa62 100644 --- a/dev/devicelab/bin/tasks/flutter_attach_test.dart +++ b/dev/devicelab/bin/tasks/flutter_attach_test.dart @@ -33,7 +33,7 @@ Future testReload(Process process, { Future Function() onListening } stdout.add(line); if (line.contains('Waiting') && onListening != null) listening.complete(onListening()); - if (line.contains('To detach, press "d"; to quit, press "q".')) + if (line.contains('Quit (terminate the application on the device)')) ready.complete(); if (line.contains('Reloaded ')) reloaded.complete(); diff --git a/dev/devicelab/bin/tasks/run_release_test.dart b/dev/devicelab/bin/tasks/run_release_test.dart index 6049975f52..93dde4da2a 100644 --- a/dev/devicelab/bin/tasks/run_release_test.dart +++ b/dev/devicelab/bin/tasks/run_release_test.dart @@ -46,7 +46,7 @@ void main() { ) { stdout.add(line); } - if (line.contains('To quit, press "q".')) { + if (line.contains('Quit (terminate the application on the device).')) { ready.complete(); } }); @@ -96,8 +96,8 @@ void main() { _findNextMatcherInList( stdout, - (String line) => line == 'To quit, press "q".', - 'To quit, press "q".', + (String line) => line.contains('Quit (terminate the application on the device).'), + 'q Quit (terminate the application on the device)', ); _findNextMatcherInList( diff --git a/packages/flutter_tools/lib/src/base/command_help.dart b/packages/flutter_tools/lib/src/base/command_help.dart new file mode 100644 index 0000000000..c8797f4b04 --- /dev/null +++ b/packages/flutter_tools/lib/src/base/command_help.dart @@ -0,0 +1,76 @@ +// Copyright 2014 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:math' as math; + +import '../globals.dart' as globals; +import 'terminal.dart'; + +const String fire = '🔥'; +const int maxLineWidth = 84; + +/// Encapsulates the help text construction and printing +class CommandHelp { + + const CommandHelp._(this.key, this.description, [this.inParenthesis = '']); + + static const CommandHelp L = CommandHelp._('L', 'Dump layer tree to the console.', 'debugDumpLayerTree'); + static const CommandHelp P = CommandHelp._('P', 'Toggle performance overlay.', 'WidgetsApp.showPerformanceOverlay'); + static const CommandHelp R = CommandHelp._('R', 'Hot restart.'); + static const CommandHelp S = CommandHelp._('S', 'Dump accessibility tree in traversal order.', 'debugDumpSemantics'); + static const CommandHelp U = CommandHelp._('U', 'Dump accessibility tree in inverse hit test order.', 'debugDumpSemantics'); + static const CommandHelp a = CommandHelp._('a', 'Toggle timeline events for all widget build methods.', 'debugProfileWidgetBuilds'); + static const CommandHelp d = CommandHelp._('d', 'Detach (terminate "flutter run" but leave application running).'); + static const CommandHelp h = CommandHelp._('h', 'Repeat this help message.'); + static const CommandHelp i = CommandHelp._('i', 'Toggle widget inspector.', 'WidgetsApp.showWidgetInspectorOverride'); + static const CommandHelp o = CommandHelp._('o', 'Simulate different operating systems.', 'defaultTargetPlatform'); + static const CommandHelp p = CommandHelp._('p', 'Toggle the display of construction lines.', 'debugPaintSizeEnabled'); + static const CommandHelp q = CommandHelp._('q', 'Quit (terminate the application on the device).'); + static const CommandHelp r = CommandHelp._('r', 'Hot reload. $fire$fire$fire'); + static const CommandHelp s = CommandHelp._('s', 'Save a screenshot to flutter.png.'); + static const CommandHelp t = CommandHelp._('t', 'Dump rendering tree to the console.', 'debugDumpRenderTree'); + static const CommandHelp w = CommandHelp._('w', 'Dump widget hierarchy to the console.', 'debugDumpApp'); + static const CommandHelp z = CommandHelp._('z', 'Toggle elevation checker.'); + + /// The key associated with this command + final String key; + /// A description of what this command does + final String description; + /// Text shown in parenthesis to give the context + final String inParenthesis; + + bool get _hasTextInParenthesis => inParenthesis != null && inParenthesis.isNotEmpty; + + int get _rawMessageLength => key.length + description.length; + + @override + String toString() { + final StringBuffer message = StringBuffer(); + message.writeAll([globals.terminal.bolden(key), description], ' '); + + if (_hasTextInParenthesis) { + bool wrap = false; + final int maxWidth = math.max(outputPreferences.wrapColumn ?? 0, maxLineWidth); + int width = maxWidth - (globals.platform.stdoutSupportsAnsi ? _rawMessageLength + 1 : message.length); + final String parentheticalText = '($inParenthesis)'; + if (width < parentheticalText.length) { + width = maxWidth; + wrap = true; + } + + if (wrap) { + message.write('\n'); + } + // pad according to the raw text + message.write(''.padLeft(width - parentheticalText.length)); + + message.write(globals.terminal.color(parentheticalText, TerminalColor.grey)); + } + return message.toString(); + } + + void print() { + globals.printStatus(toString()); + } +} diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart index 9e4669447f..1ce14f4cb2 100644 --- a/packages/flutter_tools/lib/src/resident_runner.dart +++ b/packages/flutter_tools/lib/src/resident_runner.dart @@ -9,6 +9,7 @@ import 'package:meta/meta.dart'; import 'application_package.dart'; import 'artifacts.dart'; import 'asset.dart'; +import 'base/command_help.dart'; import 'base/common.dart'; import 'base/file_system.dart'; import 'base/io.dart' as io; @@ -1003,23 +1004,30 @@ abstract class ResidentRunner { void printHelp({ @required bool details }); void printHelpDetails() { + if (flutterDevices.any((FlutterDevice d) => d.device.supportsScreenshot)) { + CommandHelp.s.print(); + } if (supportsServiceProtocol) { - globals.printStatus('You can dump the widget hierarchy of the app (debugDumpApp) by pressing "w".'); - globals.printStatus('To dump the rendering tree of the app (debugDumpRenderTree), press "t".'); + CommandHelp.w.print(); + CommandHelp.t.print(); if (isRunningDebug) { - globals.printStatus('For layers (debugDumpLayerTree), use "L"; for accessibility (debugDumpSemantics), use "S" (for traversal order) or "U" (for inverse hit test order).'); - globals.printStatus('To toggle the widget inspector (WidgetsApp.showWidgetInspectorOverride), press "i".'); - globals.printStatus('To toggle the display of construction lines (debugPaintSizeEnabled), press "p".'); - globals.printStatus('To simulate different operating systems, (defaultTargetPlatform), press "o".'); - globals.printStatus('To toggle the elevation checker, press "z".'); + CommandHelp.L.print(); + CommandHelp.S.print(); + CommandHelp.U.print(); + CommandHelp.i.print(); + CommandHelp.p.print(); + CommandHelp.o.print(); + CommandHelp.z.print(); } else { - globals.printStatus('To dump the accessibility tree (debugDumpSemantics), press "S" (for traversal order) or "U" (for inverse hit test order).'); + CommandHelp.S.print(); + CommandHelp.U.print(); } - globals.printStatus('To display the performance overlay (WidgetsApp.showPerformanceOverlay), press "P".'); - globals.printStatus('To enable timeline events for all widget build methods, (debugProfileWidgetBuilds), press "a"'); + // `P` should precede `a` + CommandHelp.P.print(); + CommandHelp.a.print(); } if (flutterDevices.any((FlutterDevice d) => d.device.supportsScreenshot)) { - globals.printStatus('To save a screenshot to flutter.png, press "s".'); + CommandHelp.s.print(); } } diff --git a/packages/flutter_tools/lib/src/run_cold.dart b/packages/flutter_tools/lib/src/run_cold.dart index 12114527ca..26405b3fe5 100644 --- a/packages/flutter_tools/lib/src/run_cold.dart +++ b/packages/flutter_tools/lib/src/run_cold.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'package:meta/meta.dart'; +import 'base/command_help.dart'; import 'base/file_system.dart'; import 'device.dart'; import 'globals.dart' as globals; @@ -179,32 +180,27 @@ class ColdRunner extends ResidentRunner { @override void printHelp({ @required bool details }) { - bool haveDetails = false; - bool haveAnything = false; + globals.printStatus('Flutter run key commands.'); + if (supportsServiceProtocol) { + if (details) { + printHelpDetails(); + } + } + CommandHelp.h.print(); + if (_didAttach) { + CommandHelp.d.print(); + } + CommandHelp.q.print(); for (final FlutterDevice device in flutterDevices) { final String dname = device.device.name; if (device.vmService != null) { - globals.printStatus('An Observatory debugger and profiler on $dname is ' - 'available at: ${device.vmService .httpAddress}'); + // Caution: This log line is parsed by device lab tests. + globals.printStatus( + 'An Observatory debugger and profiler on $dname is available at: ' + '${device.vmService.httpAddress}', + ); } } - if (supportsServiceProtocol) { - haveDetails = true; - if (details) { - printHelpDetails(); - haveAnything = true; - } - } - final String quitMessage = _didAttach - ? 'To detach, press "d"; to quit, press "q".' - : 'To quit, press "q".'; - if (haveDetails && !details) { - globals.printStatus('For a more detailed help message, press "h". $quitMessage'); - } else if (haveAnything) { - globals.printStatus('To repeat this help message, press "h". $quitMessage'); - } else { - globals.printStatus(quitMessage); - } } @override diff --git a/packages/flutter_tools/lib/src/run_hot.dart b/packages/flutter_tools/lib/src/run_hot.dart index 2eadea5a9f..a5dd209893 100644 --- a/packages/flutter_tools/lib/src/run_hot.dart +++ b/packages/flutter_tools/lib/src/run_hot.dart @@ -9,12 +9,12 @@ import 'package:json_rpc_2/error_code.dart' as rpc_error_code; import 'package:json_rpc_2/json_rpc_2.dart' as rpc; import 'package:meta/meta.dart'; import 'package:pool/pool.dart'; - import 'base/async_guard.dart'; + +import 'base/command_help.dart'; import 'base/context.dart'; import 'base/file_system.dart'; import 'base/logger.dart'; -import 'base/terminal.dart'; import 'base/utils.dart'; import 'build_info.dart'; import 'compile.dart'; @@ -1045,29 +1045,26 @@ class HotRunner extends ResidentRunner { @override void printHelp({ @required bool details }) { - const String fire = '🔥'; - String rawMessage = ' To hot reload changes while running, press "r". '; + globals.printStatus('Flutter run key commands.'); + CommandHelp.r.print(); if (canHotRestart) { - rawMessage += 'To hot restart (and rebuild state), press "R".'; + CommandHelp.R.print(); } - final String message = globals.terminal.color( - fire + globals.terminal.bolden(rawMessage), - TerminalColor.red, - ); - globals.printStatus(message); - for (final FlutterDevice device in flutterDevices) { - final String dname = device.device.name; - globals.printStatus('An Observatory debugger and profiler on $dname is ' - 'available at: ${device.vmService.httpAddress}'); + CommandHelp.h.print(); + if (_didAttach) { + CommandHelp.d.print(); } - final String quitMessage = _didAttach - ? 'To detach, press "d"; to quit, press "q".' - : 'To quit, press "q".'; + CommandHelp.q.print(); if (details) { printHelpDetails(); - globals.printStatus('To repeat this help message, press "h". $quitMessage'); - } else { - globals.printStatus('For a more detailed help message, press "h". $quitMessage'); + } + for (final FlutterDevice device in flutterDevices) { + final String dname = device.device.name; + // Caution: This log line is parsed by device lab tests. + globals.printStatus( + 'An Observatory debugger and profiler on $dname is available at: ' + '${device.vmService.httpAddress}', + ); } } diff --git a/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart index 3ce4cd68cf..8e589abcd3 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart @@ -165,7 +165,10 @@ void main() { if (message == '[stdout] Lost connection to device.') { observatoryLogs.add(message); } - if (message.contains('To hot reload changes while running, press "r". To hot restart (and rebuild state), press "R".')) { + if (message.contains('Hot reload.')) { + observatoryLogs.add(message); + } + if (message.contains('Hot restart.')) { observatoryLogs.add(message); } }); @@ -203,14 +206,16 @@ void main() { })); }); - expect(observatoryLogs.length, 7); + expect(observatoryLogs.length, 9); expect(observatoryLogs[0], '[stdout] Waiting for a connection from Flutter on MockAndroidDevice...'); expect(observatoryLogs[1], '[verbose] Observatory URL on device: http://127.0.0.1:1234'); expect(observatoryLogs[2], '[stdout] Lost connection to device.'); - expect(observatoryLogs[3].contains('To hot reload changes while running, press "r". To hot restart (and rebuild state), press "R"'), isTrue); - expect(observatoryLogs[4], '[verbose] Observatory URL on device: http://127.0.0.1:1235'); - expect(observatoryLogs[5], '[stdout] Lost connection to device.'); - expect(observatoryLogs[6].contains('To hot reload changes while running, press "r". To hot restart (and rebuild state), press "R"'), isTrue); + expect(observatoryLogs[3].contains('Hot reload.'), isTrue); + expect(observatoryLogs[4].contains('Hot restart.'), isTrue); + expect(observatoryLogs[5], '[verbose] Observatory URL on device: http://127.0.0.1:1235'); + expect(observatoryLogs[6], '[stdout] Lost connection to device.'); + expect(observatoryLogs[7].contains('Hot reload.'), isTrue); + expect(observatoryLogs[8].contains('Hot restart.'), isTrue); verify(portForwarder.forward(1234, hostPort: anyNamed('hostPort'))).called(1); verify(portForwarder.forward(1235, hostPort: anyNamed('hostPort'))).called(1); diff --git a/packages/flutter_tools/test/general.shard/base/command_help_test.dart b/packages/flutter_tools/test/general.shard/base/command_help_test.dart new file mode 100644 index 0000000000..5fdbc84a58 --- /dev/null +++ b/packages/flutter_tools/test/general.shard/base/command_help_test.dart @@ -0,0 +1,176 @@ +// Copyright 2014 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 'package:flutter_tools/src/base/command_help.dart'; +import 'package:flutter_tools/src/base/terminal.dart' show OutputPreferences, outputPreferences; +import 'package:flutter_tools/src/globals.dart' as globals; +import 'package:mockito/mockito.dart'; +import 'package:platform/platform.dart'; + +import '../../src/common.dart'; +import '../../src/context.dart'; + +// Used to use the message length in different scenarios in a DRY way +Future Function() _testMessageLength(bool stdoutSupportsAnsi, int maxTestLineLength) => () async { + when(globals.platform.stdoutSupportsAnsi).thenReturn(stdoutSupportsAnsi); + + int expectedWidth = maxTestLineLength; + + if (stdoutSupportsAnsi) { + const int ansiMetaCharactersLength = 33; + expectedWidth += ansiMetaCharactersLength; + } + + expect(CommandHelp.L.toString().length, lessThanOrEqualTo(expectedWidth)); + expect(CommandHelp.P.toString().length, lessThanOrEqualTo(expectedWidth)); + expect(CommandHelp.R.toString().length, lessThanOrEqualTo(expectedWidth)); + expect(CommandHelp.S.toString().length, lessThanOrEqualTo(expectedWidth)); + expect(CommandHelp.U.toString().length, lessThanOrEqualTo(expectedWidth)); + expect(CommandHelp.a.toString().length, lessThanOrEqualTo(expectedWidth)); + expect(CommandHelp.d.toString().length, lessThanOrEqualTo(expectedWidth)); + expect(CommandHelp.h.toString().length, lessThanOrEqualTo(expectedWidth)); + expect(CommandHelp.i.toString().length, lessThanOrEqualTo(expectedWidth)); + expect(CommandHelp.o.toString().length, lessThanOrEqualTo(expectedWidth)); + expect(CommandHelp.p.toString().length, lessThanOrEqualTo(expectedWidth)); + expect(CommandHelp.q.toString().length, lessThanOrEqualTo(expectedWidth)); + expect(CommandHelp.r.toString().length, lessThanOrEqualTo(expectedWidth)); + expect(CommandHelp.s.toString().length, lessThanOrEqualTo(expectedWidth)); + expect(CommandHelp.t.toString().length, lessThanOrEqualTo(expectedWidth)); + expect(CommandHelp.w.toString().length, lessThanOrEqualTo(expectedWidth)); + expect(CommandHelp.z.toString().length, lessThanOrEqualTo(expectedWidth)); +}; + +void main() { + group('CommandHelp', () { + group('toString', () { + + testUsingContext('should have a bold command key', () async { + when(globals.platform.stdoutSupportsAnsi).thenReturn(true); + + expect(CommandHelp.L.toString(), startsWith('\x1B[1mL\x1B[22m')); + expect(CommandHelp.P.toString(), startsWith('\x1B[1mP\x1B[22m')); + expect(CommandHelp.R.toString(), startsWith('\x1B[1mR\x1B[22m')); + expect(CommandHelp.S.toString(), startsWith('\x1B[1mS\x1B[22m')); + expect(CommandHelp.U.toString(), startsWith('\x1B[1mU\x1B[22m')); + expect(CommandHelp.a.toString(), startsWith('\x1B[1ma\x1B[22m')); + expect(CommandHelp.d.toString(), startsWith('\x1B[1md\x1B[22m')); + expect(CommandHelp.h.toString(), startsWith('\x1B[1mh\x1B[22m')); + expect(CommandHelp.i.toString(), startsWith('\x1B[1mi\x1B[22m')); + expect(CommandHelp.o.toString(), startsWith('\x1B[1mo\x1B[22m')); + expect(CommandHelp.p.toString(), startsWith('\x1B[1mp\x1B[22m')); + expect(CommandHelp.q.toString(), startsWith('\x1B[1mq\x1B[22m')); + expect(CommandHelp.r.toString(), startsWith('\x1B[1mr\x1B[22m')); + expect(CommandHelp.s.toString(), startsWith('\x1B[1ms\x1B[22m')); + expect(CommandHelp.t.toString(), startsWith('\x1B[1mt\x1B[22m')); + expect(CommandHelp.w.toString(), startsWith('\x1B[1mw\x1B[22m')); + expect(CommandHelp.z.toString(), startsWith('\x1B[1mz\x1B[22m')); + }, overrides: { + OutputPreferences: () => OutputPreferences(wrapColumn: maxLineWidth), + Platform: () => MockPlatform(), + }); + + testUsingContext('commands L,P,S,U,a,i,o,p,t,w should have a grey bolden parenthetical text', () async { + when(globals.platform.stdoutSupportsAnsi).thenReturn(true); + + expect(CommandHelp.L.toString(), endsWith('\x1B[1;30m(debugDumpLayerTree)\x1B[39m')); + expect(CommandHelp.P.toString(), endsWith('\x1B[1;30m(WidgetsApp.showPerformanceOverlay)\x1B[39m')); + expect(CommandHelp.S.toString(), endsWith('\x1B[1;30m(debugDumpSemantics)\x1B[39m')); + expect(CommandHelp.U.toString(), endsWith('\x1B[1;30m(debugDumpSemantics)\x1B[39m')); + expect(CommandHelp.a.toString(), endsWith('\x1B[1;30m(debugProfileWidgetBuilds)\x1B[39m')); + expect(CommandHelp.i.toString(), endsWith('\x1B[1;30m(WidgetsApp.showWidgetInspectorOverride)\x1B[39m')); + expect(CommandHelp.o.toString(), endsWith('\x1B[1;30m(defaultTargetPlatform)\x1B[39m')); + expect(CommandHelp.p.toString(), endsWith('\x1B[1;30m(debugPaintSizeEnabled)\x1B[39m')); + expect(CommandHelp.t.toString(), endsWith('\x1B[1;30m(debugDumpRenderTree)\x1B[39m')); + expect(CommandHelp.w.toString(), endsWith('\x1B[1;30m(debugDumpApp)\x1B[39m')); + }, overrides: { + OutputPreferences: () => OutputPreferences(wrapColumn: maxLineWidth), + Platform: () => MockPlatform(), + }); + + testUsingContext('should not create a help text longer than maxLineWidth without ansi support', + _testMessageLength(false, maxLineWidth), + overrides: { + OutputPreferences: () => OutputPreferences(wrapColumn: 0), + Platform: () => MockPlatform(), + }); + + testUsingContext('should not create a help text longer than maxLineWidth with ansi support', + _testMessageLength(true, maxLineWidth), + overrides: { + OutputPreferences: () => OutputPreferences(wrapColumn: 0), + Platform: () => MockPlatform(), + }); + + testUsingContext('should not create a help text longer than outputPreferences.wrapColumn without ansi support', + _testMessageLength(false, outputPreferences.wrapColumn), + overrides: { + Platform: () => MockPlatform(), + }); + + testUsingContext('should not create a help text longer than outputPreferences.wrapColumn with ansi support', + _testMessageLength(true, outputPreferences.wrapColumn), + overrides: { + Platform: () => MockPlatform(), + }); + + testUsingContext('should create the correct help text with ansi support', () async { + when(globals.platform.stdoutSupportsAnsi).thenReturn(true); + + expect(CommandHelp.L.toString(), equals('\x1B[1mL\x1B[22m Dump layer tree to the console. \x1B[1;30m(debugDumpLayerTree)\x1B[39m')); + expect(CommandHelp.P.toString(), equals('\x1B[1mP\x1B[22m Toggle performance overlay. \x1B[1;30m(WidgetsApp.showPerformanceOverlay)\x1B[39m')); + expect(CommandHelp.R.toString(), equals('\x1B[1mR\x1B[22m Hot restart.')); + expect(CommandHelp.S.toString(), equals('\x1B[1mS\x1B[22m Dump accessibility tree in traversal order. \x1B[1;30m(debugDumpSemantics)\x1B[39m')); + expect(CommandHelp.U.toString(), equals('\x1B[1mU\x1B[22m Dump accessibility tree in inverse hit test order. \x1B[1;30m(debugDumpSemantics)\x1B[39m')); + expect(CommandHelp.a.toString(), equals('\x1B[1ma\x1B[22m Toggle timeline events for all widget build methods. \x1B[1;30m(debugProfileWidgetBuilds)\x1B[39m')); + expect(CommandHelp.d.toString(), equals('\x1B[1md\x1B[22m Detach (terminate "flutter run" but leave application running).')); + expect(CommandHelp.h.toString(), equals('\x1B[1mh\x1B[22m Repeat this help message.')); + expect(CommandHelp.i.toString(), equals('\x1B[1mi\x1B[22m Toggle widget inspector. \x1B[1;30m(WidgetsApp.showWidgetInspectorOverride)\x1B[39m')); + expect(CommandHelp.o.toString(), equals('\x1B[1mo\x1B[22m Simulate different operating systems. \x1B[1;30m(defaultTargetPlatform)\x1B[39m')); + expect(CommandHelp.p.toString(), equals('\x1B[1mp\x1B[22m Toggle the display of construction lines. \x1B[1;30m(debugPaintSizeEnabled)\x1B[39m')); + expect(CommandHelp.q.toString(), equals('\x1B[1mq\x1B[22m Quit (terminate the application on the device).')); + expect(CommandHelp.r.toString(), equals('\x1B[1mr\x1B[22m Hot reload. $fire$fire$fire')); + expect(CommandHelp.s.toString(), equals('\x1B[1ms\x1B[22m Save a screenshot to flutter.png.')); + expect(CommandHelp.t.toString(), equals('\x1B[1mt\x1B[22m Dump rendering tree to the console. \x1B[1;30m(debugDumpRenderTree)\x1B[39m')); + expect(CommandHelp.w.toString(), equals('\x1B[1mw\x1B[22m Dump widget hierarchy to the console. \x1B[1;30m(debugDumpApp)\x1B[39m')); + expect(CommandHelp.z.toString(), equals('\x1B[1mz\x1B[22m Toggle elevation checker.')); + }, overrides: { + OutputPreferences: () => OutputPreferences(wrapColumn: maxLineWidth), + Platform: () => MockPlatform(), + }); + + testUsingContext('should create the correct help text without ansi support', () async { + when(globals.platform.stdoutSupportsAnsi).thenReturn(false); + + expect(CommandHelp.L.toString(), equals('L Dump layer tree to the console. (debugDumpLayerTree)')); + expect(CommandHelp.P.toString(), equals('P Toggle performance overlay. (WidgetsApp.showPerformanceOverlay)')); + expect(CommandHelp.R.toString(), equals('R Hot restart.')); + expect(CommandHelp.S.toString(), equals('S Dump accessibility tree in traversal order. (debugDumpSemantics)')); + expect(CommandHelp.U.toString(), equals('U Dump accessibility tree in inverse hit test order. (debugDumpSemantics)')); + expect(CommandHelp.a.toString(), equals('a Toggle timeline events for all widget build methods. (debugProfileWidgetBuilds)')); + expect(CommandHelp.d.toString(), equals('d Detach (terminate "flutter run" but leave application running).')); + expect(CommandHelp.h.toString(), equals('h Repeat this help message.')); + expect(CommandHelp.i.toString(), equals('i Toggle widget inspector. (WidgetsApp.showWidgetInspectorOverride)')); + expect(CommandHelp.o.toString(), equals('o Simulate different operating systems. (defaultTargetPlatform)')); + expect(CommandHelp.p.toString(), equals('p Toggle the display of construction lines. (debugPaintSizeEnabled)')); + expect(CommandHelp.q.toString(), equals('q Quit (terminate the application on the device).')); + expect(CommandHelp.r.toString(), equals('r Hot reload. $fire$fire$fire')); + expect(CommandHelp.s.toString(), equals('s Save a screenshot to flutter.png.')); + expect(CommandHelp.t.toString(), equals('t Dump rendering tree to the console. (debugDumpRenderTree)')); + expect(CommandHelp.w.toString(), equals('w Dump widget hierarchy to the console. (debugDumpApp)')); + expect(CommandHelp.z.toString(), equals('z Toggle elevation checker.')); + }, overrides: { + OutputPreferences: () => OutputPreferences(wrapColumn: maxLineWidth), + Platform: () => MockPlatform(), + }); + + }); + }); +} + +class MockPlatform extends Mock implements Platform { + @override + Map environment = { + 'FLUTTER_ROOT': '/', + }; +} diff --git a/packages/flutter_tools/test/general.shard/resident_runner_test.dart b/packages/flutter_tools/test/general.shard/resident_runner_test.dart index 19c4593f47..1e33692e89 100644 --- a/packages/flutter_tools/test/general.shard/resident_runner_test.dart +++ b/packages/flutter_tools/test/general.shard/resident_runner_test.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'package:file/memory.dart'; import 'package:flutter_tools/src/artifacts.dart'; +import 'package:flutter_tools/src/base/command_help.dart'; import 'package:flutter_tools/src/base/common.dart'; import 'package:flutter_tools/src/base/context.dart'; import 'package:flutter_tools/src/base/file_system.dart'; @@ -365,21 +366,33 @@ void main() { // supports service protocol expect(residentRunner.supportsServiceProtocol, true); - expect(testLogger.statusText, contains('"w"')); - expect(testLogger.statusText, contains('"t"')); - expect(testLogger.statusText, contains('"P"')); - expect(testLogger.statusText, contains('"a"')); // isRunningDebug expect(residentRunner.isRunningDebug, true); - expect(testLogger.statusText, contains('"L"')); - expect(testLogger.statusText, contains('"S"')); - expect(testLogger.statusText, contains('"U"')); - expect(testLogger.statusText, contains('"i"')); - expect(testLogger.statusText, contains('"p"')); - expect(testLogger.statusText, contains('"o"')); - expect(testLogger.statusText, contains('"z"')); - // screenshot - expect(testLogger.statusText, contains('"s"')); + // commands + expect(testLogger.statusText, equals( + [ + 'Flutter run key commands.', + CommandHelp.r, + CommandHelp.R, + CommandHelp.h, + CommandHelp.q, + CommandHelp.s, + CommandHelp.w, + CommandHelp.t, + CommandHelp.L, + CommandHelp.S, + CommandHelp.U, + CommandHelp.i, + CommandHelp.p, + CommandHelp.o, + CommandHelp.z, + CommandHelp.P, + CommandHelp.a, + CommandHelp.s, + 'An Observatory debugger and profiler on null is available at: null', + '' + ].join('\n') + )); })); test('ResidentRunner can take screenshot on debug device', () => testbed.run(() async {