[flutter_tools] Remove context usage from status and clean up test cases (#48446)

This commit is contained in:
Jonah Williams 2020-01-10 09:36:49 -08:00 committed by GitHub
parent 5f4e4a3f8a
commit 749589d896
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 238 additions and 121 deletions

View file

@ -5,6 +5,7 @@
import 'dart:async';
import 'package:meta/meta.dart';
import 'package:platform/platform.dart';
import '../base/context.dart';
import '../globals.dart' as globals;
@ -21,6 +22,15 @@ const Duration _kSlowOperation = Duration(minutes: 2);
/// If not provided via injection, a default instance is provided.
TimeoutConfiguration get timeoutConfiguration => context.get<TimeoutConfiguration>() ?? const TimeoutConfiguration();
/// A factory for generating [Stopwatch] instances for [Status] instances.
class StopwatchFactory {
/// const constructor so that subclasses may be const.
const StopwatchFactory();
/// Create a new [Stopwatch] instance.
Stopwatch createStopwatch() => Stopwatch();
}
class TimeoutConfiguration {
const TimeoutConfiguration();
@ -166,11 +176,15 @@ class StdoutLogger extends Logger {
@required Stdio stdio,
@required OutputPreferences outputPreferences,
@required TimeoutConfiguration timeoutConfiguration,
@required Platform platform,
StopwatchFactory stopwatchFactory = const StopwatchFactory(),
})
: _stdio = stdio,
_terminal = terminal,
_timeoutConfiguration = timeoutConfiguration,
_outputPreferences = outputPreferences;
_outputPreferences = outputPreferences,
_stopwatchFactory = stopwatchFactory,
_platform = platform;
@override
final AnsiTerminal _terminal;
@ -179,6 +193,8 @@ class StdoutLogger extends Logger {
@override
final TimeoutConfiguration _timeoutConfiguration;
final Stdio _stdio;
final StopwatchFactory _stopwatchFactory;
final Platform _platform;
Status _status;
@ -273,7 +289,8 @@ class StdoutLogger extends Logger {
return SilentStatus(
timeout: timeout,
onFinish: _clearStatus,
timeoutConfiguration: _timeoutConfiguration
timeoutConfiguration: _timeoutConfiguration,
stopwatch: _stopwatchFactory.createStopwatch(),
)..start();
}
if (supportsColor) {
@ -285,6 +302,8 @@ class StdoutLogger extends Logger {
onFinish: _clearStatus,
stdio: _stdio,
timeoutConfiguration: _timeoutConfiguration,
platform: _platform,
stopwatch: _stopwatchFactory.createStopwatch(),
)..start();
} else {
_status = SummaryStatus(
@ -294,6 +313,7 @@ class StdoutLogger extends Logger {
onFinish: _clearStatus,
stdio: _stdio,
timeoutConfiguration: _timeoutConfiguration,
stopwatch: _stopwatchFactory.createStopwatch(),
)..start();
}
return _status;
@ -321,11 +341,15 @@ class WindowsStdoutLogger extends StdoutLogger {
@required Stdio stdio,
@required OutputPreferences outputPreferences,
@required TimeoutConfiguration timeoutConfiguration,
@required Platform platform,
StopwatchFactory stopwatchFactory = const StopwatchFactory(),
}) : super(
terminal: terminal,
stdio: stdio,
outputPreferences: outputPreferences,
timeoutConfiguration: timeoutConfiguration,
stopwatchFactory: stopwatchFactory,
platform: platform,
);
@override
@ -342,10 +366,12 @@ class BufferLogger extends Logger {
BufferLogger({
@required AnsiTerminal terminal,
@required OutputPreferences outputPreferences,
TimeoutConfiguration timeoutConfiguration = const TimeoutConfiguration()
TimeoutConfiguration timeoutConfiguration = const TimeoutConfiguration(),
StopwatchFactory stopwatchFactory = const StopwatchFactory(),
}) : _outputPreferences = outputPreferences,
_terminal = terminal,
_timeoutConfiguration = timeoutConfiguration;
_timeoutConfiguration = timeoutConfiguration,
_stopwatchFactory = stopwatchFactory;
@override
final OutputPreferences _outputPreferences;
@ -356,6 +382,8 @@ class BufferLogger extends Logger {
@override
final TimeoutConfiguration _timeoutConfiguration;
final StopwatchFactory _stopwatchFactory;
@override
bool get isVerbose => false;
@ -437,7 +465,7 @@ class BufferLogger extends Logger {
return SilentStatus(
timeout: timeout,
timeoutConfiguration: _timeoutConfiguration,
stopwatch: _stopwatchFactory.createStopwatch(),
)..start();
}
@ -453,8 +481,10 @@ class BufferLogger extends Logger {
}
class VerboseLogger extends Logger {
VerboseLogger(this.parent, { @required Stopwatch stopwatch }) :
_stopwatch = stopwatch ?? context.get<Stopwatch>() ?? Stopwatch() {
VerboseLogger(this.parent, {
StopwatchFactory stopwatchFactory = const StopwatchFactory()
}) : _stopwatch = stopwatchFactory.createStopwatch(),
_stopwatchFactory = stopwatchFactory {
_stopwatch.start();
}
@ -471,6 +501,8 @@ class VerboseLogger extends Logger {
@override
TimeoutConfiguration get _timeoutConfiguration => parent._timeoutConfiguration;
final StopwatchFactory _stopwatchFactory;
@override
bool get isVerbose => true;
@ -529,10 +561,12 @@ class VerboseLogger extends Logger {
}) {
assert(progressIndicatorPadding != null);
printStatus(message);
final Stopwatch timer = Stopwatch()..start();
final Stopwatch timer = _stopwatchFactory.createStopwatch()..start();
return SilentStatus(
timeout: timeout,
timeoutConfiguration: _timeoutConfiguration,
// This is intentionally a different stopwatch than above.
stopwatch: _stopwatchFactory.createStopwatch(),
onFinish: () {
String time;
if (timeout == null || timeout > _timeoutConfiguration.fastOperation) {
@ -623,28 +657,36 @@ abstract class Status {
@required this.timeout,
@required TimeoutConfiguration timeoutConfiguration,
this.onFinish,
}) : _timeoutConfiguration = timeoutConfiguration;
@required Stopwatch stopwatch,
}) : _timeoutConfiguration = timeoutConfiguration,
_stopwatch = stopwatch;
/// A [SilentStatus] or an [AnsiSpinner] (depending on whether the
/// terminal is fancy enough), already started.
factory Status.withSpinner({
@required Duration timeout,
@required TimeoutConfiguration timeoutConfiguration,
@required Stopwatch stopwatch,
@required bool supportsColor,
@required Platform platform,
VoidCallback onFinish,
SlowWarningCallback slowWarningCallback,
}) {
if (globals.terminal.supportsColor) {
if (supportsColor) {
return AnsiSpinner(
timeout: timeout,
onFinish: onFinish,
slowWarningCallback: slowWarningCallback,
timeoutConfiguration: timeoutConfiguration,
stopwatch: stopwatch,
platform: platform,
)..start();
}
return SilentStatus(
timeout: timeout,
onFinish: onFinish,
timeoutConfiguration: timeoutConfiguration,
stopwatch: stopwatch,
)..start();
}
@ -653,7 +695,7 @@ abstract class Status {
final TimeoutConfiguration _timeoutConfiguration;
@protected
final Stopwatch _stopwatch = context.get<Stopwatch>() ?? Stopwatch();
final Stopwatch _stopwatch;
@protected
@visibleForTesting
@ -704,11 +746,13 @@ class SilentStatus extends Status {
SilentStatus({
@required Duration timeout,
@required TimeoutConfiguration timeoutConfiguration,
@required Stopwatch stopwatch,
VoidCallback onFinish,
}) : super(
timeout: timeout,
onFinish: onFinish,
timeoutConfiguration: timeoutConfiguration,
stopwatch: stopwatch,
);
}
@ -718,14 +762,20 @@ class SummaryStatus extends Status {
SummaryStatus({
this.message = '',
@required Duration timeout,
@required TimeoutConfiguration timeoutConfiguration,
@required Stopwatch stopwatch,
this.padding = kDefaultStatusPadding,
VoidCallback onFinish,
Stdio stdio,
@required TimeoutConfiguration timeoutConfiguration,
}) : assert(message != null),
assert(padding != null),
_stdio = stdio ?? globals.stdio,
super(timeout: timeout, onFinish: onFinish, timeoutConfiguration: timeoutConfiguration);
super(
timeout: timeout,
onFinish: onFinish,
timeoutConfiguration: timeoutConfiguration,
stopwatch: stopwatch,
);
final String message;
final int padding;
@ -794,16 +844,25 @@ class SummaryStatus extends Status {
class AnsiSpinner extends Status {
AnsiSpinner({
@required Duration timeout,
@required TimeoutConfiguration timeoutConfiguration,
@required Stopwatch stopwatch,
@required Platform platform,
VoidCallback onFinish,
this.slowWarningCallback,
Stdio stdio,
@required TimeoutConfiguration timeoutConfiguration,
}) : _stdio = stdio ?? globals.stdio,
super(timeout: timeout, onFinish: onFinish, timeoutConfiguration: timeoutConfiguration);
_isWindows = platform.isWindows,
super(
timeout: timeout,
onFinish: onFinish,
timeoutConfiguration: timeoutConfiguration,
stopwatch: stopwatch,
);
final String _backspaceChar = '\b';
final String _clearChar = ' ';
final Stdio _stdio;
final bool _isWindows;
bool timedOut = false;
@ -811,9 +870,9 @@ class AnsiSpinner extends Status {
Timer timer;
// Windows console font has a limited set of Unicode characters.
List<String> get _animation => globals.platform.isWindows
? <String>[r'-', r'\', r'|', r'/']
: <String>['', '', '', '', '', '', '', ''];
List<String> get _animation => _isWindows
? const <String>[r'-', r'\', r'|', r'/']
: const <String>['', '', '', '', '', '', '', ''];
static const String _defaultSlowWarning = '(This is taking an unexpectedly long time.)';
final SlowWarningCallback slowWarningCallback;
@ -903,16 +962,25 @@ const int _kTimePadding = 8; // should fit "99,999ms"
class AnsiStatus extends AnsiSpinner {
AnsiStatus({
this.message = '',
@required Duration timeout,
this.multilineOutput = false,
this.padding = kDefaultStatusPadding,
@required Duration timeout,
@required Stopwatch stopwatch,
@required Platform platform,
VoidCallback onFinish,
Stdio stdio,
TimeoutConfiguration timeoutConfiguration,
}) : assert(message != null),
assert(multilineOutput != null),
assert(padding != null),
super(timeout: timeout, onFinish: onFinish, stdio: stdio, timeoutConfiguration: timeoutConfiguration);
super(
timeout: timeout,
onFinish: onFinish,
stdio: stdio,
timeoutConfiguration: timeoutConfiguration,
stopwatch: stopwatch,
platform: platform,
);
final String message;
final bool multilineOutput;

View file

@ -974,7 +974,11 @@ class NotifyingLogger extends Logger {
}) {
assert(timeout != null);
printStatus(message);
return SilentStatus(timeout: timeout, timeoutConfiguration: timeoutConfiguration);
return SilentStatus(
timeout: timeout,
timeoutConfiguration: timeoutConfiguration,
stopwatch: Stopwatch(),
);
}
void dispose() {
@ -1181,7 +1185,7 @@ class _AppRunLogger extends Logger {
'progressId': progressId,
'finished': true,
});
})..start();
}, stopwatch: Stopwatch())..start();
return _status;
}

View file

@ -108,12 +108,14 @@ Future<T> runInContext<T>(
stdio: globals.stdio,
outputPreferences: outputPreferences,
timeoutConfiguration: timeoutConfiguration,
platform: globals.platform,
)
: StdoutLogger(
terminal: globals.terminal,
stdio: globals.stdio,
outputPreferences: outputPreferences,
timeoutConfiguration: timeoutConfiguration,
platform: globals.platform,
),
MacOSWorkflow: () => const MacOSWorkflow(),
MDnsObservatoryDiscovery: () => MDnsObservatoryDiscovery(),

View file

@ -259,6 +259,9 @@ class Doctor {
timeout: timeoutConfiguration.fastOperation,
slowWarningCallback: () => validator.slowWarning,
timeoutConfiguration: timeoutConfiguration,
stopwatch: Stopwatch(),
supportsColor: globals.terminal.supportsColor,
platform: globals.platform,
);
ValidationResult result;
try {

View file

@ -285,6 +285,9 @@ class XcodeProjectInterpreter {
final Status status = Status.withSpinner(
timeout: timeoutConfiguration.fastOperation,
timeoutConfiguration: timeoutConfiguration,
platform: globals.platform,
stopwatch: Stopwatch(),
supportsColor: globals.terminal.supportsColor,
);
final List<String> showBuildSettingsCommand = <String>[
_executable,

View file

@ -239,7 +239,7 @@ class FlutterCommandRunner extends CommandRunner<void> {
// Check for verbose.
if (topLevelResults['verbose'] as bool) {
// Override the logger.
contextOverrides[Logger] = VerboseLogger(globals.logger, stopwatch: Stopwatch());
contextOverrides[Logger] = VerboseLogger(globals.logger);
}
// Don't set wrapColumns unless the user said to: if it's set, then all

View file

@ -704,7 +704,11 @@ class StreamLogger extends Logger {
int progressIndicatorPadding = kDefaultStatusPadding,
}) {
_log('[progress] $message');
return SilentStatus(timeout: timeout, timeoutConfiguration: timeoutConfiguration)..start();
return SilentStatus(
timeout: timeout,
timeoutConfiguration: timeoutConfiguration,
stopwatch: Stopwatch(),
)..start();
}
bool _interrupt = false;

View file

@ -5,24 +5,20 @@
import 'dart:convert' show jsonEncode;
import 'package:platform/platform.dart';
import 'package:flutter_tools/src/base/context.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;
import 'package:quiver/testing/async.dart';
import '../../src/common.dart';
import '../../src/context.dart';
import '../../src/mocks.dart';
final Platform _kNoAnsiPlatform = FakePlatform.fromPlatform(const LocalPlatform())..stdoutSupportsAnsi = false;
final String red = RegExp.escape(AnsiTerminal.red);
final String bold = RegExp.escape(AnsiTerminal.bold);
final String resetBold = RegExp.escape(AnsiTerminal.resetBold);
final String resetColor = RegExp.escape(AnsiTerminal.resetColor);
void main() {
final String red = RegExp.escape(AnsiTerminal.red);
final String bold = RegExp.escape(AnsiTerminal.bold);
final String resetBold = RegExp.escape(AnsiTerminal.resetBold);
final String resetColor = RegExp.escape(AnsiTerminal.resetColor);
group('AppContext', () {
FakeStopwatch fakeStopWatch;
@ -37,7 +33,10 @@ void main() {
),
outputPreferences: OutputPreferences.test(showColor: false),
);
final VerboseLogger verboseLogger = VerboseLogger(mockLogger, stopwatch: fakeStopWatch);
final VerboseLogger verboseLogger = VerboseLogger(
mockLogger,
stopwatchFactory: FakeStopwatchFactory(fakeStopWatch),
);
verboseLogger.printStatus('Hey Hey Hey Hey');
verboseLogger.printTrace('Oooh, I do I do I do');
@ -57,7 +56,9 @@ void main() {
),
outputPreferences: OutputPreferences.test(showColor: true),
);
final VerboseLogger verboseLogger = VerboseLogger(mockLogger, stopwatch: fakeStopWatch);
final VerboseLogger verboseLogger = VerboseLogger(
mockLogger, stopwatchFactory: FakeStopwatchFactory(fakeStopWatch),
);
verboseLogger.printStatus('Hey Hey Hey Hey');
verboseLogger.printTrace('Oooh, I do I do I do');
@ -77,25 +78,16 @@ void main() {
group('Spinners', () {
MockStdio mockStdio;
FakeStopwatch mockStopwatch;
FakeStopwatchFactory stopwatchFactory;
int called;
const List<String> testPlatforms = <String>['linux', 'macos', 'windows', 'fuchsia'];
final RegExp secondDigits = RegExp(r'[0-9,.]*[0-9]m?s');
AnsiStatus _createAnsiStatus() {
return AnsiStatus(
message: 'Hello world',
timeout: const Duration(seconds: 2),
padding: 20,
onFinish: () => called += 1,
stdio: mockStdio,
timeoutConfiguration: const TimeoutConfiguration(),
);
}
setUp(() {
mockStopwatch = FakeStopwatch();
mockStdio = MockStdio();
called = 0;
stopwatchFactory = FakeStopwatchFactory(mockStopwatch);
});
List<String> outputStdout() => mockStdio.writtenToStdout.join('').split('\n');
@ -109,7 +101,21 @@ void main() {
}
for (final String testOs in testPlatforms) {
testUsingContext('AnsiSpinner works for $testOs (1)', () async {
Platform currentPlatform() => FakePlatform(operatingSystem: testOs);
AnsiStatus createAnsiStatus() {
return AnsiStatus(
message: 'Hello world',
timeout: const Duration(seconds: 2),
padding: 20,
onFinish: () => called += 1,
stdio: mockStdio,
timeoutConfiguration: const TimeoutConfiguration(),
stopwatch: stopwatchFactory.createStopwatch(),
platform: currentPlatform(),
);
}
testWithoutContext('AnsiSpinner works for $testOs (1)', () async {
bool done = false;
mockStopwatch = FakeStopwatch();
FakeAsync().run((FakeAsync time) {
@ -117,38 +123,35 @@ void main() {
timeout: const Duration(hours: 10),
stdio: mockStdio,
timeoutConfiguration: const TimeoutConfiguration(),
stopwatch: stopwatchFactory.createStopwatch(),
platform: currentPlatform(),
)..start();
doWhileAsync(time, () => ansiSpinner.ticks < 10);
List<String> lines = outputStdout();
expect(lines[0], startsWith(
globals.platform.isWindows
currentPlatform().isWindows
? ' \b\\\b|\b/\b-\b\\\b|\b/\b-'
: ' \b\b\b\b\b\b\b\b\b\b'
),
);
expect(lines[0].endsWith('\n'), isFalse);
expect(lines.length, equals(1));
ansiSpinner.stop();
lines = outputStdout();
expect(lines[0], endsWith('\b \b'));
expect(lines.length, equals(1));
// Verify that stopping or canceling multiple times throws.
expect(() {
ansiSpinner.stop();
}, throwsA(isInstanceOf<AssertionError>()));
expect(() {
ansiSpinner.cancel();
}, throwsA(isInstanceOf<AssertionError>()));
expect(ansiSpinner.stop, throwsA(isInstanceOf<AssertionError>()));
expect(ansiSpinner.cancel, throwsA(isInstanceOf<AssertionError>()));
done = true;
});
expect(done, isTrue);
}, overrides: <Type, Generator>{
Platform: () => FakePlatform(operatingSystem: testOs),
Stopwatch: () => mockStopwatch,
});
testUsingContext('AnsiSpinner works for $testOs (2)', () async {
testWithoutContext('AnsiSpinner works for $testOs (2)', () async {
bool done = false;
mockStopwatch = FakeStopwatch();
FakeAsync().run((FakeAsync time) {
@ -156,13 +159,17 @@ void main() {
timeout: const Duration(seconds: 2),
stdio: mockStdio,
timeoutConfiguration: const TimeoutConfiguration(),
stopwatch: mockStopwatch,
platform: FakePlatform(operatingSystem: testOs),
)..start();
mockStopwatch.elapsed = const Duration(seconds: 1);
doWhileAsync(time, () => ansiSpinner.ticks < 10); // one second
expect(ansiSpinner.seemsSlow, isFalse);
expect(outputStdout().join('\n'), isNot(contains('This is taking an unexpectedly long time.')));
mockStopwatch.elapsed = const Duration(seconds: 3);
doWhileAsync(time, () => ansiSpinner.ticks < 30); // three seconds
expect(ansiSpinner.seemsSlow, isTrue);
// Check the 2nd line to verify there's a newline before the warning
expect(outputStdout()[1], contains('This is taking an unexpectedly long time.'));
@ -171,13 +178,9 @@ void main() {
done = true;
});
expect(done, isTrue);
}, overrides: <Type, Generator>{
Platform: () => FakePlatform(operatingSystem: testOs),
Stopwatch: () => mockStopwatch,
});
// Uses Stopwatch from context.
testUsingContext('Stdout startProgress on colored terminal for $testOs', () async {
testWithoutContext('Stdout startProgress on colored terminal for $testOs', () async {
bool done = false;
FakeAsync().run((FakeAsync time) {
final Logger logger = StdoutLogger(
@ -188,27 +191,31 @@ void main() {
stdio: mockStdio,
outputPreferences: OutputPreferences.test(showColor: true),
timeoutConfiguration: const TimeoutConfiguration(),
stopwatchFactory: stopwatchFactory,
platform: currentPlatform(),
);
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));
expect(outputStderr().first, isEmpty);
// the 5 below is the margin that is always included between the message and the time.
expect(outputStdout().join('\n'), matches(globals.platform.isWindows ? r'^Hello {15} {5} {8}[\b]{8} {7}\\$' :
r'^Hello {15} {5} {8}[\b]{8} {7}⣽$'));
expect(outputStdout().join('\n'),
matches(currentPlatform().isWindows ? r'^Hello {15} {5} {8}[\b]{8} {7}\\$' :
r'^Hello {15} {5} {8}[\b]{8} {7}⣽$'));
status.stop();
expect(outputStdout().join('\n'), matches(globals.platform.isWindows ? r'^Hello {15} {5} {8}[\b]{8} {7}\\[\b]{8} {8}[\b]{8}[\d, ]{4}[\d]\.[\d]s[\n]$' :
r'^Hello {15} {5} {8}[\b]{8} {7}⣽[\b]{8} {8}[\b]{8}[\d, ]{4}[\d]\.[\d]s[\n]$'));
expect(outputStdout().join('\n'),
matches(currentPlatform().isWindows ? r'^Hello {15} {5} {8}[\b]{8} {7}\\[\b]{8} {8}[\b]{8}[\d, ]{4}[\d]\.[\d]s[\n]$' :
r'^Hello {15} {5} {8}[\b]{8} {7}⣽[\b]{8} {8}[\b]{8}[\d, ]{4}[\d]\.[\d]s[\n]$'));
done = true;
});
expect(done, isTrue);
});
testUsingContext('Stdout startProgress on colored terminal pauses on $testOs', () async {
testWithoutContext('Stdout startProgress on colored terminal pauses on $testOs', () async {
bool done = false;
FakeAsync().run((FakeAsync time) {
mockStopwatch.elapsed = const Duration(seconds: 5);
@ -220,6 +227,8 @@ void main() {
stdio: mockStdio,
outputPreferences: OutputPreferences.test(showColor: true),
timeoutConfiguration: const TimeoutConfiguration(),
stopwatchFactory: stopwatchFactory,
platform: currentPlatform(),
);
final Status status = logger.startProgress(
'Knock Knock, Who\'s There',
@ -228,8 +237,9 @@ void main() {
);
logger.printStatus('Rude Interrupting Cow');
status.stop();
final String a = globals.platform.isWindows ? '\\' : '';
final String b = globals.platform.isWindows ? '|' : '';
final String a = currentPlatform().isWindows ? '\\' : '';
final String b = currentPlatform().isWindows ? '|' : '';
expect(
outputStdout().join('\n'),
'Knock Knock, Who\'s There ' // initial message
@ -250,22 +260,22 @@ void main() {
done = true;
});
expect(done, isTrue);
}, overrides: <Type, Generator>{
Stopwatch: () => mockStopwatch,
});
testUsingContext('AnsiStatus works for $testOs', () {
final AnsiStatus ansiStatus = _createAnsiStatus();
testWithoutContext('AnsiStatus works for $testOs', () {
final AnsiStatus ansiStatus = createAnsiStatus();
bool done = false;
FakeAsync().run((FakeAsync time) {
ansiStatus.start();
mockStopwatch.elapsed = const Duration(seconds: 1);
doWhileAsync(time, () => ansiStatus.ticks < 10); // one second
expect(ansiStatus.seemsSlow, isFalse);
expect(outputStdout().join('\n'), isNot(contains('This is taking an unexpectedly long time.')));
expect(outputStdout().join('\n'), isNot(contains('(!)')));
mockStopwatch.elapsed = const Duration(seconds: 3);
doWhileAsync(time, () => ansiStatus.ticks < 30); // three seconds
expect(ansiStatus.seemsSlow, isTrue);
expect(outputStdout().join('\n'), contains('This is taking an unexpectedly long time.'));
@ -283,20 +293,18 @@ void main() {
done = true;
});
expect(done, isTrue);
}, overrides: <Type, Generator>{
Platform: () => FakePlatform(operatingSystem: testOs),
Stopwatch: () => mockStopwatch,
});
testUsingContext('AnsiStatus works when canceled for $testOs', () async {
final AnsiStatus ansiStatus = _createAnsiStatus();
testWithoutContext('AnsiStatus works when canceled for $testOs', () async {
final AnsiStatus ansiStatus = createAnsiStatus();
bool done = false;
FakeAsync().run((FakeAsync time) {
ansiStatus.start();
mockStopwatch.elapsed = const Duration(seconds: 1);
doWhileAsync(time, () => ansiStatus.ticks < 10);
List<String> lines = outputStdout();
expect(lines[0], startsWith(globals.platform.isWindows
expect(lines[0], startsWith(testOs == 'windows'
? 'Hello world \b\b\b\b\b\b\b\b \\\b\b\b\b\b\b\b\b |\b\b\b\b\b\b\b\b /\b\b\b\b\b\b\b\b -\b\b\b\b\b\b\b\b \\\b\b\b\b\b\b\b\b |\b\b\b\b\b\b\b\b /\b\b\b\b\b\b\b\b -\b\b\b\b\b\b\b\b \\\b\b\b\b\b\b\b\b |'
: 'Hello world \b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b'));
expect(lines.length, equals(1));
@ -307,8 +315,9 @@ void main() {
lines = outputStdout();
final List<Match> matches = secondDigits.allMatches(lines[0]).toList();
expect(matches, isEmpty);
final String x = globals.platform.isWindows ? '|' : '';
expect(lines[0], endsWith('$x\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b'));
final String leading = currentPlatform().isWindows ? '|' : '';
expect(lines[0], endsWith('$leading\b\b\b\b\b\b\b\b \b\b\b\b\b\b\b\b'));
expect(called, equals(1));
expect(lines.length, equals(2));
expect(lines[1], equals(''));
@ -319,22 +328,20 @@ void main() {
done = true;
});
expect(done, isTrue);
}, overrides: <Type, Generator>{
Platform: () => FakePlatform(operatingSystem: testOs),
Stopwatch: () => mockStopwatch,
});
testUsingContext('AnsiStatus works when stopped for $testOs', () async {
final AnsiStatus ansiStatus = _createAnsiStatus();
testWithoutContext('AnsiStatus works when stopped for $testOs', () async {
final AnsiStatus ansiStatus = createAnsiStatus();
bool done = false;
FakeAsync().run((FakeAsync time) {
ansiStatus.start();
mockStopwatch.elapsed = const Duration(seconds: 1);
doWhileAsync(time, () => ansiStatus.ticks < 10);
List<String> lines = outputStdout();
expect(lines, hasLength(1));
expect(lines[0],
globals.platform.isWindows
currentPlatform().isWindows
? 'Hello world \b\b\b\b\b\b\b\b \\\b\b\b\b\b\b\b\b |\b\b\b\b\b\b\b\b /\b\b\b\b\b\b\b\b -\b\b\b\b\b\b\b\b \\\b\b\b\b\b\b\b\b |\b\b\b\b\b\b\b\b /\b\b\b\b\b\b\b\b -\b\b\b\b\b\b\b\b \\\b\b\b\b\b\b\b\b |'
: 'Hello world \b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b',
);
@ -344,7 +351,7 @@ void main() {
lines = outputStdout();
expect(lines, hasLength(2));
expect(lines[0], matches(
globals.platform.isWindows
currentPlatform().isWindows
? r'Hello world {8}[\b]{8} {7}\\[\b]{8} {7}|[\b]{8} {7}/[\b]{8} {7}-[\b]{8} {7}\\[\b]{8} {7}|[\b]{8} {7}/[\b]{8} {7}-[\b]{8} {7}\\[\b]{8} {7}|[\b]{8} {7} [\b]{8}[\d., ]{6}[\d]ms$'
: r'Hello world {8}[\b]{8} {7}⣽[\b]{8} {7}⣻[\b]{8} {7}⢿[\b]{8} {7}⡿[\b]{8} {7}⣟[\b]{8} {7}⣯[\b]{8} {7}⣷[\b]{8} {7}⣾[\b]{8} {7}⣽[\b]{8} {7}⣻[\b]{8} {7} [\b]{8}[\d., ]{5}[\d]ms$'
));
@ -353,20 +360,18 @@ void main() {
expect(times, isNotNull);
expect(times, hasLength(1));
final Match match = times.single;
expect(lines[0], endsWith(match.group(0)));
expect(called, equals(1));
expect(lines.length, equals(2));
expect(lines[1], equals(''));
// Verify that stopping or canceling multiple times throws.
expect(() { ansiStatus.stop(); }, throwsA(isInstanceOf<AssertionError>()));
expect(() { ansiStatus.cancel(); }, throwsA(isInstanceOf<AssertionError>()));
expect(ansiStatus.stop, throwsA(isInstanceOf<AssertionError>()));
expect(ansiStatus.cancel, throwsA(isInstanceOf<AssertionError>()));
done = true;
});
expect(done, isTrue);
}, overrides: <Type, Generator>{
Platform: () => FakePlatform(operatingSystem: testOs),
Stopwatch: () => mockStopwatch,
});
}
});
@ -381,11 +386,12 @@ void main() {
called = 0;
summaryStatus = SummaryStatus(
message: 'Hello world',
timeout: timeoutConfiguration.slowOperation,
timeout: const TimeoutConfiguration().slowOperation,
padding: 20,
onFinish: () => called++,
stdio: mockStdio,
timeoutConfiguration: const TimeoutConfiguration(),
stopwatch: FakeStopwatch(),
);
});
@ -401,6 +407,7 @@ void main() {
stdio: mockStdio,
outputPreferences: OutputPreferences.test(wrapText: true, wrapColumn: 40, showColor: false),
timeoutConfiguration: const TimeoutConfiguration(),
platform: FakePlatform(),
);
logger.printError('0123456789' * 15);
final List<String> lines = outputStderr();
@ -422,6 +429,7 @@ void main() {
stdio: mockStdio,
outputPreferences: OutputPreferences.test(wrapText: true, wrapColumn: 40, showColor: false),
timeoutConfiguration: const TimeoutConfiguration(),
platform: FakePlatform(),
);
logger.printError('0123456789' * 15, indent: 5);
final List<String> lines = outputStderr();
@ -446,6 +454,7 @@ void main() {
stdio: mockStdio,
outputPreferences: OutputPreferences.test(wrapText: true, wrapColumn: 40, showColor: false),
timeoutConfiguration: const TimeoutConfiguration(),
platform: FakePlatform(),
);
logger.printError('0123456789' * 15, hangingIndent: 5);
final List<String> lines = outputStderr();
@ -470,6 +479,7 @@ void main() {
stdio: mockStdio,
outputPreferences: OutputPreferences.test(wrapText: true, wrapColumn: 40, showColor: false),
timeoutConfiguration: const TimeoutConfiguration(),
platform: FakePlatform(),
);
logger.printError('0123456789' * 15, indent: 4, hangingIndent: 5);
final List<String> lines = outputStderr();
@ -494,6 +504,7 @@ void main() {
stdio: mockStdio,
outputPreferences: OutputPreferences.test(wrapText: true, wrapColumn: 40, showColor: false),
timeoutConfiguration: const TimeoutConfiguration(),
platform: FakePlatform(),
);
logger.printStatus('0123456789' * 15);
final List<String> lines = outputStdout();
@ -515,6 +526,7 @@ void main() {
stdio: mockStdio,
outputPreferences: OutputPreferences.test(wrapText: true, wrapColumn: 40, showColor: false),
timeoutConfiguration: const TimeoutConfiguration(),
platform: FakePlatform(),
);
logger.printStatus('0123456789' * 15, indent: 5);
final List<String> lines = outputStdout();
@ -539,6 +551,7 @@ void main() {
stdio: mockStdio,
outputPreferences: OutputPreferences.test(wrapText: true, wrapColumn: 40, showColor: false),
timeoutConfiguration: const TimeoutConfiguration(),
platform: FakePlatform(),
);
logger.printStatus('0123456789' * 15, hangingIndent: 5);
final List<String> lines = outputStdout();
@ -554,7 +567,7 @@ void main() {
expect(lines[5], isEmpty);
});
testUsingContext('Stdout logs are wrapped, indented, and can have hanging indent.', () async {
testWithoutContext('Stdout logs are wrapped, indented, and can have hanging indent.', () async {
final Logger logger = StdoutLogger(
terminal: AnsiTerminal(
stdio: mockStdio,
@ -563,6 +576,7 @@ void main() {
stdio: mockStdio,
outputPreferences: OutputPreferences.test(wrapText: true, wrapColumn: 40, showColor: false),
timeoutConfiguration: const TimeoutConfiguration(),
platform: FakePlatform(),
);
logger.printStatus('0123456789' * 15, indent: 4, hangingIndent: 5);
final List<String> lines = outputStdout();
@ -587,6 +601,7 @@ void main() {
stdio: mockStdio,
outputPreferences: OutputPreferences.test(showColor: true),
timeoutConfiguration: const TimeoutConfiguration(),
platform: FakePlatform(),
);
logger.printError('Pants on fire!');
final List<String> lines = outputStderr();
@ -605,6 +620,7 @@ void main() {
stdio: mockStdio,
outputPreferences: OutputPreferences.test(showColor: true),
timeoutConfiguration: const TimeoutConfiguration(),
platform: FakePlatform(),
);
logger.printStatus('All good.');
@ -623,6 +639,7 @@ void main() {
stdio: mockStdio,
outputPreferences: OutputPreferences.test(showColor: true),
timeoutConfiguration: const TimeoutConfiguration(),
platform: FakePlatform(),
);
logger.printStatus(
null,
@ -647,6 +664,7 @@ void main() {
stdio: mockStdio,
outputPreferences: OutputPreferences.test(showColor: false),
timeoutConfiguration: const TimeoutConfiguration(),
platform: FakePlatform(),
);
logger.printStatus(
null,
@ -661,8 +679,7 @@ void main() {
expect(lines[0], equals(''));
});
// Status uses Stopwatch from context.
test('Stdout startProgress on non-color terminal', () async {
testWithoutContext('Stdout startProgress on non-color terminal', () async {
bool done = false;
FakeAsync().run((FakeAsync time) {
final Logger logger = StdoutLogger(
@ -673,6 +690,7 @@ void main() {
stdio: mockStdio,
outputPreferences: OutputPreferences.test(showColor: false),
timeoutConfiguration: const TimeoutConfiguration(),
platform: FakePlatform(),
);
final Status status = logger.startProgress(
'Hello',
@ -683,17 +701,24 @@ void main() {
expect(outputStderr().length, equals(1));
expect(outputStderr().first, isEmpty);
// the 5 below is the margin that is always included between the message and the time.
expect(outputStdout().join('\n'), matches(globals.platform.isWindows ? r'^Hello {15} {5}$' :
r'^Hello {15} {5}$'));
expect(outputStdout().join('\n'), matches(r'^Hello {15} {5}$'));
status.stop();
expect(outputStdout().join('\n'), matches(globals.platform.isWindows ? r'^Hello {15} {5}[\d, ]{4}[\d]\.[\d]s[\n]$' :
r'^Hello {15} {5}[\d, ]{4}[\d]\.[\d]s[\n]$'));
expect(outputStdout().join('\n'), matches(r'^Hello {15} {5}[\d, ]{4}[\d]\.[\d]s[\n]$'));
done = true;
});
expect(done, isTrue);
});
testUsingContext('SummaryStatus works when canceled', () async {
testWithoutContext('SummaryStatus works when canceled', () async {
final SummaryStatus summaryStatus = SummaryStatus(
message: 'Hello world',
timeout: const TimeoutConfiguration().slowOperation,
padding: 20,
onFinish: () => called++,
stdio: mockStdio,
timeoutConfiguration: const TimeoutConfiguration(),
stopwatch: FakeStopwatch(),
);
summaryStatus.start();
List<String> lines = outputStdout();
expect(lines[0], startsWith('Hello world '));
@ -711,13 +736,11 @@ void main() {
expect(lines[1], equals(''));
// Verify that stopping or canceling multiple times throws.
expect(() { summaryStatus.cancel(); }, throwsA(isInstanceOf<AssertionError>()));
expect(() { summaryStatus.stop(); }, throwsA(isInstanceOf<AssertionError>()));
}, overrides: <Type, Generator>{
Platform: () => _kNoAnsiPlatform,
expect(summaryStatus.cancel, throwsA(isInstanceOf<AssertionError>()));
expect(summaryStatus.stop, throwsA(isInstanceOf<AssertionError>()));
});
testUsingContext('SummaryStatus works when stopped', () async {
testWithoutContext('SummaryStatus works when stopped', () async {
summaryStatus.start();
List<String> lines = outputStdout();
expect(lines[0], startsWith('Hello world '));
@ -736,16 +759,11 @@ void main() {
expect(lines[1], equals(''));
// Verify that stopping or canceling multiple times throws.
expect(() { summaryStatus.stop(); }, throwsA(isInstanceOf<AssertionError>()));
expect(() { summaryStatus.cancel(); }, throwsA(isInstanceOf<AssertionError>()));
}, overrides: <Type, Generator>{
Platform: () => _kNoAnsiPlatform,
expect(summaryStatus.stop, throwsA(isInstanceOf<AssertionError>()));
expect(summaryStatus.cancel, throwsA(isInstanceOf<AssertionError>()));
});
// Status still uses Stopwatch from context.
// TODO(jonahwilliams): switch to testWithoutContext once logger uses a
// TimerFactory.
test('sequential startProgress calls with StdoutLogger', () async {
testWithoutContext('sequential startProgress calls with StdoutLogger', () async {
final Logger logger = StdoutLogger(
terminal: AnsiTerminal(
stdio: mockStdio,
@ -754,6 +772,7 @@ void main() {
stdio: mockStdio,
outputPreferences: OutputPreferences.test(showColor: false),
timeoutConfiguration: const TimeoutConfiguration(),
platform: FakePlatform(),
);
logger.startProgress('AAA', timeout: const TimeoutConfiguration().fastOperation)..stop();
logger.startProgress('BBB', timeout: const TimeoutConfiguration().fastOperation)..stop();
@ -768,8 +787,7 @@ void main() {
expect(output[1], matches(RegExp(r'BBB[ ]{61}[\d, ]{5}[\d]ms')));
});
// Status still uses Stopwatch from context.
test('sequential startProgress calls with VerboseLogger and StdoutLogger', () async {
testWithoutContext('sequential startProgress calls with VerboseLogger and StdoutLogger', () async {
final Logger logger = VerboseLogger(
StdoutLogger(
terminal: AnsiTerminal(
@ -779,8 +797,9 @@ void main() {
stdio: mockStdio,
outputPreferences: OutputPreferences.test(),
timeoutConfiguration: const TimeoutConfiguration(),
platform: FakePlatform(),
),
stopwatch: FakeStopwatch(),
stopwatchFactory: FakeStopwatchFactory(),
);
logger.startProgress('AAA', timeout: const TimeoutConfiguration().fastOperation)..stop();
logger.startProgress('BBB', timeout: const TimeoutConfiguration().fastOperation)..stop();
@ -794,8 +813,7 @@ void main() {
]);
});
// Status still uses Stopwatch from context.
test('sequential startProgress calls with BufferLogger', () async {
testWithoutContext('sequential startProgress calls with BufferLogger', () async {
final BufferLogger logger = BufferLogger(
terminal: AnsiTerminal(
stdio: mockStdio,
@ -846,3 +864,14 @@ class FakeStopwatch implements Stopwatch {
@override
String toString() => '$runtimeType $elapsed $isRunning';
}
class FakeStopwatchFactory implements StopwatchFactory {
FakeStopwatchFactory([this.stopwatch]);
Stopwatch stopwatch;
@override
Stopwatch createStopwatch() {
return stopwatch ?? FakeStopwatch();
}
}

View file

@ -196,6 +196,7 @@ void main() {
stdio: mockStdio,
terminal: AnsiTerminal(stdio: mockStdio, platform: const LocalPlatform()),
timeoutConfiguration: const TimeoutConfiguration(),
platform: FakePlatform(),
),
WebSocketConnector: () => (String url, {CompressionOptions compression}) async => throw const SocketException('test'),
});
@ -275,6 +276,7 @@ void main() {
terminal: AnsiTerminal(stdio: mockStdio, platform: const LocalPlatform()),
stdio: mockStdio,
timeoutConfiguration: const TimeoutConfiguration(),
platform: FakePlatform(),
),
});
@ -292,6 +294,7 @@ void main() {
terminal: AnsiTerminal(stdio: mockStdio, platform: const LocalPlatform()),
stdio: mockStdio,
timeoutConfiguration: const TimeoutConfiguration(),
platform: FakePlatform(),
),
});
@ -310,6 +313,7 @@ void main() {
terminal: AnsiTerminal(stdio: mockStdio, platform: const LocalPlatform()),
stdio: mockStdio,
timeoutConfiguration: const TimeoutConfiguration(),
platform: FakePlatform(),
),
});
});