Unverified Commit f7b8d62c authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tool] Refactor Logger and Terminal to (mostly) no longer depend...

[flutter_tool] Refactor Logger and Terminal to (mostly) no longer depend directly on context (#47269)
parent 0384c8c4
......@@ -15,6 +15,7 @@ import 'src/base/file_system.dart';
import 'src/base/io.dart';
import 'src/base/logger.dart';
import 'src/base/process.dart';
import 'src/base/terminal.dart';
import 'src/base/utils.dart';
import 'src/context_runner.dart';
import 'src/doctor.dart';
......@@ -223,7 +224,10 @@ Future<File> _createLocalCrashReport(List<String> args, dynamic error, StackTrac
Future<String> _doctorText() async {
try {
final BufferLogger logger = BufferLogger();
final BufferLogger logger = BufferLogger(
terminal: terminal,
outputPreferences: outputPreferences,
);
await context.run<bool>(
body: () => doctor.diagnose(verbose: true, showColor: false),
......
......@@ -247,11 +247,10 @@ class Stdio {
bool get supportsAnsiEscapes => hasTerminal && io.stdout.supportsAnsiEscapes;
}
Stdio get stdio => context.get<Stdio>() ?? const Stdio();
io.Stdout get stdout => stdio.stdout;
Stream<List<int>> get stdin => stdio.stdin;
io.IOSink get stderr => stdio.stderr;
bool get stdinHasTerminal => stdio.stdinHasTerminal;
io.Stdout get stdout => globals.stdio.stdout;
Stream<List<int>> get stdin => globals.stdio.stdin;
io.IOSink get stderr => globals.stdio.stderr;
bool get stdinHasTerminal => globals.stdio.stdinHasTerminal;
/// An overridable version of io.ProcessInfo.
abstract class ProcessInfo {
......
......@@ -8,8 +8,8 @@ import 'package:meta/meta.dart';
import '../base/context.dart';
import '../globals.dart' as globals;
import 'io.dart';
import 'terminal.dart';
import 'io.dart' hide stderr, stdin, stdout;
import 'terminal.dart' show AnsiTerminal, TerminalColor, OutputPreferences;
import 'utils.dart';
const int kDefaultStatusPadding = 59;
......@@ -44,9 +44,15 @@ abstract class Logger {
bool quiet = false;
bool get supportsColor => globals.terminal.supportsColor;
bool get supportsColor;
bool get hasTerminal => stdio.hasTerminal;
bool get hasTerminal;
AnsiTerminal get _terminal;
OutputPreferences get _outputPreferences;
TimeoutConfiguration get _timeoutConfiguration;
/// Display an error `message` to the user. Commands should use this if they
/// fail in some way.
......@@ -155,11 +161,36 @@ abstract class Logger {
}
class StdoutLogger extends Logger {
StdoutLogger({
@required AnsiTerminal terminal,
@required Stdio stdio,
@required OutputPreferences outputPreferences,
@required TimeoutConfiguration timeoutConfiguration,
})
: _stdio = stdio,
_terminal = terminal,
_timeoutConfiguration = timeoutConfiguration,
_outputPreferences = outputPreferences;
@override
final AnsiTerminal _terminal;
@override
final OutputPreferences _outputPreferences;
@override
final TimeoutConfiguration _timeoutConfiguration;
final Stdio _stdio;
Status _status;
@override
bool get isVerbose => false;
@override
bool get supportsColor => _terminal.supportsColor;
@override
bool get hasTerminal => _stdio.stdinHasTerminal;
@override
void printError(
String message, {
......@@ -172,14 +203,19 @@ class StdoutLogger extends Logger {
}) {
_status?.pause();
message ??= '';
message = wrapText(message, indent: indent, hangingIndent: hangingIndent, shouldWrap: wrap);
message = wrapText(message,
indent: indent,
hangingIndent: hangingIndent,
shouldWrap: wrap ?? _outputPreferences.wrapText,
columnWidth: _outputPreferences.wrapColumn,
);
if (emphasis == true) {
message = globals.terminal.bolden(message);
message = _terminal.bolden(message);
}
message = globals.terminal.color(message, color ?? TerminalColor.red);
stderr.writeln(message);
message = _terminal.color(message, color ?? TerminalColor.red);
_stdio.stderr.writeln(message);
if (stackTrace != null) {
stderr.writeln(stackTrace.toString());
_stdio.stderr.writeln(stackTrace.toString());
}
_status?.resume();
}
......@@ -196,12 +232,17 @@ class StdoutLogger extends Logger {
}) {
_status?.pause();
message ??= '';
message = wrapText(message, indent: indent, hangingIndent: hangingIndent, shouldWrap: wrap);
message = wrapText(message,
indent: indent,
hangingIndent: hangingIndent,
shouldWrap: wrap ?? _outputPreferences.wrapText,
columnWidth: _outputPreferences.wrapColumn,
);
if (emphasis == true) {
message = globals.terminal.bolden(message);
message = _terminal.bolden(message);
}
if (color != null) {
message = globals.terminal.color(message, color);
message = _terminal.color(message, color);
}
if (newline != false) {
message = '$message\n';
......@@ -212,7 +253,7 @@ class StdoutLogger extends Logger {
@protected
void writeToStdOut(String message) {
stdout.write(message);
_stdio.stdout.write(message);
}
@override
......@@ -232,15 +273,18 @@ class StdoutLogger extends Logger {
return SilentStatus(
timeout: timeout,
onFinish: _clearStatus,
timeoutConfiguration: _timeoutConfiguration
)..start();
}
if (globals.terminal.supportsColor) {
if (supportsColor) {
_status = AnsiStatus(
message: message,
timeout: timeout,
multilineOutput: multilineOutput,
padding: progressIndicatorPadding,
onFinish: _clearStatus,
stdio: _stdio,
timeoutConfiguration: _timeoutConfiguration,
)..start();
} else {
_status = SummaryStatus(
......@@ -248,6 +292,8 @@ class StdoutLogger extends Logger {
timeout: timeout,
padding: progressIndicatorPadding,
onFinish: _clearStatus,
stdio: _stdio,
timeoutConfiguration: _timeoutConfiguration,
)..start();
}
return _status;
......@@ -270,10 +316,22 @@ class StdoutLogger extends Logger {
/// fonts, should be replaced by this class with printable symbols. Otherwise,
/// they will show up as the unrepresentable character symbol '�'.
class WindowsStdoutLogger extends StdoutLogger {
WindowsStdoutLogger({
@required AnsiTerminal terminal,
@required Stdio stdio,
@required OutputPreferences outputPreferences,
@required TimeoutConfiguration timeoutConfiguration,
}) : super(
terminal: terminal,
stdio: stdio,
outputPreferences: outputPreferences,
timeoutConfiguration: timeoutConfiguration,
);
@override
void writeToStdOut(String message) {
// TODO(jcollins-g): wrong abstraction layer for this, move to [Stdio].
stdout.write(message
_stdio.stdout.write(message
.replaceAll('✗', 'X')
.replaceAll('✓', '√')
);
......@@ -281,9 +339,29 @@ class WindowsStdoutLogger extends StdoutLogger {
}
class BufferLogger extends Logger {
BufferLogger({
@required AnsiTerminal terminal,
@required OutputPreferences outputPreferences,
TimeoutConfiguration timeoutConfiguration = const TimeoutConfiguration()
}) : _outputPreferences = outputPreferences,
_terminal = terminal,
_timeoutConfiguration = timeoutConfiguration;
@override
final OutputPreferences _outputPreferences;
@override
final AnsiTerminal _terminal;
@override
final TimeoutConfiguration _timeoutConfiguration;
@override
bool get isVerbose => false;
@override
bool get supportsColor => _terminal.supportsColor;
final StringBuffer _error = StringBuffer();
final StringBuffer _status = StringBuffer();
final StringBuffer _trace = StringBuffer();
......@@ -305,8 +383,13 @@ class BufferLogger extends Logger {
int hangingIndent,
bool wrap,
}) {
_error.writeln(globals.terminal.color(
wrapText(message, indent: indent, hangingIndent: hangingIndent, shouldWrap: wrap),
_error.writeln(_terminal.color(
wrapText(message,
indent: indent,
hangingIndent: hangingIndent,
shouldWrap: wrap ?? _outputPreferences.wrapText,
columnWidth: _outputPreferences.wrapColumn,
),
color ?? TerminalColor.red,
));
}
......@@ -322,9 +405,19 @@ class BufferLogger extends Logger {
bool wrap,
}) {
if (newline != false) {
_status.writeln(wrapText(message, indent: indent, hangingIndent: hangingIndent, shouldWrap: wrap));
_status.writeln(wrapText(message,
indent: indent,
hangingIndent: hangingIndent,
shouldWrap: wrap ?? _outputPreferences.wrapText,
columnWidth: _outputPreferences.wrapColumn,
));
} else {
_status.write(wrapText(message, indent: indent, hangingIndent: hangingIndent, shouldWrap: wrap));
_status.write(wrapText(message,
indent: indent,
hangingIndent: hangingIndent,
shouldWrap: wrap ?? _outputPreferences.wrapText,
columnWidth: _outputPreferences.wrapColumn,
));
}
}
......@@ -341,7 +434,11 @@ class BufferLogger extends Logger {
}) {
assert(progressIndicatorPadding != null);
printStatus(message);
return SilentStatus(timeout: timeout)..start();
return SilentStatus(
timeout: timeout,
timeoutConfiguration: _timeoutConfiguration,
)..start();
}
/// Clears all buffers.
......@@ -356,13 +453,23 @@ class BufferLogger extends Logger {
}
class VerboseLogger extends Logger {
VerboseLogger(this.parent) : assert(globals.terminal != null) {
VerboseLogger(this.parent, { @required Stopwatch stopwatch }) :
_stopwatch = stopwatch ?? context.get<Stopwatch>() ?? Stopwatch() {
_stopwatch.start();
}
final Logger parent;
final Stopwatch _stopwatch = context.get<Stopwatch>() ?? Stopwatch();
final Stopwatch _stopwatch;
@override
AnsiTerminal get _terminal => parent._terminal;
@override
OutputPreferences get _outputPreferences => parent._outputPreferences;
@override
TimeoutConfiguration get _timeoutConfiguration => parent._timeoutConfiguration;
@override
bool get isVerbose => true;
......@@ -379,7 +486,12 @@ class VerboseLogger extends Logger {
}) {
_emit(
_LogType.error,
wrapText(message, indent: indent, hangingIndent: hangingIndent, shouldWrap: wrap),
wrapText(message,
indent: indent,
hangingIndent: hangingIndent,
shouldWrap: wrap ?? _outputPreferences.wrapText,
columnWidth: _outputPreferences.wrapColumn,
),
stackTrace,
);
}
......@@ -394,7 +506,12 @@ class VerboseLogger extends Logger {
int hangingIndent,
bool wrap,
}) {
_emit(_LogType.status, wrapText(message, indent: indent, hangingIndent: hangingIndent, shouldWrap: wrap));
_emit(_LogType.status, wrapText(message,
indent: indent,
hangingIndent: hangingIndent,
shouldWrap: wrap ?? _outputPreferences.wrapText,
columnWidth: _outputPreferences.wrapColumn,
));
}
@override
......@@ -415,9 +532,10 @@ class VerboseLogger extends Logger {
final Stopwatch timer = Stopwatch()..start();
return SilentStatus(
timeout: timeout,
timeoutConfiguration: _timeoutConfiguration,
onFinish: () {
String time;
if (timeout == null || timeout > timeoutConfiguration.fastOperation) {
if (timeout == null || timeout > _timeoutConfiguration.fastOperation) {
time = getElapsedAsSeconds(timer.elapsed);
} else {
time = getElapsedAsMilliseconds(timer.elapsed);
......@@ -446,7 +564,7 @@ class VerboseLogger extends Logger {
} else {
prefix = '+$millis ms'.padLeft(prefixWidth);
if (millis >= 100) {
prefix = globals.terminal.bolden(prefix);
prefix = _terminal.bolden(prefix);
}
}
prefix = '[$prefix] ';
......@@ -455,12 +573,12 @@ class VerboseLogger extends Logger {
final String indentMessage = message.replaceAll('\n', '\n$indent');
if (type == _LogType.error) {
parent.printError(prefix + globals.terminal.bolden(indentMessage));
parent.printError(prefix + _terminal.bolden(indentMessage));
if (stackTrace != null) {
parent.printError(indent + stackTrace.toString().replaceAll('\n', '\n$indent'));
}
} else if (type == _LogType.status) {
parent.printStatus(prefix + globals.terminal.bolden(indentMessage));
parent.printStatus(prefix + _terminal.bolden(indentMessage));
} else {
parent.printStatus(prefix + indentMessage);
}
......@@ -468,6 +586,12 @@ class VerboseLogger extends Logger {
@override
void sendEvent(String name, [Map<String, dynamic> args]) { }
@override
bool get supportsColor => parent.supportsColor;
@override
bool get hasTerminal => parent.hasTerminal;
}
enum _LogType { error, status, trace }
......@@ -495,12 +619,17 @@ typedef SlowWarningCallback = String Function();
/// Generally, consider `logger.startProgress` instead of directly creating
/// a [Status] or one of its subclasses.
abstract class Status {
Status({ @required this.timeout, this.onFinish });
Status({
@required this.timeout,
@required TimeoutConfiguration timeoutConfiguration,
this.onFinish,
}) : _timeoutConfiguration = timeoutConfiguration;
/// A [SilentStatus] or an [AnsiSpinner] (depending on whether the
/// terminal is fancy enough), already started.
factory Status.withSpinner({
@required Duration timeout,
@required TimeoutConfiguration timeoutConfiguration,
VoidCallback onFinish,
SlowWarningCallback slowWarningCallback,
}) {
......@@ -509,13 +638,19 @@ abstract class Status {
timeout: timeout,
onFinish: onFinish,
slowWarningCallback: slowWarningCallback,
timeoutConfiguration: timeoutConfiguration,
)..start();
}
return SilentStatus(timeout: timeout, onFinish: onFinish)..start();
return SilentStatus(
timeout: timeout,
onFinish: onFinish,
timeoutConfiguration: timeoutConfiguration,
)..start();
}
final Duration timeout;
final VoidCallback onFinish;
final TimeoutConfiguration _timeoutConfiguration;
@protected
final Stopwatch _stopwatch = context.get<Stopwatch>() ?? Stopwatch();
......@@ -526,7 +661,7 @@ abstract class Status {
@protected
String get elapsedTime {
if (timeout == null || timeout > timeoutConfiguration.fastOperation) {
if (timeout == null || timeout > _timeoutConfiguration.fastOperation) {
return getElapsedAsSeconds(_stopwatch.elapsed);
}
return getElapsedAsMilliseconds(_stopwatch.elapsed);
......@@ -568,8 +703,13 @@ abstract class Status {
class SilentStatus extends Status {
SilentStatus({
@required Duration timeout,
@required TimeoutConfiguration timeoutConfiguration,
VoidCallback onFinish,
}) : super(timeout: timeout, onFinish: onFinish);
}) : super(
timeout: timeout,
onFinish: onFinish,
timeoutConfiguration: timeoutConfiguration,
);
}
/// Constructor writes [message] to [stdout]. On [cancel] or [stop], will call
......@@ -580,12 +720,16 @@ class SummaryStatus extends Status {
@required Duration timeout,
this.padding = kDefaultStatusPadding,
VoidCallback onFinish,
Stdio stdio,
@required TimeoutConfiguration timeoutConfiguration,
}) : assert(message != null),
assert(padding != null),
super(timeout: timeout, onFinish: onFinish);
_stdio = stdio ?? globals.stdio,
super(timeout: timeout, onFinish: onFinish, timeoutConfiguration: timeoutConfiguration);
final String message;
final int padding;
final Stdio _stdio;
bool _messageShowingOnCurrentLine = false;
......@@ -597,7 +741,7 @@ class SummaryStatus extends Status {
void _printMessage() {
assert(!_messageShowingOnCurrentLine);
stdout.write('${message.padRight(padding)} ');
_stdio.stdout.write('${message.padRight(padding)} ');
_messageShowingOnCurrentLine = true;
}
......@@ -608,14 +752,14 @@ class SummaryStatus extends Status {
}
super.stop();
writeSummaryInformation();
stdout.write('\n');
_stdio.stdout.write('\n');
}
@override
void cancel() {
super.cancel();
if (_messageShowingOnCurrentLine) {
stdout.write('\n');
_stdio.stdout.write('\n');
}
}
......@@ -628,16 +772,16 @@ class SummaryStatus extends Status {
/// Examples: ` 0.5s`, ` 150ms`, ` 1,600ms`, ` 3.1s (!)`
void writeSummaryInformation() {
assert(_messageShowingOnCurrentLine);
stdout.write(elapsedTime.padLeft(_kTimePadding));
_stdio.stdout.write(elapsedTime.padLeft(_kTimePadding));
if (seemsSlow) {
stdout.write(' (!)');
_stdio.stdout.write(' (!)');
}
}
@override
void pause() {
super.pause();
stdout.write('\n');
_stdio.stdout.write('\n');
_messageShowingOnCurrentLine = false;
}
}
......@@ -652,10 +796,14 @@ class AnsiSpinner extends Status {
@required Duration timeout,
VoidCallback onFinish,
this.slowWarningCallback,
}) : super(timeout: timeout, onFinish: onFinish);
Stdio stdio,
@required TimeoutConfiguration timeoutConfiguration,
}) : _stdio = stdio ?? globals.stdio,
super(timeout: timeout, onFinish: onFinish, timeoutConfiguration: timeoutConfiguration);
final String _backspaceChar = '\b';
final String _clearChar = ' ';
final Stdio _stdio;
bool timedOut = false;
......@@ -688,7 +836,7 @@ class AnsiSpinner extends Status {
}
void _startSpinner() {
stdout.write(_clear); // for _callback to backspace over
_stdio.stdout.write(_clear); // for _callback to backspace over
timer = Timer.periodic(const Duration(milliseconds: 100), _callback);
_callback(timer);
}
......@@ -697,21 +845,21 @@ class AnsiSpinner extends Status {
assert(this.timer == timer);
assert(timer != null);
assert(timer.isActive);
stdout.write(_backspace);
_stdio.stdout.write(_backspace);
ticks += 1;
if (seemsSlow) {
if (!timedOut) {
timedOut = true;
stdout.write('$_clear\n');
_stdio.stdout.write('$_clear\n');
}
if (slowWarningCallback != null) {
_slowWarning = slowWarningCallback();
} else {
_slowWarning = _defaultSlowWarning;
}
stdout.write(_slowWarning);
_stdio.stdout.write(_slowWarning);
}
stdout.write('${_clearChar * spinnerIndent}$_currentAnimationFrame');
_stdio.stdout.write('${_clearChar * spinnerIndent}$_currentAnimationFrame');
}
@override
......@@ -725,7 +873,7 @@ class AnsiSpinner extends Status {
}
void _clearSpinner() {
stdout.write('$_backspace$_clear$_backspace');
_stdio.stdout.write('$_backspace$_clear$_backspace');
}
@override
......@@ -759,10 +907,12 @@ class AnsiStatus extends AnsiSpinner {
this.multilineOutput = false,
this.padding = kDefaultStatusPadding,
VoidCallback onFinish,
Stdio stdio,
TimeoutConfiguration timeoutConfiguration,
}) : assert(message != null),
assert(multilineOutput != null),
assert(padding != null),
super(timeout: timeout, onFinish: onFinish);
super(timeout: timeout, onFinish: onFinish, stdio: stdio, timeoutConfiguration: timeoutConfiguration);
final String message;
final bool multilineOutput;
......@@ -784,20 +934,20 @@ class AnsiStatus extends AnsiSpinner {
void _startStatus() {
final String line = '${message.padRight(padding)}$_margin';
_totalMessageLength = line.length;
stdout.write(line);
_stdio.stdout.write(line);
}
@override
void stop() {
super.stop();
writeSummaryInformation();
stdout.write('\n');
_stdio.stdout.write('\n');
}
@override
void cancel() {
super.cancel();
stdout.write('\n');
_stdio.stdout.write('\n');
}
/// Print summary information when a task is done.
......@@ -808,16 +958,16 @@ class AnsiStatus extends AnsiSpinner {
/// line before writing the elapsed time.
void writeSummaryInformation() {
if (multilineOutput) {
stdout.write('\n${'$message Done'.padRight(padding)}$_margin');
_stdio.stdout.write('\n${'$message Done'.padRight(padding)}$_margin');
}
stdout.write(elapsedTime.padLeft(_kTimePadding));
_stdio.stdout.write(elapsedTime.padLeft(_kTimePadding));
if (seemsSlow) {
stdout.write(' (!)');
_stdio.stdout.write(' (!)');
}
}
void _clearStatus() {
stdout.write('${_backspaceChar * _totalMessageLength}${_clearChar * _totalMessageLength}${_backspaceChar * _totalMessageLength}');
_stdio.stdout.write('${_backspaceChar * _totalMessageLength}${_clearChar * _totalMessageLength}${_backspaceChar * _totalMessageLength}');
}
@override
......
......@@ -4,10 +4,14 @@
import 'dart:async';
import 'package:meta/meta.dart';
import 'package:platform/platform.dart';
import '../convert.dart';
import '../globals.dart' as globals;
import 'context.dart';
import 'io.dart' as io;
import 'logger.dart';
enum TerminalColor {
red,
......@@ -41,12 +45,13 @@ class OutputPreferences {
bool wrapText,
int wrapColumn,
bool showColor,
}) : wrapText = wrapText ?? io.stdio.hasTerminal,
}) : wrapText = wrapText ?? globals.stdio.hasTerminal,
_overrideWrapColumn = wrapColumn,
showColor = showColor ?? globals.platform.stdoutSupportsAnsi ?? false;
/// A version of this class for use in tests.
OutputPreferences.test() : wrapText = false, _overrideWrapColumn = null, showColor = false;
OutputPreferences.test({this.wrapText = false, int wrapColumn = kDefaultTerminalColumns, this.showColor = false})
: _overrideWrapColumn = wrapColumn;
/// If [wrapText] is true, then any text sent to the context's [Logger]
/// instance (e.g. from the [printError] or [printStatus] functions) will be
......@@ -66,7 +71,7 @@ class OutputPreferences {
/// terminal, or to [kDefaultTerminalColumns] if not writing to a terminal.
final int _overrideWrapColumn;
int get wrapColumn {
return _overrideWrapColumn ?? io.stdio.terminalColumns ?? kDefaultTerminalColumns;
return _overrideWrapColumn ?? globals.stdio.terminalColumns ?? kDefaultTerminalColumns;
}
/// Whether or not to output ANSI color codes when writing to the output
......@@ -81,6 +86,13 @@ class OutputPreferences {
}
class AnsiTerminal {
AnsiTerminal({@required io.Stdio stdio, @required Platform platform})
: _stdio = stdio,
_platform = platform;
final io.Stdio _stdio;
final Platform _platform;
static const String bold = '\u001B[1m';
static const String resetAll = '\u001B[0m';
static const String resetColor = '\u001B[39m';
......@@ -107,7 +119,8 @@ class AnsiTerminal {
static String colorCode(TerminalColor color) => _colorMap[color];
bool get supportsColor => globals.platform.stdoutSupportsAnsi ?? false;
bool get supportsColor => _platform.stdoutSupportsAnsi ?? false;
final RegExp _boldControls = RegExp('(${RegExp.escape(resetBold)}|${RegExp.escape(bold)})');
/// Whether we are interacting with the flutter tool via the terminal.
......@@ -159,10 +172,10 @@ class AnsiTerminal {
String clearScreen() => supportsColor ? clear : '\n\n';
set singleCharMode(bool value) {
if (!io.stdinHasTerminal) {
if (!_stdio.stdinHasTerminal) {
return;
}
final io.Stdin stdin = io.stdin as io.Stdin;
final io.Stdin stdin = _stdio.stdin as io.Stdin;
// The order of setting lineMode and echoMode is important on Windows.
if (value) {
stdin.echoMode = false;
......@@ -179,7 +192,7 @@ class AnsiTerminal {
///
/// Useful when the console is in [singleCharMode].
Stream<String> get keystrokes {
_broadcastStdInString ??= io.stdin.transform<String>(const AsciiDecoder(allowInvalid: true)).asBroadcastStream();
_broadcastStdInString ??= _stdio.stdin.transform<String>(const AsciiDecoder(allowInvalid: true)).asBroadcastStream();
return _broadcastStdInString;
}
......@@ -198,6 +211,7 @@ class AnsiTerminal {
/// If [usesTerminalUi] is false, throws a [StateError].
Future<String> promptForCharInput(
List<String> acceptedCharacters, {
@required Logger logger,
String prompt,
int defaultChoiceIndex,
bool displayAcceptedCharacters = true,
......@@ -220,14 +234,14 @@ class AnsiTerminal {
singleCharMode = true;
while (choice == null || choice.length > 1 || !acceptedCharacters.contains(choice)) {
if (prompt != null) {
globals.printStatus(prompt, emphasis: true, newline: false);
logger.printStatus(prompt, emphasis: true, newline: false);
if (displayAcceptedCharacters) {
globals.printStatus(' [${charactersToDisplay.join("|")}]', newline: false);
logger.printStatus(' [${charactersToDisplay.join("|")}]', newline: false);
}
globals.printStatus(': ', emphasis: true, newline: false);
logger.printStatus(': ', emphasis: true, newline: false);
}
choice = await keystrokes.first;
globals.printStatus(choice);
logger.printStatus(choice);
}
singleCharMode = false;
if (defaultChoiceIndex != null && choice == '\n') {
......
......@@ -6,6 +6,7 @@ import 'dart:async';
import 'dart:math' show Random, max;
import 'package:intl/intl.dart';
import 'package:meta/meta.dart';
import '../convert.dart';
import '../globals.dart' as globals;
......@@ -395,7 +396,7 @@ class _AnsiRun {
/// If [outputPreferences.wrapText] is false, then the text will be returned
/// simply split at the newlines, but not wrapped. If [shouldWrap] is specified,
/// then it overrides the [outputPreferences.wrapText] setting.
List<String> _wrapTextAsLines(String text, { int start = 0, int columnWidth, bool shouldWrap }) {
List<String> _wrapTextAsLines(String text, { int start = 0, int columnWidth, @required bool shouldWrap }) {
if (text == null || text.isEmpty) {
return <String>[''];
}
......
......@@ -974,7 +974,7 @@ class NotifyingLogger extends Logger {
}) {
assert(timeout != null);
printStatus(message);
return SilentStatus(timeout: timeout);
return SilentStatus(timeout: timeout, timeoutConfiguration: timeoutConfiguration);
}
void dispose() {
......@@ -983,6 +983,12 @@ class NotifyingLogger extends Logger {
@override
void sendEvent(String name, [Map<String, dynamic> args]) { }
@override
bool get supportsColor => throw UnimplementedError();
@override
bool get hasTerminal => false;
}
/// A running application, started by this daemon.
......@@ -1167,6 +1173,7 @@ class _AppRunLogger extends Logger {
_status = SilentStatus(
timeout: timeout,
timeoutConfiguration: timeoutConfiguration,
onFinish: () {
_status = null;
_sendProgressEvent(<String, dynamic>{
......@@ -1206,6 +1213,12 @@ class _AppRunLogger extends Logger {
domain.sendEvent(name, args);
}
}
@override
bool get supportsColor => throw UnimplementedError();
@override
bool get hasTerminal => false;
}
class LogMessage {
......
......@@ -19,6 +19,7 @@ import 'base/logger.dart';
import 'base/os.dart';
import 'base/process.dart';
import 'base/signals.dart';
import 'base/terminal.dart';
import 'base/time.dart';
import 'base/user_messages.dart';
import 'base/utils.dart';
......@@ -101,7 +102,19 @@ Future<T> runInContext<T>(
IOSWorkflow: () => const IOSWorkflow(),
KernelCompilerFactory: () => const KernelCompilerFactory(),
LinuxWorkflow: () => const LinuxWorkflow(),
Logger: () => globals.platform.isWindows ? WindowsStdoutLogger() : StdoutLogger(),
Logger: () => globals.platform.isWindows
? WindowsStdoutLogger(
terminal: globals.terminal,
stdio: globals.stdio,
outputPreferences: outputPreferences,
timeoutConfiguration: timeoutConfiguration,
)
: StdoutLogger(
terminal: globals.terminal,
stdio: globals.stdio,
outputPreferences: outputPreferences,
timeoutConfiguration: timeoutConfiguration,
),
MacOSWorkflow: () => const MacOSWorkflow(),
MDnsObservatoryDiscovery: () => MDnsObservatoryDiscovery(),
OperatingSystemUtils: () => OperatingSystemUtils(),
......
......@@ -211,7 +211,12 @@ class Doctor {
lineBuffer.write(' (${result.statusInfo})');
}
buffer.write(wrapText(lineBuffer.toString(), hangingIndent: result.leadingBox.length + 1));
buffer.write(wrapText(
lineBuffer.toString(),
hangingIndent: result.leadingBox.length + 1,
columnWidth: outputPreferences.wrapColumn,
shouldWrap: outputPreferences.wrapText,
));
buffer.writeln();
if (result.type != ValidationType.installed) {
......@@ -253,6 +258,7 @@ class Doctor {
final Status status = Status.withSpinner(
timeout: timeoutConfiguration.fastOperation,
slowWarningCallback: () => validator.slowWarning,
timeoutConfiguration: timeoutConfiguration,
);
ValidationResult result;
try {
......
......@@ -10,6 +10,7 @@ import 'base/config.dart';
import 'base/context.dart';
import 'base/error_handling_file_system.dart';
import 'base/file_system.dart';
import 'base/io.dart';
import 'base/logger.dart';
import 'base/terminal.dart';
import 'cache.dart';
......@@ -103,4 +104,10 @@ AnsiTerminal get terminal {
return context?.get<AnsiTerminal>() ?? _defaultAnsiTerminal;
}
final AnsiTerminal _defaultAnsiTerminal = AnsiTerminal();
final AnsiTerminal _defaultAnsiTerminal = AnsiTerminal(
stdio: stdio,
platform: platform,
);
/// The global Stdio wrapper.
Stdio get stdio => context.get<Stdio>() ?? const Stdio();
......@@ -243,6 +243,7 @@ Future<String> _chooseSigningIdentity(List<String> validCodeSigningIdentities) a
prompt: 'Please select a certificate for code signing',
displayAcceptedCharacters: true,
defaultChoiceIndex: 0, // Just pressing enter chooses the first one.
logger: globals.logger,
);
if (choice == 'a') {
......
......@@ -284,6 +284,7 @@ class XcodeProjectInterpreter {
}) async {
final Status status = Status.withSpinner(
timeout: timeoutConfiguration.fastOperation,
timeoutConfiguration: timeoutConfiguration,
);
final List<String> showBuildSettingsCommand = <String>[
_executable,
......
......@@ -13,7 +13,6 @@ import '../artifacts.dart';
import '../base/common.dart';
import '../base/context.dart';
import '../base/file_system.dart';
import '../base/io.dart' as io;
import '../base/logger.dart';
import '../base/terminal.dart';
import '../base/user_messages.dart';
......@@ -146,13 +145,20 @@ class FlutterCommandRunner extends CommandRunner<void> {
@override
String get usageFooter {
return wrapText('Run "flutter help -v" for verbose help output, including less commonly used options.');
return wrapText('Run "flutter help -v" for verbose help output, including less commonly used options.',
columnWidth: outputPreferences.wrapColumn,
shouldWrap: outputPreferences.wrapText,
);
}
@override
String get usage {
final String usageWithoutDescription = super.usage.substring(description.length + 2);
return '${wrapText(description)}\n\n$usageWithoutDescription';
final String prefix = wrapText(description,
shouldWrap: outputPreferences.wrapText,
columnWidth: outputPreferences.wrapColumn,
);
return '$prefix\n\n$usageWithoutDescription';
}
static String get defaultFlutterRoot {
......@@ -233,7 +239,7 @@ class FlutterCommandRunner extends CommandRunner<void> {
// Check for verbose.
if (topLevelResults['verbose'] as bool) {
// Override the logger.
contextOverrides[Logger] = VerboseLogger(globals.logger);
contextOverrides[Logger] = VerboseLogger(globals.logger, stopwatch: Stopwatch());
}
// Don't set wrapColumns unless the user said to: if it's set, then all
......@@ -255,7 +261,7 @@ class FlutterCommandRunner extends CommandRunner<void> {
// anything, unless the user explicitly said to.
final bool useWrapping = topLevelResults.wasParsed('wrap')
? topLevelResults['wrap'] as bool
: io.stdio.terminalColumns != null && topLevelResults['wrap'] as bool;
: globals.stdio.terminalColumns != null && topLevelResults['wrap'] as bool;
contextOverrides[OutputPreferences] = OutputPreferences(
wrapText: useWrapping,
showColor: topLevelResults['color'] as bool,
......
......@@ -699,7 +699,7 @@ class StreamLogger extends Logger {
int progressIndicatorPadding = kDefaultStatusPadding,
}) {
_log('[progress] $message');
return SilentStatus(timeout: timeout)..start();
return SilentStatus(timeout: timeout, timeoutConfiguration: timeoutConfiguration)..start();
}
bool _interrupt = false;
......@@ -722,6 +722,12 @@ class StreamLogger extends Logger {
@override
void sendEvent(String name, [Map<String, dynamic> args]) { }
@override
bool get supportsColor => throw UnimplementedError();
@override
bool get hasTerminal => false;
}
class LoggerInterrupted implements Exception {
......
......@@ -7,7 +7,6 @@ import 'package:platform/platform.dart';
import 'package:flutter_tools/src/base/context.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/commands/clean.dart';
import 'package:flutter_tools/src/ios/xcodeproj.dart';
import 'package:flutter_tools/src/macos/xcode.dart';
......@@ -77,7 +76,6 @@ void main() {
verify(mockFile.deleteSync(recursive: true)).called(1);
}, overrides: <Type, Generator>{
Platform: () => windowsPlatform,
Logger: () => BufferLogger(),
Xcode: () => mockXcode,
});
......@@ -94,7 +92,6 @@ void main() {
verifyNever(mockFile.deleteSync(recursive: true));
}, overrides: <Type, Generator>{
Platform: () => windowsPlatform,
Logger: () => BufferLogger(),
Xcode: () => mockXcode,
});
}
......
......@@ -15,7 +15,6 @@ import 'package:flutter_tools/src/base/context.dart';
import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/terminal.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart';
......@@ -451,11 +450,9 @@ void main() {
});
group('Config files', () {
BufferLogger mockLogger;
Directory tempDir;
setUp(() {
mockLogger = BufferLogger();
tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_settings_aar_test.');
});
......@@ -498,13 +495,12 @@ include ':app'
createSettingsAarGradle(tempDir);
expect(mockLogger.statusText, contains('created successfully'));
expect(testLogger.statusText, contains('created successfully'));
expect(tempDir.childFile('settings_aar.gradle').existsSync(), isTrue);
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(),
Logger: () => mockLogger,
});
testUsingContext('create settings_aar.gradle when current settings.gradle doesn\'t load plugins', () {
......@@ -532,13 +528,12 @@ include ':app'
createSettingsAarGradle(tempDir);
expect(mockLogger.statusText, contains('created successfully'));
expect(testLogger.statusText, contains('created successfully'));
expect(tempDir.childFile('settings_aar.gradle').existsSync(), isTrue);
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(),
Logger: () => mockLogger,
});
});
......
......@@ -9,7 +9,6 @@ import 'package:file/memory.dart';
import 'package:flutter_tools/src/asset.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/bundle.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/devfs.dart';
......@@ -183,16 +182,15 @@ flutter:
});
});
test('Failed directory delete shows message', () async {
testUsingContext('Failed directory delete shows message', () async {
final MockDirectory mockDirectory = MockDirectory();
final BufferLogger bufferLogger = BufferLogger();
when(mockDirectory.existsSync()).thenReturn(true);
when(mockDirectory.deleteSync(recursive: true)).thenThrow(const FileSystemException('ABCD'));
await writeBundle(mockDirectory, <String, DevFSContent>{}, loggerOverride: bufferLogger);
await writeBundle(mockDirectory, <String, DevFSContent>{}, loggerOverride: testLogger);
verify(mockDirectory.createSync(recursive: true)).called(1);
expect(bufferLogger.errorText, contains('ABCD'));
expect(testLogger.errorText, contains('ABCD'));
});
}
......
......@@ -13,7 +13,6 @@ import 'package:flutter_tools/src/base/build.dart';
import 'package:flutter_tools/src/base/context.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/process.dart';
import 'package:flutter_tools/src/macos/xcode.dart';
import 'package:flutter_tools/src/version.dart';
......@@ -225,7 +224,6 @@ void main() {
MockAndroidSdk mockAndroidSdk;
MockArtifacts mockArtifacts;
MockXcode mockXcode;
BufferLogger bufferLogger;
setUp(() async {
fs = MemoryFileSystem();
......@@ -247,7 +245,6 @@ void main() {
mockXcode = MockXcode();
when(mockXcode.sdkLocation(any)).thenAnswer((_) => Future<String>.value(kSDKPath));
bufferLogger = BufferLogger();
for (final BuildMode mode in BuildMode.values) {
when(mockArtifacts.getArtifactPath(Artifact.snapshotDart,
platform: anyNamed('platform'), mode: mode)).thenReturn(kSnapshotDart);
......@@ -261,7 +258,6 @@ void main() {
ProcessManager: () => FakeProcessManager.any(),
GenSnapshot: () => genSnapshot,
Xcode: () => mockXcode,
Logger: () => bufferLogger,
};
testUsingContext('iOS debug AOT snapshot is invalid', () async {
......@@ -658,7 +654,7 @@ void main() {
expect(genSnapshotExitCode, 0);
expect(genSnapshot.callCount, 1);
expect(bufferLogger.statusText, matches(RegExp(r'snapshot\(CompileTime\): \d+ ms.')));
expect(testLogger.statusText, matches(RegExp(r'snapshot\(CompileTime\): \d+ ms.')));
}, overrides: contextOverrides);
});
}
......@@ -6,7 +6,6 @@ import 'dart:convert' show jsonEncode;
import 'package:platform/platform.dart';
import 'package:flutter_tools/src/base/context.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/terminal.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
......@@ -16,7 +15,7 @@ import '../../src/common.dart';
import '../../src/context.dart';
import '../../src/mocks.dart';
final Generator _kNoAnsiPlatform = () => FakePlatform.fromPlatform(const LocalPlatform())..stdoutSupportsAnsi = false;
final Platform _kNoAnsiPlatform = FakePlatform.fromPlatform(const LocalPlatform())..stdoutSupportsAnsi = false;
void main() {
final String red = RegExp.escape(AnsiTerminal.red);
......@@ -30,9 +29,15 @@ void main() {
setUp(() {
fakeStopWatch = FakeStopwatch();
});
testUsingContext('error', () async {
final BufferLogger mockLogger = BufferLogger();
final VerboseLogger verboseLogger = VerboseLogger(mockLogger);
testWithoutContext('error', () async {
final BufferLogger mockLogger = BufferLogger(
terminal: AnsiTerminal(
stdio: MockStdio(),
platform: _kNoAnsiPlatform,
),
outputPreferences: OutputPreferences.test(showColor: false),
);
final VerboseLogger verboseLogger = VerboseLogger(mockLogger, stopwatch: fakeStopWatch);
verboseLogger.printStatus('Hey Hey Hey Hey');
verboseLogger.printTrace('Oooh, I do I do I do');
......@@ -42,15 +47,17 @@ void main() {
r'\[ (?: {0,2}\+[0-9]{1,4} ms| )\] Oooh, I do I do I do\n$'));
expect(mockLogger.traceText, '');
expect(mockLogger.errorText, matches( r'^\[ (?: {0,2}\+[0-9]{1,4} ms| )\] Helpless!\n$'));
}, overrides: <Type, Generator>{
OutputPreferences: () => OutputPreferences(showColor: false),
Platform: _kNoAnsiPlatform,
Stopwatch: () => fakeStopWatch,
});
testUsingContext('ANSI colored errors', () async {
final BufferLogger mockLogger = BufferLogger();
final VerboseLogger verboseLogger = VerboseLogger(mockLogger);
testWithoutContext('ANSI colored errors', () async {
final BufferLogger mockLogger = BufferLogger(
terminal: AnsiTerminal(
stdio: MockStdio(),
platform: FakePlatform()..stdoutSupportsAnsi = true,
),
outputPreferences: OutputPreferences.test(showColor: true),
);
final VerboseLogger verboseLogger = VerboseLogger(mockLogger, stopwatch: fakeStopWatch);
verboseLogger.printStatus('Hey Hey Hey Hey');
verboseLogger.printTrace('Oooh, I do I do I do');
......@@ -64,10 +71,6 @@ void main() {
expect(
mockLogger.errorText,
matches('^$red' r'\[ (?: {0,2}\+[0-9]{1,4} ms| )\] ' '${bold}Helpless!$resetBold$resetColor' r'\n$'));
}, overrides: <Type, Generator>{
OutputPreferences: () => OutputPreferences(showColor: true),
Platform: () => FakePlatform()..stdoutSupportsAnsi = true,
Stopwatch: () => fakeStopWatch,
});
});
......@@ -84,6 +87,8 @@ void main() {
timeout: const Duration(seconds: 2),
padding: 20,
onFinish: () => called += 1,
stdio: mockStdio,
timeoutConfiguration: const TimeoutConfiguration(),
);
}
......@@ -110,6 +115,8 @@ void main() {
FakeAsync().run((FakeAsync time) {
final AnsiSpinner ansiSpinner = AnsiSpinner(
timeout: const Duration(hours: 10),
stdio: mockStdio,
timeoutConfiguration: const TimeoutConfiguration(),
)..start();
doWhileAsync(time, () => ansiSpinner.ticks < 10);
List<String> lines = outputStdout();
......@@ -138,7 +145,6 @@ void main() {
expect(done, isTrue);
}, overrides: <Type, Generator>{
Platform: () => FakePlatform(operatingSystem: testOs),
Stdio: () => mockStdio,
Stopwatch: () => mockStopwatch,
});
......@@ -148,6 +154,8 @@ void main() {
FakeAsync().run((FakeAsync time) {
final AnsiSpinner ansiSpinner = AnsiSpinner(
timeout: const Duration(seconds: 2),
stdio: mockStdio,
timeoutConfiguration: const TimeoutConfiguration(),
)..start();
mockStopwatch.elapsed = const Duration(seconds: 1);
doWhileAsync(time, () => ansiSpinner.ticks < 10); // one second
......@@ -165,14 +173,22 @@ void main() {
expect(done, isTrue);
}, overrides: <Type, Generator>{
Platform: () => FakePlatform(operatingSystem: testOs),
Stdio: () => mockStdio,
Stopwatch: () => mockStopwatch,
});
// Uses Stopwatch from context.
testUsingContext('Stdout startProgress on colored terminal for $testOs', () async {
bool done = false;
FakeAsync().run((FakeAsync time) {
final Logger logger = context.get<Logger>();
final Logger logger = StdoutLogger(
terminal: AnsiTerminal(
stdio: mockStdio,
platform: FakePlatform(operatingSystem: testOs)..stdoutSupportsAnsi = true,
),
stdio: mockStdio,
outputPreferences: OutputPreferences.test(showColor: true),
timeoutConfiguration: const TimeoutConfiguration(),
);
final Status status = logger.startProgress(
'Hello',
progressId: null,
......@@ -190,18 +206,21 @@ void main() {
done = true;
});
expect(done, isTrue);
}, overrides: <Type, Generator>{
Logger: () => StdoutLogger(),
OutputPreferences: () => OutputPreferences(showColor: true),
Platform: () => FakePlatform(operatingSystem: testOs)..stdoutSupportsAnsi = true,
Stdio: () => mockStdio,
});
testUsingContext('Stdout startProgress on colored terminal pauses on $testOs', () async {
bool done = false;
FakeAsync().run((FakeAsync time) {
mockStopwatch.elapsed = const Duration(seconds: 5);
final Logger logger = context.get<Logger>();
final Logger logger = StdoutLogger(
terminal: AnsiTerminal(
stdio: mockStdio,
platform: FakePlatform(operatingSystem: testOs)..stdoutSupportsAnsi = true,
),
stdio: mockStdio,
outputPreferences: OutputPreferences.test(showColor: true),
timeoutConfiguration: const TimeoutConfiguration(),
);
final Status status = logger.startProgress(
'Knock Knock, Who\'s There',
timeout: const Duration(days: 10),
......@@ -232,10 +251,6 @@ void main() {
});
expect(done, isTrue);
}, overrides: <Type, Generator>{
Logger: () => StdoutLogger(),
OutputPreferences: () => OutputPreferences(showColor: true),
Platform: () => FakePlatform(operatingSystem: testOs)..stdoutSupportsAnsi = true,
Stdio: () => mockStdio,
Stopwatch: () => mockStopwatch,
});
......@@ -270,7 +285,6 @@ void main() {
expect(done, isTrue);
}, overrides: <Type, Generator>{
Platform: () => FakePlatform(operatingSystem: testOs),
Stdio: () => mockStdio,
Stopwatch: () => mockStopwatch,
});
......@@ -307,7 +321,6 @@ void main() {
expect(done, isTrue);
}, overrides: <Type, Generator>{
Platform: () => FakePlatform(operatingSystem: testOs),
Stdio: () => mockStdio,
Stopwatch: () => mockStopwatch,
});
......@@ -353,7 +366,6 @@ void main() {
expect(done, isTrue);
}, overrides: <Type, Generator>{
Platform: () => FakePlatform(operatingSystem: testOs),
Stdio: () => mockStdio,
Stopwatch: () => mockStopwatch,
});
}
......@@ -372,33 +384,48 @@ void main() {
timeout: timeoutConfiguration.slowOperation,
padding: 20,
onFinish: () => called++,
stdio: mockStdio,
timeoutConfiguration: const TimeoutConfiguration(),
);
});
List<String> outputStdout() => mockStdio.writtenToStdout.join('').split('\n');
List<String> outputStderr() => mockStdio.writtenToStderr.join('').split('\n');
testUsingContext('Error logs are wrapped', () async {
final Logger logger = context.get<Logger>();
testWithoutContext('Error logs are wrapped', () async {
final Logger logger = StdoutLogger(
terminal: AnsiTerminal(
stdio: mockStdio,
platform: _kNoAnsiPlatform,
),
stdio: mockStdio,
outputPreferences: OutputPreferences.test(wrapText: true, wrapColumn: 40, showColor: false),
timeoutConfiguration: const TimeoutConfiguration(),
);
logger.printError('0123456789' * 15);
final List<String> lines = outputStderr();
expect(outputStdout().length, equals(1));
expect(outputStdout().first, isEmpty);
expect(lines[0], equals('0123456789' * 4));
expect(lines[1], equals('0123456789' * 4));
expect(lines[2], equals('0123456789' * 4));
expect(lines[3], equals('0123456789' * 3));
}, overrides: <Type, Generator>{
Logger: () => StdoutLogger(),
OutputPreferences: () => OutputPreferences(wrapText: true, wrapColumn: 40, showColor: false),
Stdio: () => mockStdio,
Platform: _kNoAnsiPlatform,
});
testUsingContext('Error logs are wrapped and can be indented.', () async {
final Logger logger = context.get<Logger>();
testWithoutContext('Error logs are wrapped and can be indented.', () async {
final Logger logger = StdoutLogger(
terminal: AnsiTerminal(
stdio: mockStdio,
platform: _kNoAnsiPlatform,
),
stdio: mockStdio,
outputPreferences: OutputPreferences.test(wrapText: true, wrapColumn: 40, showColor: false),
timeoutConfiguration: const TimeoutConfiguration(),
);
logger.printError('0123456789' * 15, indent: 5);
final List<String> lines = outputStderr();
expect(outputStdout().length, equals(1));
expect(outputStdout().first, isEmpty);
expect(lines.length, equals(6));
......@@ -408,17 +435,21 @@ void main() {
expect(lines[3], equals(' 56789012345678901234567890123456789'));
expect(lines[4], equals(' 0123456789'));
expect(lines[5], isEmpty);
}, overrides: <Type, Generator>{
Logger: () => StdoutLogger(),
OutputPreferences: () => OutputPreferences(wrapText: true, wrapColumn: 40, showColor: false),
Stdio: () => mockStdio,
Platform: _kNoAnsiPlatform,
});
testUsingContext('Error logs are wrapped and can have hanging indent.', () async {
final Logger logger = context.get<Logger>();
testWithoutContext('Error logs are wrapped and can have hanging indent.', () async {
final Logger logger = StdoutLogger(
terminal: AnsiTerminal(
stdio: mockStdio,
platform: _kNoAnsiPlatform,
),
stdio: mockStdio,
outputPreferences: OutputPreferences.test(wrapText: true, wrapColumn: 40, showColor: false),
timeoutConfiguration: const TimeoutConfiguration(),
);
logger.printError('0123456789' * 15, hangingIndent: 5);
final List<String> lines = outputStderr();
expect(outputStdout().length, equals(1));
expect(outputStdout().first, isEmpty);
expect(lines.length, equals(6));
......@@ -428,17 +459,21 @@ void main() {
expect(lines[3], equals(' 01234567890123456789012345678901234'));
expect(lines[4], equals(' 56789'));
expect(lines[5], isEmpty);
}, overrides: <Type, Generator>{
Logger: () => StdoutLogger(),
OutputPreferences: () => OutputPreferences(wrapText: true, wrapColumn: 40, showColor: false),
Stdio: () => mockStdio,
Platform: _kNoAnsiPlatform,
});
testUsingContext('Error logs are wrapped, indented, and can have hanging indent.', () async {
final Logger logger = context.get<Logger>();
testWithoutContext('Error logs are wrapped, indented, and can have hanging indent.', () async {
final Logger logger = StdoutLogger(
terminal: AnsiTerminal(
stdio: mockStdio,
platform: _kNoAnsiPlatform,
),
stdio: mockStdio,
outputPreferences: OutputPreferences.test(wrapText: true, wrapColumn: 40, showColor: false),
timeoutConfiguration: const TimeoutConfiguration(),
);
logger.printError('0123456789' * 15, indent: 4, hangingIndent: 5);
final List<String> lines = outputStderr();
expect(outputStdout().length, equals(1));
expect(outputStdout().first, isEmpty);
expect(lines.length, equals(6));
......@@ -448,34 +483,42 @@ void main() {
expect(lines[3], equals(' 8901234567890123456789012345678'));
expect(lines[4], equals(' 901234567890123456789'));
expect(lines[5], isEmpty);
}, overrides: <Type, Generator>{
Logger: () => StdoutLogger(),
OutputPreferences: () => OutputPreferences(wrapText: true, wrapColumn: 40, showColor: false),
Stdio: () => mockStdio,
Platform: _kNoAnsiPlatform,
});
testUsingContext('Stdout logs are wrapped', () async {
final Logger logger = context.get<Logger>();
testWithoutContext('Stdout logs are wrapped', () async {
final Logger logger = StdoutLogger(
terminal: AnsiTerminal(
stdio: mockStdio,
platform: _kNoAnsiPlatform,
),
stdio: mockStdio,
outputPreferences: OutputPreferences.test(wrapText: true, wrapColumn: 40, showColor: false),
timeoutConfiguration: const TimeoutConfiguration(),
);
logger.printStatus('0123456789' * 15);
final List<String> lines = outputStdout();
expect(outputStderr().length, equals(1));
expect(outputStderr().first, isEmpty);
expect(lines[0], equals('0123456789' * 4));
expect(lines[1], equals('0123456789' * 4));
expect(lines[2], equals('0123456789' * 4));
expect(lines[3], equals('0123456789' * 3));
}, overrides: <Type, Generator>{
Logger: () => StdoutLogger(),
OutputPreferences: () => OutputPreferences(wrapText: true, wrapColumn: 40, showColor: false),
Stdio: () => mockStdio,
Platform: _kNoAnsiPlatform,
});
testUsingContext('Stdout logs are wrapped and can be indented.', () async {
final Logger logger = context.get<Logger>();
testWithoutContext('Stdout logs are wrapped and can be indented.', () async {
final Logger logger = StdoutLogger(
terminal: AnsiTerminal(
stdio: mockStdio,
platform: _kNoAnsiPlatform,
),
stdio: mockStdio,
outputPreferences: OutputPreferences.test(wrapText: true, wrapColumn: 40, showColor: false),
timeoutConfiguration: const TimeoutConfiguration(),
);
logger.printStatus('0123456789' * 15, indent: 5);
final List<String> lines = outputStdout();
expect(outputStderr().length, equals(1));
expect(outputStderr().first, isEmpty);
expect(lines.length, equals(6));
......@@ -485,17 +528,21 @@ void main() {
expect(lines[3], equals(' 56789012345678901234567890123456789'));
expect(lines[4], equals(' 0123456789'));
expect(lines[5], isEmpty);
}, overrides: <Type, Generator>{
Logger: () => StdoutLogger(),
OutputPreferences: () => OutputPreferences(wrapText: true, wrapColumn: 40, showColor: false),
Stdio: () => mockStdio,
Platform: _kNoAnsiPlatform,
});
testUsingContext('Stdout logs are wrapped and can have hanging indent.', () async {
final Logger logger = context.get<Logger>();
testWithoutContext('Stdout logs are wrapped and can have hanging indent.', () async {
final Logger logger = StdoutLogger(
terminal: AnsiTerminal(
stdio: mockStdio,
platform: _kNoAnsiPlatform,
),
stdio: mockStdio,
outputPreferences: OutputPreferences.test(wrapText: true, wrapColumn: 40, showColor: false),
timeoutConfiguration: const TimeoutConfiguration(),
);
logger.printStatus('0123456789' * 15, hangingIndent: 5);
final List<String> lines = outputStdout();
expect(outputStderr().length, equals(1));
expect(outputStderr().first, isEmpty);
expect(lines.length, equals(6));
......@@ -505,17 +552,21 @@ void main() {
expect(lines[3], equals(' 01234567890123456789012345678901234'));
expect(lines[4], equals(' 56789'));
expect(lines[5], isEmpty);
}, overrides: <Type, Generator>{
Logger: () => StdoutLogger(),
OutputPreferences: () => OutputPreferences(wrapText: true, wrapColumn: 40, showColor: false),
Stdio: () => mockStdio,
Platform: _kNoAnsiPlatform,
});
testUsingContext('Stdout logs are wrapped, indented, and can have hanging indent.', () async {
final Logger logger = context.get<Logger>();
final Logger logger = StdoutLogger(
terminal: AnsiTerminal(
stdio: mockStdio,
platform: _kNoAnsiPlatform,
),
stdio: mockStdio,
outputPreferences: OutputPreferences.test(wrapText: true, wrapColumn: 40, showColor: false),
timeoutConfiguration: const TimeoutConfiguration(),
);
logger.printStatus('0123456789' * 15, indent: 4, hangingIndent: 5);
final List<String> lines = outputStdout();
expect(outputStderr().length, equals(1));
expect(outputStderr().first, isEmpty);
expect(lines.length, equals(6));
......@@ -525,42 +576,54 @@ void main() {
expect(lines[3], equals(' 8901234567890123456789012345678'));
expect(lines[4], equals(' 901234567890123456789'));
expect(lines[5], isEmpty);
}, overrides: <Type, Generator>{
Logger: () => StdoutLogger(),
OutputPreferences: () => OutputPreferences(wrapText: true, wrapColumn: 40, showColor: false),
Stdio: () => mockStdio,
Platform: _kNoAnsiPlatform,
});
testUsingContext('Error logs are red', () async {
final Logger logger = context.get<Logger>();
testWithoutContext('Error logs are red', () async {
final Logger logger = StdoutLogger(
terminal: AnsiTerminal(
stdio: mockStdio,
platform: FakePlatform()..stdoutSupportsAnsi = true,
),
stdio: mockStdio,
outputPreferences: OutputPreferences.test(showColor: true),
timeoutConfiguration: const TimeoutConfiguration(),
);
logger.printError('Pants on fire!');
final List<String> lines = outputStderr();
expect(outputStdout().length, equals(1));
expect(outputStdout().first, isEmpty);
expect(lines[0], equals('${AnsiTerminal.red}Pants on fire!${AnsiTerminal.resetColor}'));
}, overrides: <Type, Generator>{
Logger: () => StdoutLogger(),
OutputPreferences: () => OutputPreferences(showColor: true),
Platform: () => FakePlatform()..stdoutSupportsAnsi = true,
Stdio: () => mockStdio,
});
testUsingContext('Stdout logs are not colored', () async {
final Logger logger = context.get<Logger>();
testWithoutContext('Stdout logs are not colored', () async {
final Logger logger = StdoutLogger(
terminal: AnsiTerminal(
stdio: mockStdio,
platform: FakePlatform(),
),
stdio: mockStdio,
outputPreferences: OutputPreferences.test(showColor: true),
timeoutConfiguration: const TimeoutConfiguration(),
);
logger.printStatus('All good.');
final List<String> lines = outputStdout();
expect(outputStderr().length, equals(1));
expect(outputStderr().first, isEmpty);
expect(lines[0], equals('All good.'));
}, overrides: <Type, Generator>{
Logger: () => StdoutLogger(),
OutputPreferences: () => OutputPreferences(showColor: true),
Stdio: () => mockStdio,
});
testUsingContext('Stdout printStatus handle null inputs on colored terminal', () async {
final Logger logger = context.get<Logger>();
testWithoutContext('Stdout printStatus handle null inputs on colored terminal', () async {
final Logger logger = StdoutLogger(
terminal: AnsiTerminal(
stdio: mockStdio,
platform: FakePlatform(),
),
stdio: mockStdio,
outputPreferences: OutputPreferences.test(showColor: true),
timeoutConfiguration: const TimeoutConfiguration(),
);
logger.printStatus(
null,
emphasis: null,
......@@ -569,17 +632,22 @@ void main() {
indent: null,
);
final List<String> lines = outputStdout();
expect(outputStderr().length, equals(1));
expect(outputStderr().first, isEmpty);
expect(lines[0], equals(''));
}, overrides: <Type, Generator>{
Logger: () => StdoutLogger(),
OutputPreferences: () => OutputPreferences(showColor: true),
Stdio: () => mockStdio,
});
testUsingContext('Stdout printStatus handle null inputs on non-color terminal', () async {
final Logger logger = context.get<Logger>();
testWithoutContext('Stdout printStatus handle null inputs on non-color terminal', () async {
final Logger logger = StdoutLogger(
terminal: AnsiTerminal(
stdio: mockStdio,
platform: _kNoAnsiPlatform,
),
stdio: mockStdio,
outputPreferences: OutputPreferences.test(showColor: false),
timeoutConfiguration: const TimeoutConfiguration(),
);
logger.printStatus(
null,
emphasis: null,
......@@ -591,21 +659,25 @@ void main() {
expect(outputStderr().length, equals(1));
expect(outputStderr().first, isEmpty);
expect(lines[0], equals(''));
}, overrides: <Type, Generator>{
Logger: () => StdoutLogger(),
OutputPreferences: () => OutputPreferences(showColor: false),
Stdio: () => mockStdio,
Platform: _kNoAnsiPlatform,
});
testUsingContext('Stdout startProgress on non-color terminal', () async {
// Status uses Stopwatch from context.
test('Stdout startProgress on non-color terminal', () async {
bool done = false;
FakeAsync().run((FakeAsync time) {
final Logger logger = context.get<Logger>();
final Logger logger = StdoutLogger(
terminal: AnsiTerminal(
stdio: mockStdio,
platform: _kNoAnsiPlatform,
),
stdio: mockStdio,
outputPreferences: OutputPreferences.test(showColor: false),
timeoutConfiguration: const TimeoutConfiguration(),
);
final Status status = logger.startProgress(
'Hello',
progressId: null,
timeout: timeoutConfiguration.slowOperation,
timeout: const TimeoutConfiguration().slowOperation,
progressIndicatorPadding: 20, // this minus the "Hello" equals the 15 below.
);
expect(outputStderr().length, equals(1));
......@@ -619,11 +691,6 @@ void main() {
done = true;
});
expect(done, isTrue);
}, overrides: <Type, Generator>{
Logger: () => StdoutLogger(),
OutputPreferences: () => OutputPreferences(showColor: false),
Stdio: () => mockStdio,
Platform: _kNoAnsiPlatform,
});
testUsingContext('SummaryStatus works when canceled', () async {
......@@ -646,7 +713,9 @@ void main() {
// Verify that stopping or canceling multiple times throws.
expect(() { summaryStatus.cancel(); }, throwsA(isInstanceOf<AssertionError>()));
expect(() { summaryStatus.stop(); }, throwsA(isInstanceOf<AssertionError>()));
}, overrides: <Type, Generator>{Stdio: () => mockStdio, Platform: _kNoAnsiPlatform});
}, overrides: <Type, Generator>{
Platform: () => _kNoAnsiPlatform,
});
testUsingContext('SummaryStatus works when stopped', () async {
summaryStatus.start();
......@@ -669,30 +738,53 @@ void main() {
// Verify that stopping or canceling multiple times throws.
expect(() { summaryStatus.stop(); }, throwsA(isInstanceOf<AssertionError>()));
expect(() { summaryStatus.cancel(); }, throwsA(isInstanceOf<AssertionError>()));
}, overrides: <Type, Generator>{Stdio: () => mockStdio, Platform: _kNoAnsiPlatform});
}, overrides: <Type, Generator>{
Platform: () => _kNoAnsiPlatform,
});
testUsingContext('sequential startProgress calls with StdoutLogger', () async {
final Logger logger = context.get<Logger>();
logger.startProgress('AAA', timeout: timeoutConfiguration.fastOperation)..stop();
logger.startProgress('BBB', timeout: timeoutConfiguration.fastOperation)..stop();
// Status still uses Stopwatch from context.
// TODO(jonahwilliams): switch to testWithoutContext once logger uses a
// TimerFactory.
test('sequential startProgress calls with StdoutLogger', () async {
final Logger logger = StdoutLogger(
terminal: AnsiTerminal(
stdio: mockStdio,
platform: _kNoAnsiPlatform,
),
stdio: mockStdio,
outputPreferences: OutputPreferences.test(showColor: false),
timeoutConfiguration: const TimeoutConfiguration(),
);
logger.startProgress('AAA', timeout: const TimeoutConfiguration().fastOperation)..stop();
logger.startProgress('BBB', timeout: const TimeoutConfiguration().fastOperation)..stop();
final List<String> output = outputStdout();
expect(output.length, equals(3));
// There's 61 spaces at the start: 59 (padding default) - 3 (length of AAA) + 5 (margin).
// Then there's a left-padded "0ms" 8 characters wide, so 5 spaces then "0ms"
// (except sometimes it's randomly slow so we handle up to "99,999ms").
expect(output[0], matches(RegExp(r'AAA[ ]{61}[\d, ]{5}[\d]ms')));
expect(output[1], matches(RegExp(r'BBB[ ]{61}[\d, ]{5}[\d]ms')));
}, overrides: <Type, Generator>{
Logger: () => StdoutLogger(),
OutputPreferences: () => OutputPreferences(showColor: false),
Stdio: () => mockStdio,
Platform: _kNoAnsiPlatform,
});
testUsingContext('sequential startProgress calls with VerboseLogger and StdoutLogger', () async {
final Logger logger = context.get<Logger>();
logger.startProgress('AAA', timeout: timeoutConfiguration.fastOperation)..stop();
logger.startProgress('BBB', timeout: timeoutConfiguration.fastOperation)..stop();
// Status still uses Stopwatch from context.
test('sequential startProgress calls with VerboseLogger and StdoutLogger', () async {
final Logger logger = VerboseLogger(
StdoutLogger(
terminal: AnsiTerminal(
stdio: mockStdio,
platform: _kNoAnsiPlatform,
),
stdio: mockStdio,
outputPreferences: OutputPreferences.test(),
timeoutConfiguration: const TimeoutConfiguration(),
),
stopwatch: FakeStopwatch(),
);
logger.startProgress('AAA', timeout: const TimeoutConfiguration().fastOperation)..stop();
logger.startProgress('BBB', timeout: const TimeoutConfiguration().fastOperation)..stop();
expect(outputStdout(), <Matcher>[
matches(r'^\[ (?: {0,2}\+[0-9]{1,4} ms| )\] AAA$'),
matches(r'^\[ (?: {0,2}\+[0-9]{1,4} ms| )\] AAA \(completed.*\)$'),
......@@ -700,19 +792,21 @@ void main() {
matches(r'^\[ (?: {0,2}\+[0-9]{1,4} ms| )\] BBB \(completed.*\)$'),
matches(r'^$'),
]);
}, overrides: <Type, Generator>{
Logger: () => VerboseLogger(StdoutLogger()),
Stdio: () => mockStdio,
Platform: _kNoAnsiPlatform,
});
testUsingContext('sequential startProgress calls with BufferLogger', () async {
testLogger.startProgress('AAA', timeout: timeoutConfiguration.fastOperation)..stop();
testLogger.startProgress('BBB', timeout: timeoutConfiguration.fastOperation)..stop();
expect(testLogger.statusText, 'AAA\nBBB\n');
}, overrides: <Type, Generator>{
Logger: () => BufferLogger(),
Platform: _kNoAnsiPlatform,
// Status still uses Stopwatch from context.
test('sequential startProgress calls with BufferLogger', () async {
final BufferLogger logger = BufferLogger(
terminal: AnsiTerminal(
stdio: mockStdio,
platform: _kNoAnsiPlatform,
),
outputPreferences: OutputPreferences.test(),
);
logger.startProgress('AAA', timeout: const TimeoutConfiguration().fastOperation)..stop();
logger.startProgress('BBB', timeout: const TimeoutConfiguration().fastOperation)..stop();
expect(logger.statusText, 'AAA\nBBB\n');
});
});
}
......
......@@ -6,7 +6,6 @@ import 'dart:async';
import 'package:platform/platform.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/process.dart';
import 'package:flutter_tools/src/base/terminal.dart';
import 'package:mockito/mockito.dart';
......@@ -69,11 +68,9 @@ void main() {
group('output formatting', () {
MockProcessManager mockProcessManager;
BufferLogger mockLogger;
setUp(() {
mockProcessManager = MockProcessManager();
mockLogger = BufferLogger();
});
MockProcess Function(List<String>) processMetaFactory(List<String> stdout, { List<String> stderr = const <String>[] }) {
......@@ -88,10 +85,10 @@ void main() {
final List<String> testString = <String>['0123456789' * 10];
mockProcessManager.processFactory = processMetaFactory(testString, stderr: testString);
await processUtils.stream(<String>['command']);
expect(mockLogger.statusText, equals('${testString[0]}\n'));
expect(mockLogger.errorText, equals('${testString[0]}\n'));
expect(testLogger.statusText, equals('${testString[0]}\n'));
expect(testLogger.errorText, equals('${testString[0]}\n'));
}, overrides: <Type, Generator>{
Logger: () => mockLogger,
ProcessManager: () => mockProcessManager,
OutputPreferences: () => OutputPreferences(wrapText: true, wrapColumn: 40),
Platform: () => FakePlatform.fromPlatform(const LocalPlatform())..stdoutSupportsAnsi = false,
......
......@@ -4,6 +4,7 @@
import 'dart:async';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:platform/platform.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/terminal.dart';
......@@ -11,24 +12,28 @@ import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:mockito/mockito.dart';
import '../../src/common.dart';
import '../../src/context.dart';
void main() {
group('output preferences', () {
testUsingContext('can wrap output', () async {
globals.printStatus('0123456789' * 8);
expect(testLogger.statusText, equals(('0123456789' * 4 + '\n') * 2));
}, overrides: <Type, Generator>{
OutputPreferences: () => OutputPreferences(wrapText: true, wrapColumn: 40),
testWithoutContext('can wrap output', () async {
final BufferLogger bufferLogger = BufferLogger(
outputPreferences: OutputPreferences.test(wrapText: true, wrapColumn: 40),
terminal: TestTerminal(platform: FakePlatform()..stdoutSupportsAnsi = true),
);
bufferLogger.printStatus('0123456789' * 8);
expect(bufferLogger.statusText, equals(('0123456789' * 4 + '\n') * 2));
});
testUsingContext('can turn off wrapping', () async {
testWithoutContext('can turn off wrapping', () async {
final BufferLogger bufferLogger = BufferLogger(
outputPreferences: OutputPreferences.test(wrapText: false),
terminal: TestTerminal(platform: FakePlatform()..stdoutSupportsAnsi = true),
);
final String testString = '0123456789' * 20;
globals.printStatus(testString);
expect(testLogger.statusText, equals('$testString\n'));
}, overrides: <Type, Generator>{
Platform: () => FakePlatform()..stdoutSupportsAnsi = true,
OutputPreferences: () => OutputPreferences(wrapText: false),
bufferLogger.printStatus(testString);
expect(bufferLogger.statusText, equals('$testString\n'));
});
});
......@@ -36,32 +41,29 @@ void main() {
AnsiTerminal terminal;
setUp(() {
terminal = AnsiTerminal();
terminal = AnsiTerminal(
stdio: globals.stdio, // Danger, using real stdio.
platform: FakePlatform()..stdoutSupportsAnsi = true,
);
});
testUsingContext('adding colors works', () {
testWithoutContext('adding colors works', () {
for (final TerminalColor color in TerminalColor.values) {
expect(
terminal.color('output', color),
equals('${AnsiTerminal.colorCode(color)}output${AnsiTerminal.resetColor}'),
);
}
}, overrides: <Type, Generator>{
OutputPreferences: () => OutputPreferences(showColor: true),
Platform: () => FakePlatform()..stdoutSupportsAnsi = true,
});
testUsingContext('adding bold works', () {
testWithoutContext('adding bold works', () {
expect(
terminal.bolden('output'),
equals('${AnsiTerminal.bold}output${AnsiTerminal.resetBold}'),
);
}, overrides: <Type, Generator>{
OutputPreferences: () => OutputPreferences(showColor: true),
Platform: () => FakePlatform()..stdoutSupportsAnsi = true,
});
testUsingContext('nesting bold within color works', () {
testWithoutContext('nesting bold within color works', () {
expect(
terminal.color(terminal.bolden('output'), TerminalColor.blue),
equals('${AnsiTerminal.blue}${AnsiTerminal.bold}output${AnsiTerminal.resetBold}${AnsiTerminal.resetColor}'),
......@@ -70,12 +72,9 @@ void main() {
terminal.color('non-bold ${terminal.bolden('output')} also non-bold', TerminalColor.blue),
equals('${AnsiTerminal.blue}non-bold ${AnsiTerminal.bold}output${AnsiTerminal.resetBold} also non-bold${AnsiTerminal.resetColor}'),
);
}, overrides: <Type, Generator>{
OutputPreferences: () => OutputPreferences(showColor: true),
Platform: () => FakePlatform()..stdoutSupportsAnsi = true,
});
testUsingContext('nesting color within bold works', () {
testWithoutContext('nesting color within bold works', () {
expect(
terminal.bolden(terminal.color('output', TerminalColor.blue)),
equals('${AnsiTerminal.bold}${AnsiTerminal.blue}output${AnsiTerminal.resetColor}${AnsiTerminal.resetBold}'),
......@@ -84,12 +83,9 @@ void main() {
terminal.bolden('non-color ${terminal.color('output', TerminalColor.blue)} also non-color'),
equals('${AnsiTerminal.bold}non-color ${AnsiTerminal.blue}output${AnsiTerminal.resetColor} also non-color${AnsiTerminal.resetBold}'),
);
}, overrides: <Type, Generator>{
OutputPreferences: () => OutputPreferences(showColor: true),
Platform: () => FakePlatform()..stdoutSupportsAnsi = true,
});
testUsingContext('nesting color within color works', () {
testWithoutContext('nesting color within color works', () {
expect(
terminal.color(terminal.color('output', TerminalColor.blue), TerminalColor.magenta),
equals('${AnsiTerminal.magenta}${AnsiTerminal.blue}output${AnsiTerminal.resetColor}${AnsiTerminal.magenta}${AnsiTerminal.resetColor}'),
......@@ -98,12 +94,9 @@ void main() {
terminal.color('magenta ${terminal.color('output', TerminalColor.blue)} also magenta', TerminalColor.magenta),
equals('${AnsiTerminal.magenta}magenta ${AnsiTerminal.blue}output${AnsiTerminal.resetColor}${AnsiTerminal.magenta} also magenta${AnsiTerminal.resetColor}'),
);
}, overrides: <Type, Generator>{
OutputPreferences: () => OutputPreferences(showColor: true),
Platform: () => FakePlatform()..stdoutSupportsAnsi = true,
});
testUsingContext('nesting bold within bold works', () {
testWithoutContext('nesting bold within bold works', () {
expect(
terminal.bolden(terminal.bolden('output')),
equals('${AnsiTerminal.bold}output${AnsiTerminal.resetBold}'),
......@@ -112,9 +105,6 @@ void main() {
terminal.bolden('bold ${terminal.bolden('output')} still bold'),
equals('${AnsiTerminal.bold}bold output still bold${AnsiTerminal.resetBold}'),
);
}, overrides: <Type, Generator>{
OutputPreferences: () => OutputPreferences(showColor: true),
Platform: () => FakePlatform()..stdoutSupportsAnsi = true,
});
});
......@@ -122,17 +112,22 @@ void main() {
AnsiTerminal terminalUnderTest;
setUp(() {
terminalUnderTest = TestTerminal();
terminalUnderTest = TestTerminal(stdio: MockStdio());
});
testUsingContext('character prompt throws if usesTerminalUi is false', () async {
testWithoutContext('character prompt throws if usesTerminalUi is false', () async {
expect(terminalUnderTest.promptForCharInput(
<String>['a', 'b', 'c'],
prompt: 'Please choose something',
logger: null,
), throwsA(isInstanceOf<StateError>()));
});
testUsingContext('character prompt', () async {
testWithoutContext('character prompt', () async {
final BufferLogger bufferLogger = BufferLogger(
terminal: terminalUnderTest,
outputPreferences: OutputPreferences.test(),
);
terminalUnderTest.usesTerminalUi = true;
mockStdInStream = Stream<String>.fromFutures(<Future<String>>[
Future<String>.value('d'), // Not in accepted list.
......@@ -142,17 +137,22 @@ void main() {
final String choice = await terminalUnderTest.promptForCharInput(
<String>['a', 'b', 'c'],
prompt: 'Please choose something',
logger: bufferLogger,
);
expect(choice, 'b');
expect(
testLogger.statusText,
bufferLogger.statusText,
'Please choose something [a|b|c]: d\n'
'Please choose something [a|b|c]: \n'
'\n'
'Please choose something [a|b|c]: b\n');
});
testUsingContext('default character choice without displayAcceptedCharacters', () async {
testWithoutContext('default character choice without displayAcceptedCharacters', () async {
final BufferLogger bufferLogger = BufferLogger(
terminal: terminalUnderTest,
outputPreferences: OutputPreferences.test(),
);
terminalUnderTest.usesTerminalUi = true;
mockStdInStream = Stream<String>.fromFutures(<Future<String>>[
Future<String>.value('\n'), // Not in accepted list
......@@ -162,21 +162,26 @@ void main() {
prompt: 'Please choose something',
displayAcceptedCharacters: false,
defaultChoiceIndex: 1, // which is b.
logger: bufferLogger,
);
expect(choice, 'b');
expect(
testLogger.statusText,
bufferLogger.statusText,
'Please choose something: \n'
'\n');
});
testUsingContext('Does not set single char mode when a terminal is not attached', () {
testWithoutContext('Does not set single char mode when a terminal is not attached', () {
final Stdio stdio = MockStdio();
when(stdio.stdin).thenThrow(StateError('This should not be called'));
when(stdio.stdinHasTerminal).thenReturn(false);
final AnsiTerminal ansiTerminal = AnsiTerminal(
stdio: stdio,
platform: const LocalPlatform()
);
globals.terminal.singleCharMode = true;
}, overrides: <Type, Generator>{
Stdio: () => MockStdio(),
expect(() => ansiTerminal.singleCharMode = true, returnsNormally);
});
});
}
......@@ -184,10 +189,17 @@ void main() {
Stream<String> mockStdInStream;
class TestTerminal extends AnsiTerminal {
TestTerminal({
Stdio stdio,
Platform platform = const LocalPlatform(),
}) : super(stdio: stdio, platform: platform);
@override
Stream<String> get keystrokes {
return mockStdInStream;
}
bool singleCharMode = false;
}
class MockStdio extends Mock implements Stdio {}
......@@ -7,6 +7,7 @@ import 'dart:async';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/terminal.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/compile.dart';
import 'package:flutter_tools/src/device.dart';
......@@ -15,6 +16,7 @@ import 'package:flutter_tools/src/run_cold.dart';
import 'package:flutter_tools/src/vmservice.dart';
import 'package:meta/meta.dart';
import 'package:mockito/mockito.dart';
import 'package:platform/platform.dart';
import '../src/common.dart';
import '../src/context.dart';
......@@ -26,7 +28,13 @@ void main() {
BufferLogger mockLogger;
setUp(() {
mockLogger = BufferLogger();
mockLogger = BufferLogger(
terminal: AnsiTerminal(
stdio: null,
platform: const LocalPlatform(),
),
outputPreferences: OutputPreferences.test(),
);
residentCompiler = MockResidentCompiler();
});
......@@ -114,23 +122,16 @@ void main() {
});
group('cold run', () {
BufferLogger mockLogger;
setUp(() {
mockLogger = BufferLogger();
});
testUsingContext('returns 1 if not prebuilt mode & mainPath does not exist', () async {
final MockDevice mockDevice = MockDevice();
final MockFlutterDevice mockFlutterDevice = MockFlutterDevice();
when(mockFlutterDevice.device).thenReturn(mockDevice);
final List<FlutterDevice> devices = <FlutterDevice>[mockFlutterDevice];
final int result = await ColdRunner(devices).run();
expect(result, 1);
expect(mockLogger.errorText, matches(r'Tried to run .*, but that file does not exist\.'));
expect(mockLogger.errorText, matches(r'Consider using the -t option to specify the Dart file to start\.'));
}, overrides: <Type, Generator>{
Logger: () => mockLogger,
expect(testLogger.errorText, matches(r'Tried to run .*, but that file does not exist\.'));
expect(testLogger.errorText, matches(r'Consider using the -t option to specify the Dart file to start\.'));
});
testUsingContext('calls runCold on attached device', () async {
......@@ -148,13 +149,12 @@ void main() {
applicationBinary: applicationBinary,
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
).run();
expect(result, 1);
verify(mockFlutterDevice.runCold(
coldRunner: anyNamed('coldRunner'),
route: anyNamed('route'),
));
}, overrides: <Type, Generator>{
Logger: () => mockLogger,
});
});
}
......
......@@ -4,7 +4,6 @@
import 'package:file/memory.dart';
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/process.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/base/file_system.dart';
......@@ -22,14 +21,12 @@ void main() {
MockXcode mockXcode;
MemoryFileSystem memoryFileSystem;
MockProcessManager mockProcessManager;
BufferLogger bufferLogger;
MockPlistUtils mockPlistUtils;
setUp(() {
mockXcode = MockXcode();
memoryFileSystem = MemoryFileSystem(style: FileSystemStyle.posix);
mockProcessManager = MockProcessManager();
bufferLogger = BufferLogger();
mockPlistUtils = MockPlistUtils();
});
......@@ -67,7 +64,6 @@ void main() {
FileSystem: () => memoryFileSystem,
ProcessManager: () => mockProcessManager,
Xcode: () => mockXcode,
Logger: () => bufferLogger,
PlistParser: () => mockPlistUtils,
});
......@@ -91,7 +87,6 @@ void main() {
FileSystem: () => memoryFileSystem,
ProcessManager: () => mockProcessManager,
Xcode: () => mockXcode,
Logger: () => bufferLogger,
PlistParser: () => mockPlistUtils,
});
......@@ -114,7 +109,6 @@ void main() {
FileSystem: () => memoryFileSystem,
ProcessManager: () => mockProcessManager,
Xcode: () => mockXcode,
Logger: () => bufferLogger,
PlistParser: () => mockPlistUtils,
});
......@@ -143,7 +137,6 @@ void main() {
FileSystem: () => memoryFileSystem,
ProcessManager: () => mockProcessManager,
Xcode: () => mockXcode,
Logger: () => bufferLogger,
PlistParser: () => mockPlistUtils,
});
......@@ -162,13 +155,12 @@ void main() {
await validateBitcode(BuildMode.release, TargetPlatform.ios);
expect(bufferLogger.statusText, '');
expect(testLogger.statusText, '');
}, overrides: <Type, Generator>{
Artifacts: () => LocalEngineArtifacts('/engine', 'ios_profile', 'host_profile'),
FileSystem: () => memoryFileSystem,
ProcessManager: () => mockProcessManager,
Xcode: () => mockXcode,
Logger: () => bufferLogger,
PlistParser: () => mockPlistUtils,
});
......@@ -187,13 +179,12 @@ void main() {
await validateBitcode(BuildMode.release, TargetPlatform.ios);
expect(bufferLogger.statusText, '');
expect(testLogger.statusText, '');
}, overrides: <Type, Generator>{
Artifacts: () => LocalEngineArtifacts('/engine', 'ios_profile', 'host_profile'),
FileSystem: () => memoryFileSystem,
ProcessManager: () => mockProcessManager,
Xcode: () => mockXcode,
Logger: () => bufferLogger,
PlistParser: () => mockPlistUtils,
});
}
......
......@@ -6,7 +6,6 @@ import 'dart:async';
import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/terminal.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/compile.dart';
......@@ -103,7 +102,6 @@ void main() {
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
OutputPreferences: () => OutputPreferences(showColor: false),
Logger: () => BufferLogger(),
Platform: kNoColorTerminalPlatform,
});
......
......@@ -6,6 +6,8 @@ import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/config.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/terminal.dart';
import 'package:platform/platform.dart';
import '../src/common.dart';
......@@ -49,7 +51,13 @@ void main() {
});
test('Config parse error', () {
final BufferLogger bufferLogger =BufferLogger();
final BufferLogger bufferLogger = BufferLogger(
terminal: AnsiTerminal(
stdio: null,
platform: const LocalPlatform(),
),
outputPreferences: OutputPreferences.test(),
);
final File file = memoryFileSystem.file('example')
..writeAsStringSync('{"hello":"bar');
config = Config(file, bufferLogger);
......
......@@ -6,7 +6,6 @@ import 'dart:async';
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/compile.dart';
import 'package:flutter_tools/src/devfs.dart';
......@@ -269,12 +268,10 @@ void main() {
group('hot attach', () {
MockResidentCompiler residentCompiler = MockResidentCompiler();
BufferLogger mockLogger;
MockLocalEngineArtifacts mockArtifacts;
setUp(() {
residentCompiler = MockResidentCompiler();
mockLogger = BufferLogger();
mockArtifacts = MockLocalEngineArtifacts();
});
......@@ -298,13 +295,12 @@ void main() {
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
).attach();
expect(exitCode, 2);
expect(mockLogger.statusText, contains('If you are using an emulator running Android Q Beta, '
expect(testLogger.statusText, contains('If you are using an emulator running Android Q Beta, '
'consider using an emulator running API level 29 or lower.'));
expect(mockLogger.statusText, contains('Learn more about the status of this issue on '
expect(testLogger.statusText, contains('Learn more about the status of this issue on '
'https://issuetracker.google.com/issues/132325318'));
}, overrides: <Type, Generator>{
Artifacts: () => mockArtifacts,
Logger: () => mockLogger,
HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true),
});
......@@ -327,13 +323,12 @@ void main() {
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
).attach();
expect(exitCode, 2);
expect(mockLogger.statusText, contains('If you are using an emulator running Android Q Beta, '
expect(testLogger.statusText, contains('If you are using an emulator running Android Q Beta, '
'consider using an emulator running API level 29 or lower.'));
expect(mockLogger.statusText, contains('Learn more about the status of this issue on '
expect(testLogger.statusText, contains('Learn more about the status of this issue on '
'https://issuetracker.google.com/issues/132325318'));
}, overrides: <Type, Generator>{
Artifacts: () => mockArtifacts,
Logger: () => mockLogger,
HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true),
});
});
......
......@@ -654,6 +654,8 @@ class MockConfig extends Mock implements Config {}
Stream<String> mockTerminalStdInStream;
class TestTerminal extends AnsiTerminal {
TestTerminal() : super(stdio: globals.stdio, platform: globals.platform);
@override
String bolden(String message) => '<bold>$message</bold>';
......
......@@ -4,22 +4,36 @@
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/terminal.dart';
import 'package:flutter_tools/src/run_hot.dart';
import 'package:platform/platform.dart';
import '../src/common.dart';
import '../src/mocks.dart';
// assumption: tests have a timeout less than 100 days
final DateTime inFuture = DateTime.now().add(const Duration(days: 100));
void main() {
BufferLogger bufferLogger;
setUp(() {
bufferLogger = BufferLogger(
terminal: AnsiTerminal(
stdio: MockStdio(),
platform: FakePlatform(),
),
outputPreferences: OutputPreferences.test(),
);
});
for (final bool asyncScanning in <bool>[true, false]) {
testWithoutContext('No last compile, asyncScanning: $asyncScanning', () async {
final ProjectFileInvalidator projectFileInvalidator = ProjectFileInvalidator(
fileSystem: MemoryFileSystem(),
platform: FakePlatform(),
logger: BufferLogger(),
logger: bufferLogger,
);
expect(
......@@ -37,7 +51,7 @@ void main() {
final ProjectFileInvalidator projectFileInvalidator = ProjectFileInvalidator(
fileSystem: MemoryFileSystem(),
platform: FakePlatform(),
logger: BufferLogger(),
logger: bufferLogger,
);
expect(
......@@ -55,7 +69,7 @@ void main() {
final ProjectFileInvalidator projectFileInvalidator = ProjectFileInvalidator(
fileSystem: MemoryFileSystem(),
platform: FakePlatform(),
logger: BufferLogger(),
logger: bufferLogger,
);
expect(
......
......@@ -7,6 +7,7 @@ import 'dart:async';
import 'package:dwds/dwds.dart';
import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/terminal.dart';
import 'package:flutter_tools/src/base/net.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/device.dart';
......@@ -17,6 +18,7 @@ import 'package:flutter_tools/src/build_runner/resident_web_runner.dart';
import 'package:flutter_tools/src/build_runner/web_fs.dart';
import 'package:meta/meta.dart';
import 'package:mockito/mockito.dart';
import 'package:platform/platform.dart';
import 'package:vm_service/vm_service.dart';
import '../src/common.dart';
......@@ -84,7 +86,13 @@ void main() {
expect(debugConnectionInfo.wsUri, null);
verify(mockStatus.stop()).called(1);
}, overrides: <Type, Generator>{
Logger: () => DelegateLogger(BufferLogger()),
Logger: () => DelegateLogger(BufferLogger(
terminal: AnsiTerminal(
stdio: null,
platform: const LocalPlatform(),
),
outputPreferences: OutputPreferences.test(),
)),
}));
test('Can full restart after attaching', () => testbed.run(() async {
......
......@@ -11,6 +11,7 @@ import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/net.dart';
import 'package:flutter_tools/src/base/terminal.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/build_runner/resident_web_runner.dart';
import 'package:flutter_tools/src/build_runner/web_fs.dart';
......@@ -26,6 +27,7 @@ import 'package:flutter_tools/src/web/chrome.dart';
import 'package:flutter_tools/src/web/web_device.dart';
import 'package:meta/meta.dart';
import 'package:mockito/mockito.dart';
import 'package:platform/platform.dart';
import 'package:vm_service/vm_service.dart';
import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';
......@@ -257,7 +259,13 @@ void main() {
expect(bufferLogger.statusText, contains('Debug service listening on ws://127.0.0.1/abcd/'));
expect(debugConnectionInfo.wsUri.toString(), 'ws://127.0.0.1/abcd/');
}, overrides: <Type, Generator>{
Logger: () => DelegateLogger(BufferLogger()),
Logger: () => DelegateLogger(BufferLogger(
terminal: AnsiTerminal(
stdio: null,
platform: const LocalPlatform(),
),
outputPreferences: OutputPreferences.test(),
)),
}));
test('Can successfully run and disconnect with --no-resident', () => testbed.run(() async {
......@@ -1080,7 +1088,13 @@ void main() {
await expectation;
verify(mockStatus.stop()).called(2);
}, overrides: <Type, Generator>{
Logger: () => DelegateLogger(BufferLogger())
Logger: () => DelegateLogger(BufferLogger(
terminal: AnsiTerminal(
stdio: null,
platform: const LocalPlatform(),
),
outputPreferences: OutputPreferences.test(),
))
}));
}
......
......@@ -7,10 +7,12 @@ import 'dart:io';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/terminal.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/vmservice.dart';
import 'package:json_rpc_2/json_rpc_2.dart' as rpc;
import 'package:mockito/mockito.dart';
import 'package:platform/platform.dart';
import 'package:quiver/testing/async.dart';
import '../src/common.dart';
......@@ -167,6 +169,7 @@ class MockPeer implements rpc.Peer {
void main() {
MockStdio mockStdio;
group('VMService', () {
setUp(() {
mockStdio = MockStdio();
});
......@@ -188,8 +191,12 @@ void main() {
expect(mockStdio.writtenToStderr.join(''), '');
});
}, overrides: <Type, Generator>{
Logger: () => StdoutLogger(),
Stdio: () => mockStdio,
Logger: () => StdoutLogger(
outputPreferences: OutputPreferences.test(),
stdio: mockStdio,
terminal: AnsiTerminal(stdio: mockStdio, platform: const LocalPlatform()),
timeoutConfiguration: const TimeoutConfiguration(),
),
WebSocketConnector: () => (String url, {CompressionOptions compression}) async => throw const SocketException('test'),
});
......@@ -263,8 +270,12 @@ void main() {
expect(mockStdio.writtenToStderr.join(''), '');
});
}, overrides: <Type, Generator>{
Logger: () => StdoutLogger(),
Stdio: () => mockStdio,
Logger: () => StdoutLogger(
outputPreferences: outputPreferences,
terminal: AnsiTerminal(stdio: mockStdio, platform: const LocalPlatform()),
stdio: mockStdio,
timeoutConfiguration: const TimeoutConfiguration(),
),
});
testUsingContext('registers hot UI method', () {
......@@ -276,8 +287,12 @@ void main() {
expect(mockPeer.registeredMethods, contains('reloadMethod'));
});
}, overrides: <Type, Generator>{
Logger: () => StdoutLogger(),
Stdio: () => mockStdio,
Logger: () => StdoutLogger(
outputPreferences: outputPreferences,
terminal: AnsiTerminal(stdio: mockStdio, platform: const LocalPlatform()),
stdio: mockStdio,
timeoutConfiguration: const TimeoutConfiguration(),
),
});
testUsingContext('registers flutterMemoryInfo service', () {
......@@ -290,8 +305,12 @@ void main() {
expect(mockPeer.registeredMethods, contains('flutterMemoryInfo'));
});
}, overrides: <Type, Generator>{
Logger: () => StdoutLogger(),
Stdio: () => mockStdio,
Logger: () => StdoutLogger(
outputPreferences: outputPreferences,
terminal: AnsiTerminal(stdio: mockStdio, platform: const LocalPlatform()),
stdio: mockStdio,
timeoutConfiguration: const TimeoutConfiguration(),
),
});
});
}
......
......@@ -92,6 +92,7 @@ void testUsingContext(
return context.run<dynamic>(
name: 'mocks',
overrides: <Type, Generator>{
AnsiTerminal: () => AnsiTerminal(platform: globals.platform, stdio: globals.stdio),
Config: () => buildConfig(globals.fs),
DeviceManager: () => FakeDeviceManager(),
Doctor: () => FakeDoctor(),
......@@ -103,7 +104,10 @@ void testUsingContext(
return mock;
},
OutputPreferences: () => OutputPreferences.test(),
Logger: () => BufferLogger(),
Logger: () => BufferLogger(
terminal: globals.terminal,
outputPreferences: outputPreferences,
),
OperatingSystemUtils: () => FakeOperatingSystemUtils(),
PersistentToolState: () => buildPersistentToolState(globals.fs),
SimControl: () => MockSimControl(),
......
......@@ -38,7 +38,10 @@ final Map<Type, Generator> _testbedDefaults = <Type, Generator>{
// Keeps tests fast by avoiding the actual file system.
FileSystem: () => MemoryFileSystem(style: globals.platform.isWindows ? FileSystemStyle.windows : FileSystemStyle.posix),
ProcessManager: () => FakeProcessManager.any(),
Logger: () => BufferLogger(), // Allows reading logs and prevents stdout.
Logger: () => BufferLogger(
terminal: AnsiTerminal(stdio: globals.stdio, platform: globals.platform), // Danger, using real stdio.
outputPreferences: OutputPreferences.test(),
), // Allows reading logs and prevents stdout.
OperatingSystemUtils: () => FakeOperatingSystemUtils(),
OutputPreferences: () => OutputPreferences.test(), // configures BufferLogger to avoid color codes.
Usage: () => NoOpUsage(), // prevent addition of analytics from burdening test mocks
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment