flutter/packages/flutter_tools/test/base/logger_test.dart
Ian Hickson 0636c6fe60
Clean up startProgress logic. (#19695)
Disallow calling stop() or cancel() multiple times. This means that
when you use startProgress you have to more carefully think about what
exactly is going on.

Properly cancel startProgress in non-ANSI situations, so that
back-to-back startProgress calls all render to the console.
2018-07-26 14:02:25 -07:00

164 lines
6.2 KiB
Dart

// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import '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:test/test.dart';
import '../src/context.dart';
import '../src/mocks.dart';
void main() {
group('AppContext', () {
test('error', () async {
final BufferLogger mockLogger = new BufferLogger();
final VerboseLogger verboseLogger = new VerboseLogger(mockLogger);
verboseLogger.supportsColor = false;
verboseLogger.printStatus('Hey Hey Hey Hey');
verboseLogger.printTrace('Oooh, I do I do I do');
verboseLogger.printError('Helpless!');
expect(mockLogger.statusText, matches(r'^\[ (?: {0,2}\+[0-9]{1,3} ms| )\] Hey Hey Hey Hey\n'
r'\[ (?: {0,2}\+[0-9]{1,3} ms| )\] Oooh, I do I do I do\n$'));
expect(mockLogger.traceText, '');
expect(mockLogger.errorText, matches(r'^\[ (?: {0,2}\+[0-9]{1,3} ms| )\] Helpless!\n$'));
});
});
group('Spinners', () {
MockStdio mockStdio;
AnsiSpinner ansiSpinner;
AnsiStatus ansiStatus;
int called;
final RegExp secondDigits = new RegExp(r'[^\b]\b\b\b\b\b[0-9]+[.][0-9]+(?:s|ms)');
setUp(() {
mockStdio = new MockStdio();
ansiSpinner = new AnsiSpinner();
called = 0;
ansiStatus = new AnsiStatus(
message: 'Hello world',
expectSlowOperation: true,
padding: 20,
onFinish: () => called++,
);
});
List<String> outputLines() => mockStdio.writtenToStdout.join('').split('\n');
Future<void> doWhileAsync(bool doThis()) async {
return Future.doWhile(() {
// We want to let other tasks run at the same time, so we schedule these
// using a timer rather than a microtask.
return Future<bool>.delayed(Duration.zero, doThis);
});
}
testUsingContext('AnsiSpinner works', () async {
ansiSpinner.start();
await doWhileAsync(() => ansiSpinner.ticks < 10);
List<String> lines = outputLines();
expect(lines[0], startsWith(' \b-\b\\\b|\b/\b-\b\\\b|\b/'));
expect(lines[0].endsWith('\n'), isFalse);
expect(lines.length, equals(1));
ansiSpinner.stop();
lines = outputLines();
expect(lines[0], endsWith('\b \b'));
expect(lines.length, equals(1));
// Verify that stopping or canceling multiple times throws.
expect(() { ansiSpinner.stop(); }, throwsA(const isInstanceOf<AssertionError>()));
expect(() { ansiSpinner.cancel(); }, throwsA(const isInstanceOf<AssertionError>()));
}, overrides: <Type, Generator>{Stdio: () => mockStdio});
testUsingContext('AnsiStatus works when cancelled', () async {
ansiStatus.start();
await doWhileAsync(() => ansiStatus.ticks < 10);
List<String> lines = outputLines();
expect(lines[0], startsWith('Hello world \b-\b\\\b|\b/\b-\b\\\b|\b/\b-'));
expect(lines.length, equals(1));
expect(lines[0].endsWith('\n'), isFalse);
// Verify a cancel does _not_ print the time and prints a newline.
ansiStatus.cancel();
lines = outputLines();
final List<Match> matches = secondDigits.allMatches(lines[0]).toList();
expect(matches, isEmpty);
expect(lines[0], endsWith('\b \b'));
expect(called, equals(1));
expect(lines.length, equals(2));
expect(lines[1], equals(''));
// Verify that stopping or canceling multiple times throws.
expect(() { ansiStatus.cancel(); }, throwsA(const isInstanceOf<AssertionError>()));
expect(() { ansiStatus.stop(); }, throwsA(const isInstanceOf<AssertionError>()));
}, overrides: <Type, Generator>{Stdio: () => mockStdio});
testUsingContext('AnsiStatus works when stopped', () async {
ansiStatus.start();
await doWhileAsync(() => ansiStatus.ticks < 10);
List<String> lines = outputLines();
expect(lines[0], startsWith('Hello world \b-\b\\\b|\b/\b-\b\\\b|\b/\b-'));
expect(lines.length, equals(1));
// Verify a stop prints the time.
ansiStatus.stop();
lines = outputLines();
final List<Match> matches = secondDigits.allMatches(lines[0]).toList();
expect(matches, isNotNull);
expect(matches, hasLength(1));
final Match match = matches.first;
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(const isInstanceOf<AssertionError>()));
expect(() { ansiStatus.cancel(); }, throwsA(const isInstanceOf<AssertionError>()));
}, overrides: <Type, Generator>{Stdio: () => mockStdio});
testUsingContext('sequential startProgress calls with StdoutLogger', () async {
context[Logger].startProgress('AAA')..stop();
context[Logger].startProgress('BBB')..stop();
expect(outputLines(), <String>[
'AAA',
'BBB',
'',
]);
}, overrides: <Type, Generator>{
Stdio: () => mockStdio,
Logger: () => new StdoutLogger(),
});
testUsingContext('sequential startProgress calls with VerboseLogger and StdoutLogger', () async {
context[Logger].startProgress('AAA')..stop();
context[Logger].startProgress('BBB')..stop();
expect(outputLines(), <String>[
'[ ] AAA',
'[ ] AAA (completed)',
'[ ] BBB',
'[ ] BBB (completed)',
''
]);
}, overrides: <Type, Generator>{
Stdio: () => mockStdio,
Logger: () => new VerboseLogger(new StdoutLogger()),
});
testUsingContext('sequential startProgress calls with BufferLogger', () async {
context[Logger].startProgress('AAA')..stop();
context[Logger].startProgress('BBB')..stop();
final BufferLogger logger = context[Logger];
expect(logger.statusText, 'AAA\nBBB\n');
}, overrides: <Type, Generator>{
Logger: () => new BufferLogger(),
});
});
}