mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

Updates documentation and non-public API to use American spellings for consistency with the rest of the codebase. No changes to behaviour... other than how it's spelt.
745 lines
33 KiB
Dart
745 lines
33 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:convert' show jsonEncode;
|
|
|
|
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/platform.dart';
|
|
import 'package:flutter_tools/src/base/terminal.dart';
|
|
import 'package:quiver/testing/async.dart';
|
|
|
|
import '../src/common.dart';
|
|
import '../src/context.dart';
|
|
import '../src/mocks.dart';
|
|
|
|
final Generator _kNoAnsiPlatform = () => FakePlatform.fromPlatform(const LocalPlatform())..stdoutSupportsAnsi = false;
|
|
|
|
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', () {
|
|
testUsingContext('error', () async {
|
|
final BufferLogger mockLogger = BufferLogger();
|
|
final VerboseLogger verboseLogger = VerboseLogger(mockLogger);
|
|
|
|
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$'));
|
|
}, overrides: <Type, Generator>{
|
|
OutputPreferences: () => OutputPreferences(showColor: false),
|
|
Platform: _kNoAnsiPlatform,
|
|
});
|
|
|
|
testUsingContext('ANSI colored errors', () async {
|
|
final BufferLogger mockLogger = BufferLogger();
|
|
final VerboseLogger verboseLogger = VerboseLogger(mockLogger);
|
|
|
|
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| )\] ' '${bold}Hey Hey Hey Hey$resetBold'
|
|
r'\n\[ (?: {0,2}\+[0-9]{1,3} ms| )\] Oooh, I do I do I do\n$'));
|
|
expect(mockLogger.traceText, '');
|
|
expect(
|
|
mockLogger.errorText,
|
|
matches('^$red' r'\[ (?: {0,2}\+[0-9]{1,3} ms| )\] ' '${bold}Helpless!$resetBold$resetColor' r'\n$'));
|
|
}, overrides: <Type, Generator>{
|
|
OutputPreferences: () => OutputPreferences(showColor: true),
|
|
Platform: () => FakePlatform()..stdoutSupportsAnsi = true,
|
|
});
|
|
});
|
|
|
|
group('Spinners', () {
|
|
MockStdio mockStdio;
|
|
FakeStopwatch mockStopwatch;
|
|
int called;
|
|
const List<String> testPlatforms = <String>['linux', 'macos', 'windows', 'fuchsia'];
|
|
final RegExp secondDigits = RegExp(r'[0-9,.]*[0-9]m?s');
|
|
|
|
AnsiStatus _createAnsiStatus() {
|
|
mockStopwatch = FakeStopwatch();
|
|
return AnsiStatus(
|
|
message: 'Hello world',
|
|
timeout: const Duration(seconds: 2),
|
|
padding: 20,
|
|
onFinish: () => called += 1,
|
|
);
|
|
}
|
|
|
|
setUp(() {
|
|
mockStdio = MockStdio();
|
|
called = 0;
|
|
});
|
|
|
|
List<String> outputStdout() => mockStdio.writtenToStdout.join('').split('\n');
|
|
List<String> outputStderr() => mockStdio.writtenToStderr.join('').split('\n');
|
|
|
|
void doWhileAsync(FakeAsync time, bool doThis()) {
|
|
do {
|
|
time.elapse(const Duration(milliseconds: 1));
|
|
} while (doThis());
|
|
}
|
|
|
|
for (String testOs in testPlatforms) {
|
|
testUsingContext('AnsiSpinner works for $testOs', () async {
|
|
bool done = false;
|
|
FakeAsync().run((FakeAsync time) {
|
|
final AnsiSpinner ansiSpinner = AnsiSpinner(
|
|
timeout: const Duration(hours: 10),
|
|
)..start();
|
|
doWhileAsync(time, () => ansiSpinner.ticks < 10);
|
|
List<String> lines = outputStdout();
|
|
expect(lines[0], startsWith(
|
|
platform.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>()));
|
|
done = true;
|
|
});
|
|
expect(done, isTrue);
|
|
}, overrides: <Type, Generator>{
|
|
Platform: () => FakePlatform(operatingSystem: testOs),
|
|
Stdio: () => mockStdio,
|
|
});
|
|
|
|
testUsingContext('AnsiSpinner works for $testOs', () async {
|
|
bool done = false;
|
|
// We pad the time here so that we have a little slack in terms of the first part of this test
|
|
// 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 {
|
|
final AnsiSpinner ansiSpinner = AnsiSpinner(
|
|
timeout: const Duration(milliseconds: 10),
|
|
)..start();
|
|
doWhileAsync(time, () => ansiSpinner.ticks < 10); // one second
|
|
expect(ansiSpinner.seemsSlow, isFalse);
|
|
expect(outputStdout().join('\n'), isNot(contains('This is taking an unexpectedly long time.')));
|
|
await tenMillisecondsLater;
|
|
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.'));
|
|
ansiSpinner.stop();
|
|
expect(outputStdout().join('\n'), isNot(contains('(!)')));
|
|
done = true;
|
|
});
|
|
expect(done, isTrue);
|
|
}, overrides: <Type, Generator>{
|
|
Platform: () => FakePlatform(operatingSystem: testOs),
|
|
Stdio: () => mockStdio,
|
|
});
|
|
|
|
testUsingContext('Stdout startProgress on colored terminal for $testOs', () async {
|
|
bool done = false;
|
|
FakeAsync().run((FakeAsync time) {
|
|
final Logger logger = context.get<Logger>();
|
|
final Status status = logger.startProgress(
|
|
'Hello',
|
|
progressId: null,
|
|
timeout: 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(platform.isWindows ? r'^Hello {15} {5} {8}[\b]{8} {7}\\$' :
|
|
r'^Hello {15} {5} {8}[\b]{8} {7}⣽$'));
|
|
status.stop();
|
|
expect(outputStdout().join('\n'), matches(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]$'));
|
|
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) {
|
|
final Logger logger = context.get<Logger>();
|
|
final Status status = logger.startProgress(
|
|
'Knock Knock, Who\'s There',
|
|
timeout: const Duration(days: 10),
|
|
progressIndicatorPadding: 10,
|
|
);
|
|
logger.printStatus('Rude Interrupting Cow');
|
|
status.stop();
|
|
final String a = platform.isWindows ? '\\' : '⣽';
|
|
final String b = platform.isWindows ? '|' : '⣻';
|
|
expect(
|
|
outputStdout().join('\n'),
|
|
'Knock Knock, Who\'s There ' // initial message
|
|
' ' // placeholder so that spinner can backspace on its first tick
|
|
'\b\b\b\b\b\b\b\b $a' // first tick
|
|
'\b\b\b\b\b\b\b\b ' // clearing the spinner
|
|
'\b\b\b\b\b\b\b\b' // clearing the clearing of the spinner
|
|
'\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 ' // clearing the message
|
|
'\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' // clearing the clearing of the message
|
|
'Rude Interrupting Cow\n' // message
|
|
'Knock Knock, Who\'s There ' // message restoration
|
|
' ' // placeholder so that spinner can backspace on its second tick
|
|
'\b\b\b\b\b\b\b\b $b' // second tick
|
|
'\b\b\b\b\b\b\b\b ' // clearing the spinner to put the time
|
|
'\b\b\b\b\b\b\b\b' // clearing the clearing of the spinner
|
|
' 0.0s\n', // replacing it with the time
|
|
);
|
|
done = true;
|
|
});
|
|
expect(done, isTrue);
|
|
}, overrides: <Type, Generator>{
|
|
Logger: () => StdoutLogger(),
|
|
OutputPreferences: () => OutputPreferences(showColor: true),
|
|
Platform: () => FakePlatform(operatingSystem: testOs)..stdoutSupportsAnsi = true,
|
|
Stdio: () => mockStdio,
|
|
});
|
|
|
|
testUsingContext('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.'));
|
|
|
|
// Test that the number of '\b' is correct.
|
|
for (String line in outputStdout()) {
|
|
int currLength = 0;
|
|
for (int i = 0; i < line.length; i += 1) {
|
|
currLength += line[i] == '\b' ? -1 : 1;
|
|
expect(currLength, isNonNegative, reason: 'The following line has overflow backtraces:\n' + jsonEncode(line));
|
|
}
|
|
}
|
|
|
|
ansiStatus.stop();
|
|
expect(outputStdout().join('\n'), contains('(!)'));
|
|
done = true;
|
|
});
|
|
expect(done, isTrue);
|
|
}, overrides: <Type, Generator>{
|
|
Platform: () => FakePlatform(operatingSystem: testOs),
|
|
Stdio: () => mockStdio,
|
|
Stopwatch: () => mockStopwatch,
|
|
});
|
|
|
|
testUsingContext('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(platform.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 ⣻'));
|
|
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 = outputStdout();
|
|
final List<Match> matches = secondDigits.allMatches(lines[0]).toList();
|
|
expect(matches, isEmpty);
|
|
final String x = platform.isWindows ? '|' : '⣻';
|
|
expect(lines[0], endsWith('$x\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(''));
|
|
|
|
// Verify that stopping or canceling multiple times throws.
|
|
expect(() { ansiStatus.cancel(); }, throwsA(isInstanceOf<AssertionError>()));
|
|
expect(() { ansiStatus.stop(); }, throwsA(isInstanceOf<AssertionError>()));
|
|
done = true;
|
|
});
|
|
expect(done, isTrue);
|
|
}, overrides: <Type, Generator>{
|
|
Platform: () => FakePlatform(operatingSystem: testOs),
|
|
Stdio: () => mockStdio,
|
|
Stopwatch: () => mockStopwatch,
|
|
});
|
|
|
|
testUsingContext('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],
|
|
platform.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 ⣻',
|
|
);
|
|
|
|
// Verify a stop prints the time.
|
|
ansiStatus.stop();
|
|
lines = outputStdout();
|
|
expect(lines, hasLength(2));
|
|
expect(lines[0], matches(
|
|
platform.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$'
|
|
));
|
|
expect(lines[1], isEmpty);
|
|
final List<Match> times = secondDigits.allMatches(lines[0]).toList();
|
|
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>()));
|
|
done = true;
|
|
});
|
|
expect(done, isTrue);
|
|
}, overrides: <Type, Generator>{
|
|
Platform: () => FakePlatform(operatingSystem: testOs),
|
|
Stdio: () => mockStdio,
|
|
Stopwatch: () => mockStopwatch,
|
|
});
|
|
}
|
|
});
|
|
group('Output format', () {
|
|
MockStdio mockStdio;
|
|
SummaryStatus summaryStatus;
|
|
int called;
|
|
final RegExp secondDigits = RegExp(r'[^\b]\b\b\b\b\b[0-9]+[.][0-9]+(?:s|ms)');
|
|
|
|
setUp(() {
|
|
mockStdio = MockStdio();
|
|
called = 0;
|
|
summaryStatus = SummaryStatus(
|
|
message: 'Hello world',
|
|
timeout: timeoutConfiguration.slowOperation,
|
|
padding: 20,
|
|
onFinish: () => called++,
|
|
);
|
|
});
|
|
|
|
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>();
|
|
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>();
|
|
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));
|
|
expect(lines[0], equals(' 01234567890123456789012345678901234'));
|
|
expect(lines[1], equals(' 56789012345678901234567890123456789'));
|
|
expect(lines[2], equals(' 01234567890123456789012345678901234'));
|
|
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>();
|
|
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));
|
|
expect(lines[0], equals('0123456789012345678901234567890123456789'));
|
|
expect(lines[1], equals(' 01234567890123456789012345678901234'));
|
|
expect(lines[2], equals(' 56789012345678901234567890123456789'));
|
|
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>();
|
|
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));
|
|
expect(lines[0], equals(' 012345678901234567890123456789012345'));
|
|
expect(lines[1], equals(' 6789012345678901234567890123456'));
|
|
expect(lines[2], equals(' 7890123456789012345678901234567'));
|
|
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>();
|
|
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>();
|
|
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));
|
|
expect(lines[0], equals(' 01234567890123456789012345678901234'));
|
|
expect(lines[1], equals(' 56789012345678901234567890123456789'));
|
|
expect(lines[2], equals(' 01234567890123456789012345678901234'));
|
|
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>();
|
|
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));
|
|
expect(lines[0], equals('0123456789012345678901234567890123456789'));
|
|
expect(lines[1], equals(' 01234567890123456789012345678901234'));
|
|
expect(lines[2], equals(' 56789012345678901234567890123456789'));
|
|
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>();
|
|
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));
|
|
expect(lines[0], equals(' 012345678901234567890123456789012345'));
|
|
expect(lines[1], equals(' 6789012345678901234567890123456'));
|
|
expect(lines[2], equals(' 7890123456789012345678901234567'));
|
|
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>();
|
|
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>();
|
|
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>();
|
|
logger.printStatus(
|
|
null,
|
|
emphasis: null,
|
|
color: null,
|
|
newline: null,
|
|
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>();
|
|
logger.printStatus(
|
|
null,
|
|
emphasis: null,
|
|
color: null,
|
|
newline: null,
|
|
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: false),
|
|
Stdio: () => mockStdio,
|
|
Platform: _kNoAnsiPlatform,
|
|
});
|
|
|
|
testUsingContext('Stdout startProgress on non-color terminal', () async {
|
|
bool done = false;
|
|
FakeAsync().run((FakeAsync time) {
|
|
final Logger logger = context.get<Logger>();
|
|
final Status status = logger.startProgress(
|
|
'Hello',
|
|
progressId: null,
|
|
timeout: 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(platform.isWindows ? r'^Hello {15} {5}$' :
|
|
r'^Hello {15} {5}$'));
|
|
status.stop();
|
|
expect(outputStdout().join('\n'), matches(platform.isWindows ? r'^Hello {15} {5}[\d, ]{4}[\d]\.[\d]s[\n]$' :
|
|
r'^Hello {15} {5}[\d, ]{4}[\d]\.[\d]s[\n]$'));
|
|
done = true;
|
|
});
|
|
expect(done, isTrue);
|
|
}, overrides: <Type, Generator>{
|
|
Logger: () => StdoutLogger(),
|
|
OutputPreferences: () => OutputPreferences(showColor: false),
|
|
Stdio: () => mockStdio,
|
|
Platform: _kNoAnsiPlatform,
|
|
});
|
|
|
|
testUsingContext('SummaryStatus works when canceled', () async {
|
|
summaryStatus.start();
|
|
List<String> lines = outputStdout();
|
|
expect(lines[0], startsWith('Hello world '));
|
|
expect(lines.length, equals(1));
|
|
expect(lines[0].endsWith('\n'), isFalse);
|
|
|
|
// Verify a cancel does _not_ print the time and prints a newline.
|
|
summaryStatus.cancel();
|
|
lines = outputStdout();
|
|
final List<Match> matches = secondDigits.allMatches(lines[0]).toList();
|
|
expect(matches, isEmpty);
|
|
expect(lines[0], endsWith(' '));
|
|
expect(called, equals(1));
|
|
expect(lines.length, equals(2));
|
|
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>{Stdio: () => mockStdio, Platform: _kNoAnsiPlatform});
|
|
|
|
testUsingContext('SummaryStatus works when stopped', () async {
|
|
summaryStatus.start();
|
|
List<String> lines = outputStdout();
|
|
expect(lines[0], startsWith('Hello world '));
|
|
expect(lines.length, equals(1));
|
|
|
|
// Verify a stop prints the time.
|
|
summaryStatus.stop();
|
|
lines = outputStdout();
|
|
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(() { summaryStatus.stop(); }, throwsA(isInstanceOf<AssertionError>()));
|
|
expect(() { summaryStatus.cancel(); }, throwsA(isInstanceOf<AssertionError>()));
|
|
}, overrides: <Type, Generator>{Stdio: () => mockStdio, 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();
|
|
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();
|
|
expect(outputStdout(), <Matcher>[
|
|
matches(r'^\[ (?: {0,2}\+[0-9]{1,3} ms| )\] AAA$'),
|
|
matches(r'^\[ (?: {0,2}\+[0-9]{1,3} ms| )\] AAA \(completed.*\)$'),
|
|
matches(r'^\[ (?: {0,2}\+[0-9]{1,3} ms| )\] BBB$'),
|
|
matches(r'^\[ (?: {0,2}\+[0-9]{1,3} ms| )\] BBB \(completed.*\)$'),
|
|
matches(r'^$'),
|
|
]);
|
|
}, overrides: <Type, Generator>{
|
|
Logger: () => VerboseLogger(StdoutLogger()),
|
|
Stdio: () => mockStdio,
|
|
Platform: _kNoAnsiPlatform,
|
|
});
|
|
|
|
testUsingContext('sequential startProgress calls with BufferLogger', () async {
|
|
final BufferLogger logger = context.get<Logger>();
|
|
logger.startProgress('AAA', timeout: timeoutConfiguration.fastOperation)..stop();
|
|
logger.startProgress('BBB', timeout: timeoutConfiguration.fastOperation)..stop();
|
|
expect(logger.statusText, 'AAA\nBBB\n');
|
|
}, overrides: <Type, Generator>{
|
|
Logger: () => BufferLogger(),
|
|
Platform: _kNoAnsiPlatform,
|
|
});
|
|
});
|
|
}
|
|
|
|
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';
|
|
}
|