Deflake AnsiSpinner tests (#27825)

This commit is contained in:
Dan Field 2019-02-12 12:28:45 -08:00 committed by GitHub
parent e711fd9744
commit fdcb861b05
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 60 additions and 15 deletions

View file

@ -6,6 +6,7 @@ import 'dart:async';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import '../base/context.dart';
import 'io.dart'; import 'io.dart';
import 'platform.dart'; import 'platform.dart';
import 'terminal.dart'; import 'terminal.dart';
@ -464,7 +465,7 @@ abstract class Status {
final VoidCallback onFinish; final VoidCallback onFinish;
@protected @protected
final Stopwatch _stopwatch = Stopwatch(); final Stopwatch _stopwatch = context[Stopwatch] ?? Stopwatch();
@protected @protected
@visibleForTesting @visibleForTesting

View file

@ -63,20 +63,24 @@ void main() {
group('Spinners', () { group('Spinners', () {
MockStdio mockStdio; MockStdio mockStdio;
AnsiStatus ansiStatus; FakeStopwatch mockStopwatch;
int called; int called;
const List<String> testPlatforms = <String>['linux', 'macos', 'windows', 'fuchsia']; const List<String> testPlatforms = <String>['linux', 'macos', 'windows', 'fuchsia'];
final RegExp secondDigits = RegExp(r'[0-9,.]*[0-9]m?s'); final RegExp secondDigits = RegExp(r'[0-9,.]*[0-9]m?s');
setUp(() { AnsiStatus _createAnsiStatus() {
mockStdio = MockStdio(); mockStopwatch = FakeStopwatch();
called = 0; return AnsiStatus(
ansiStatus = AnsiStatus(
message: 'Hello world', message: 'Hello world',
timeout: const Duration(milliseconds: 10), timeout: const Duration(seconds: 2),
padding: 20, padding: 20,
onFinish: () => called += 1, onFinish: () => called += 1,
); );
}
setUp(() {
mockStdio = MockStdio();
called = 0;
}); });
List<String> outputStdout() => mockStdio.writtenToStdout.join('').split('\n'); List<String> outputStdout() => mockStdio.writtenToStdout.join('').split('\n');
@ -221,20 +225,17 @@ void main() {
Stdio: () => mockStdio, Stdio: () => mockStdio,
}); });
testUsingContext('AnsiStatus works for $testOs', () async { testUsingContext('AnsiStatus works for $testOs', () {
final AnsiStatus ansiStatus = _createAnsiStatus();
bool done = false; bool done = false;
// We pad the time here so that we have a little slack in terms of the first part of this test FakeAsync().run((FakeAsync time) {
// taking longer to run than we'd like, since we are forced to start the timer before the actual
// stopwatch that we're trying to test. This is an unfortunate possible race condition. If this
// turns out to be flaky, we will need to find another solution.
final Future<void> tenMillisecondsLater = Future<void>.delayed(const Duration(milliseconds: 15));
await FakeAsync().run((FakeAsync time) async {
ansiStatus.start(); ansiStatus.start();
mockStopwatch.elapsed = const Duration(seconds: 1);
doWhileAsync(time, () => ansiStatus.ticks < 10); // one second doWhileAsync(time, () => ansiStatus.ticks < 10); // one second
expect(ansiStatus.seemsSlow, isFalse); expect(ansiStatus.seemsSlow, isFalse);
expect(outputStdout().join('\n'), isNot(contains('This is taking an unexpectedly long time.'))); expect(outputStdout().join('\n'), isNot(contains('This is taking an unexpectedly long time.')));
expect(outputStdout().join('\n'), isNot(contains('(!)'))); expect(outputStdout().join('\n'), isNot(contains('(!)')));
await tenMillisecondsLater; mockStopwatch.elapsed = const Duration(seconds: 3);
doWhileAsync(time, () => ansiStatus.ticks < 30); // three seconds doWhileAsync(time, () => ansiStatus.ticks < 30); // three seconds
expect(ansiStatus.seemsSlow, isTrue); expect(ansiStatus.seemsSlow, isTrue);
expect(outputStdout().join('\n'), contains('This is taking an unexpectedly long time.')); expect(outputStdout().join('\n'), contains('This is taking an unexpectedly long time.'));
@ -246,12 +247,15 @@ void main() {
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Platform: () => FakePlatform(operatingSystem: testOs), Platform: () => FakePlatform(operatingSystem: testOs),
Stdio: () => mockStdio, Stdio: () => mockStdio,
Stopwatch: () => mockStopwatch,
}); });
testUsingContext('AnsiStatus works when cancelled for $testOs', () async { testUsingContext('AnsiStatus works when cancelled for $testOs', () async {
final AnsiStatus ansiStatus = _createAnsiStatus();
bool done = false; bool done = false;
FakeAsync().run((FakeAsync time) { FakeAsync().run((FakeAsync time) {
ansiStatus.start(); ansiStatus.start();
mockStopwatch.elapsed = const Duration(seconds: 1);
doWhileAsync(time, () => ansiStatus.ticks < 10); doWhileAsync(time, () => ansiStatus.ticks < 10);
List<String> lines = outputStdout(); List<String> lines = outputStdout();
expect(lines[0], startsWith(platform.isWindows expect(lines[0], startsWith(platform.isWindows
@ -280,12 +284,15 @@ void main() {
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Platform: () => FakePlatform(operatingSystem: testOs), Platform: () => FakePlatform(operatingSystem: testOs),
Stdio: () => mockStdio, Stdio: () => mockStdio,
Stopwatch: () => mockStopwatch,
}); });
testUsingContext('AnsiStatus works when stopped for $testOs', () async { testUsingContext('AnsiStatus works when stopped for $testOs', () async {
final AnsiStatus ansiStatus = _createAnsiStatus();
bool done = false; bool done = false;
FakeAsync().run((FakeAsync time) { FakeAsync().run((FakeAsync time) {
ansiStatus.start(); ansiStatus.start();
mockStopwatch.elapsed = const Duration(seconds: 1);
doWhileAsync(time, () => ansiStatus.ticks < 10); doWhileAsync(time, () => ansiStatus.ticks < 10);
List<String> lines = outputStdout(); List<String> lines = outputStdout();
expect(lines, hasLength(1)); expect(lines, hasLength(1));
@ -323,6 +330,7 @@ void main() {
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Platform: () => FakePlatform(operatingSystem: testOs), Platform: () => FakePlatform(operatingSystem: testOs),
Stdio: () => mockStdio, Stdio: () => mockStdio,
Stopwatch: () => mockStopwatch,
}); });
} }
}); });
@ -685,3 +693,39 @@ void main() {
}); });
}); });
} }
class FakeStopwatch implements Stopwatch {
@override
bool get isRunning => _isRunning;
bool _isRunning = false;
@override
void start() => _isRunning = true;
@override
void stop() => _isRunning = false;
@override
Duration elapsed = Duration.zero;
@override
int get elapsedMicroseconds => elapsed.inMicroseconds;
@override
int get elapsedMilliseconds => elapsed.inMilliseconds;
@override
int get elapsedTicks => elapsed.inMilliseconds;
@override
int get frequency => 1000;
@override
void reset() {
_isRunning = false;
elapsed = Duration.zero;
}
@override
String toString() => '$runtimeType $elapsed $isRunning';
}