Unverified Commit efcd9a80 authored by Greg Spencer's avatar Greg Spencer Committed by GitHub

Simplify Gradle compiler output. (#21760)

This changes the compiler output for gradle to be less verbose and more easily read.

This only applies to compilation error messages: other gradle messages will continue to print as before.

It also fixes a small problem with the performance measurement printing (see that "7.1s" on it's own line in the original?) so that if something is expected to have multiple lines of output, it prints an initial line, and a "Done" line with the elapsed time, so that it's possible to know what the time applies to.

It also updates the spinner to be fancier, at least on platforms other than Windows (which is missing a lot of symbols in its console font).

Addresses #17307
parent 83cdb573
......@@ -45,6 +45,13 @@ class FlutterPlugin implements Plugin<Project> {
private File dynamicProfileFlutterJar
private File dynamicReleaseFlutterJar
// The name prefix for flutter builds. This is used to identify gradle tasks
// where we expect the flutter tool to provide any error output, and skip the
// standard Gradle error output in the FlutterEventLogger. If you change this,
// be sure to change any instances of this string in symbols in the code below
// to match.
static final String flutterBuildPrefix = "flutterBuild"
private Properties readPropertiesIfExist(File propertiesFile) {
Properties result = new Properties()
if (propertiesFile.exists()) {
......@@ -152,7 +159,7 @@ class FlutterPlugin implements Plugin<Project> {
// Add x86/x86_64 native library. Debug mode only, for now.
flutterX86Jar = project.file("${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/flutter/flutter-x86.jar")
Task flutterX86JarTask = project.tasks.create("flutterBuildX86Jar", Jar) {
Task flutterX86JarTask = project.tasks.create("${flutterBuildPrefix}X86Jar", Jar) {
destinationDir flutterX86Jar.parentFile
archiveName flutterX86Jar.name
from("${flutterRoot}/bin/cache/artifacts/engine/android-x86/libflutter.so") {
......@@ -319,7 +326,7 @@ class FlutterPlugin implements Plugin<Project> {
def addFlutterDeps = { variant ->
String flutterBuildMode = buildModeFor(variant.buildType)
if (flutterBuildMode == 'debug' && project.tasks.findByName('flutterBuildX86Jar')) {
if (flutterBuildMode == 'debug' && project.tasks.findByName('${flutterBuildPrefix}X86Jar')) {
Task task = project.tasks.findByName("compile${variant.name.capitalize()}JavaWithJavac")
if (task) {
task.dependsOn project.flutterBuildX86Jar
......@@ -330,7 +337,7 @@ class FlutterPlugin implements Plugin<Project> {
}
}
FlutterTask flutterTask = project.tasks.create(name: "flutterBuild${variant.name.capitalize()}", type: FlutterTask) {
FlutterTask flutterTask = project.tasks.create(name: "${flutterBuildPrefix}${variant.name.capitalize()}", type: FlutterTask) {
flutterRoot this.flutterRoot
flutterExecutable this.flutterExecutable
buildMode flutterBuildMode
......@@ -584,3 +591,23 @@ class FlutterTask extends BaseFlutterTask {
buildBundle()
}
}
gradle.useLogger(new FlutterEventLogger())
class FlutterEventLogger extends BuildAdapter implements TaskExecutionListener {
String mostRecentTask = ""
void beforeExecute(Task task) {
mostRecentTask = task.name
}
void afterExecute(Task task, TaskState state) {}
void buildFinished(BuildResult result) {
if (result.failure != null) {
if (!(result.failure instanceof GradleException) || !mostRecentTask.startsWith(FlutterPlugin.flutterBuildPrefix)) {
result.rethrowFailure()
}
}
}
}
......@@ -299,7 +299,11 @@ Future<Null> buildGradleProject({
Future<Null> _buildGradleProjectV1(FlutterProject project, String gradle) async {
// Run 'gradlew build'.
final Status status = logger.startProgress('Running \'gradlew build\'...', expectSlowOperation: true);
final Status status = logger.startProgress(
"Running 'gradlew build'...",
expectSlowOperation: true,
multilineOutput: true,
);
final int exitCode = await runCommandAndStreamOutput(
<String>[fs.file(gradle).absolute.path, 'build'],
workingDirectory: project.android.hostAppGradleRoot.path,
......@@ -337,7 +341,11 @@ Future<Null> _buildGradleProjectV2(
throwToolExit('Gradle build aborted.');
}
}
final Status status = logger.startProgress('Running \'gradlew $assembleTask\'...', expectSlowOperation: true);
final Status status = logger.startProgress(
"Gradle task '$assembleTask'...",
expectSlowOperation: true,
multilineOutput: true,
);
final String gradlePath = fs.file(gradle).absolute.path;
final List<String> command = <String>[gradlePath];
if (logger.isVerbose) {
......@@ -382,7 +390,7 @@ Future<Null> _buildGradleProjectV2(
status.stop();
if (exitCode != 0)
throwToolExit('Gradle build failed: $exitCode', exitCode: exitCode);
throwToolExit('Gradle task $assembleTask failed with exit code $exitCode', exitCode: exitCode);
final File apkFile = _findApkFile(project, buildInfo);
if (apkFile == null)
......
......@@ -155,6 +155,11 @@ class Stdio {
Stream<List<int>> get stdin => io.stdin;
io.IOSink get stdout => io.stdout;
io.IOSink get stderr => io.stderr;
bool get hasTerminal => io.stdout.hasTerminal;
int get terminalColumns => hasTerminal ? io.stdout.terminalColumns : null;
int get terminalLines => hasTerminal ? io.stdout.terminalLines : null;
bool get supportsAnsiEscapes => hasTerminal ? io.stdout.supportsAnsiEscapes : false;
}
io.IOSink get stderr => context[Stdio].stderr;
......@@ -162,3 +167,5 @@ io.IOSink get stderr => context[Stdio].stderr;
Stream<List<int>> get stdin => context[Stdio].stdin;
io.IOSink get stdout => context[Stdio].stdout;
Stdio get stdio => context[Stdio];
......@@ -8,6 +8,7 @@ import 'dart:convert' show LineSplitter;
import 'package:meta/meta.dart';
import 'io.dart';
import 'platform.dart';
import 'terminal.dart';
import 'utils.dart';
......@@ -25,6 +26,8 @@ abstract class Logger {
terminal.supportsColor = value;
}
bool get hasTerminal => stdio.hasTerminal;
/// Display an error level message to the user. Commands should use this if they
/// fail in some way.
void printError(
......@@ -62,6 +65,7 @@ abstract class Logger {
String message, {
String progressId,
bool expectSlowOperation,
bool multilineOutput,
int progressIndicatorPadding,
});
}
......@@ -129,6 +133,7 @@ class StdoutLogger extends Logger {
String message, {
String progressId,
bool expectSlowOperation,
bool multilineOutput,
int progressIndicatorPadding,
}) {
expectSlowOperation ??= false;
......@@ -141,6 +146,7 @@ class StdoutLogger extends Logger {
_status = AnsiStatus(
message: message,
expectSlowOperation: expectSlowOperation,
multilineOutput: multilineOutput,
padding: progressIndicatorPadding,
onFinish: _clearStatus,
)..start();
......@@ -223,6 +229,7 @@ class BufferLogger extends Logger {
String message, {
String progressId,
bool expectSlowOperation,
bool multilineOutput,
int progressIndicatorPadding,
}) {
printStatus(message);
......@@ -280,6 +287,7 @@ class VerboseLogger extends Logger {
String message, {
String progressId,
bool expectSlowOperation,
bool multilineOutput,
int progressIndicatorPadding,
}) {
printStatus(message);
......@@ -366,7 +374,7 @@ class Status {
onFinish();
}
/// Call to cancel the spinner after failure or cancelation.
/// Call to cancel the spinner after failure or cancellation.
void cancel() {
assert(_isStarted);
_isStarted = false;
......@@ -375,18 +383,25 @@ class Status {
}
}
/// An [AnsiSpinner] is a simple animation that does nothing but implement an
/// ASCII spinner. When stopped or canceled, the animation erases itself.
/// An [AnsiSpinner] is a simple animation that does nothing but implement a
/// ASCII/Unicode spinner. When stopped or canceled, the animation erases
/// itself.
class AnsiSpinner extends Status {
AnsiSpinner({VoidCallback onFinish}) : super(onFinish: onFinish);
int ticks = 0;
Timer timer;
static final List<String> _progress = <String>[r'-', r'\', r'|', r'/'];
// Windows console font has a limited set of Unicode characters.
List<String> get _animation => platform.isWindows
? <String>[r'-', r'\', r'|', r'/']
: <String>['🌕', '🌖', '🌗', '🌘', '🌑', '🌒', '🌓', '🌔'];
String get _backspace => '\b' * _animation[0].length;
String get _clear => ' ' * _animation[0].length;
void _callback(Timer timer) {
stdout.write('\b${_progress[ticks++ % _progress.length]}');
stdout.write('$_backspace${_animation[ticks++ % _animation.length]}');
}
@override
......@@ -402,7 +417,7 @@ class AnsiSpinner extends Status {
void stop() {
assert(timer.isActive);
timer.cancel();
stdout.write('\b \b');
stdout.write('$_backspace$_clear$_backspace');
super.stop();
}
......@@ -410,7 +425,7 @@ class AnsiSpinner extends Status {
void cancel() {
assert(timer.isActive);
timer.cancel();
stdout.write('\b \b');
stdout.write('$_backspace$_clear$_backspace');
super.cancel();
}
}
......@@ -423,23 +438,29 @@ class AnsiStatus extends AnsiSpinner {
AnsiStatus({
String message,
bool expectSlowOperation,
bool multilineOutput,
int padding,
VoidCallback onFinish,
}) : message = message ?? '',
padding = padding ?? 0,
expectSlowOperation = expectSlowOperation ?? false,
multilineOutput = multilineOutput ?? false,
super(onFinish: onFinish);
final String message;
final bool expectSlowOperation;
final bool multilineOutput;
final int padding;
Stopwatch stopwatch;
static const String _margin = ' ';
@override
void start() {
assert(stopwatch == null || !stopwatch.isRunning);
stopwatch = Stopwatch()..start();
stdout.write('${message.padRight(padding)} ');
stdout.write('${message.padRight(padding)}$_margin');
super.start();
}
......@@ -456,17 +477,23 @@ class AnsiStatus extends AnsiSpinner {
stdout.write('\n');
}
/// Backs up 4 characters and prints a (minimum) 5 character padded time. If
/// [expectSlowOperation] is true, the time is in seconds; otherwise,
/// milliseconds. Only backs up 4 characters because [super.cancel] backs
/// up one.
/// Print summary information when a task is done.
///
/// If [multilineOutput] is false, backs up 4 characters and prints a
/// (minimum) 5 character padded time. If [expectSlowOperation] is true, the
/// time is in seconds; otherwise, milliseconds. Only backs up 4 characters
/// because [super.cancel] backs up one.
///
/// Example: '\b\b\b\b 0.5s', '\b\b\b\b150ms', '\b\b\b\b1600ms'
/// If [multilineOutput] is true, then it prints the message again on a new
/// line before writing the elapsed time, and doesn't back up at all.
void writeSummaryInformation() {
final String prefix = multilineOutput
? '\n${'$message Done'.padRight(padding - 4)}$_margin'
: '\b\b\b\b';
if (expectSlowOperation) {
stdout.write('\b\b\b\b${getElapsedAsSeconds(stopwatch.elapsed).padLeft(5)}');
stdout.write('$prefix${getElapsedAsSeconds(stopwatch.elapsed).padLeft(5)}');
} else {
stdout.write('\b\b\b\b${getElapsedAsMilliseconds(stopwatch.elapsed).padLeft(5)}');
stdout.write('$prefix${getElapsedAsMilliseconds(stopwatch.elapsed).padLeft(5)}');
}
}
}
......
......@@ -772,6 +772,7 @@ class NotifyingLogger extends Logger {
String message, {
String progressId,
bool expectSlowOperation = false,
bool multilineOutput,
int progressIndicatorPadding = kDefaultStatusPadding,
}) {
printStatus(message);
......@@ -928,6 +929,7 @@ class _AppRunLogger extends Logger {
String message, {
String progressId,
bool expectSlowOperation = false,
bool multilineOutput,
int progressIndicatorPadding = 52,
}) {
final int id = _nextProgressId++;
......
......@@ -32,30 +32,35 @@ class _StdoutHandler {
reset();
}
bool compilerMessageReceived = false;
final CompilerMessageConsumer consumer;
String boundaryKey;
Completer<CompilerOutput> compilerOutput;
bool _suppressCompilerMessages;
void handler(String string) {
void handler(String message) {
const String kResultPrefix = 'result ';
if (boundaryKey == null) {
if (string.startsWith(kResultPrefix))
boundaryKey = string.substring(kResultPrefix.length);
} else if (string.startsWith(boundaryKey)) {
if (string.length <= boundaryKey.length) {
if (message.startsWith(kResultPrefix))
boundaryKey = message.substring(kResultPrefix.length);
} else if (message.startsWith(boundaryKey)) {
if (message.length <= boundaryKey.length) {
compilerOutput.complete(null);
return;
}
final int spaceDelimiter = string.lastIndexOf(' ');
final int spaceDelimiter = message.lastIndexOf(' ');
compilerOutput.complete(
CompilerOutput(
string.substring(boundaryKey.length + 1, spaceDelimiter),
int.parse(string.substring(spaceDelimiter + 1).trim())));
message.substring(boundaryKey.length + 1, spaceDelimiter),
int.parse(message.substring(spaceDelimiter + 1).trim())));
}
else if (!_suppressCompilerMessages) {
consumer('compiler message: $string');
if (compilerMessageReceived == false) {
consumer('\nCompiler message:');
compilerMessageReceived = true;
}
consumer(message);
}
}
......@@ -63,6 +68,7 @@ class _StdoutHandler {
// with its own boundary key and new completer.
void reset({bool suppressCompilerMessages = false}) {
boundaryKey = null;
compilerMessageReceived = false;
compilerOutput = Completer<CompilerOutput>();
_suppressCompilerMessages = suppressCompilerMessages;
}
......@@ -174,7 +180,7 @@ class KernelCompiler {
server.stderr
.transform(utf8.decoder)
.listen((String s) { printError('compiler message: $s'); });
.listen((String message) { printError(message); });
server.stdout
.transform(utf8.decoder)
.transform(const LineSplitter())
......@@ -242,7 +248,7 @@ class _CompileExpressionRequest extends _CompilationRequest {
/// restarts the Flutter app.
class ResidentCompiler {
ResidentCompiler(this._sdkRoot, {bool trackWidgetCreation = false,
String packagesPath, List<String> fileSystemRoots, String fileSystemScheme ,
String packagesPath, List<String> fileSystemRoots, String fileSystemScheme,
CompilerMessageConsumer compilerMessageConsumer = printError,
String initializeFromDill})
: assert(_sdkRoot != null),
......@@ -381,7 +387,7 @@ class ResidentCompiler {
_server.stderr
.transform(utf8.decoder)
.transform(const LineSplitter())
.listen((String s) { printError('compiler message: $s'); });
.listen((String message) { printError(message); });
_server.stdin.writeln('compile $scriptFilename');
......
......@@ -413,7 +413,7 @@ Future<XcodeBuildResult> buildXcodeProject({
Status initialBuildStatus;
Directory tempDir;
if (logger.supportsColor) {
if (logger.hasTerminal) {
tempDir = fs.systemTempDirectory.createTempSync('flutter_build_log_pipe.');
final File scriptOutputPipeFile = tempDir.childFile('pipe_to_stdout');
os.makePipe(scriptOutputPipeFile.path);
......
......@@ -217,7 +217,7 @@ class _Compiler {
if (suppressOutput)
return;
if (message.startsWith('compiler message: Error: Could not resolve the package \'test\'')) {
if (message.startsWith('Error: Could not resolve the package \'test\'')) {
printTrace(message);
printError(
'\n\nFailed to load test harness. Are you missing a dependency on flutter_test?\n',
......
......@@ -7,6 +7,7 @@ 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:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/terminal.dart';
import '../src/common.dart';
......@@ -60,6 +61,7 @@ void main() {
AnsiStatus ansiStatus;
SummaryStatus summaryStatus;
int called;
const List<String> testPlatforms = <String>['linux', 'macos', 'windows', 'fuchsia'];
final RegExp secondDigits = RegExp(r'[^\b]\b\b\b\b\b[0-9]+[.][0-9]+(?:s|ms)');
setUp(() {
......@@ -91,22 +93,105 @@ void main() {
});
}
testUsingContext('AnsiSpinner works', () async {
ansiSpinner.start();
await doWhileAsync(() => ansiSpinner.ticks < 10);
List<String> lines = outputStdout();
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 = outputStdout();
expect(lines[0], endsWith('\b \b'));
expect(lines.length, equals(1));
for (String testOs in testPlatforms) {
testUsingContext('AnsiSpinner works for $testOs', () async {
ansiSpinner.start();
await doWhileAsync(() => 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🌑\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(platform.isWindows ? '\b \b' : '\b\b \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>()));
}, overrides: <Type, Generator>{
Stdio: () => mockStdio,
Platform: () => FakePlatform(operatingSystem: testOs),
});
// Verify that stopping or canceling multiple times throws.
expect(() { ansiSpinner.stop(); }, throwsA(isInstanceOf<AssertionError>()));
expect(() { ansiSpinner.cancel(); }, throwsA(isInstanceOf<AssertionError>()));
}, overrides: <Type, Generator>{Stdio: () => mockStdio});
testUsingContext('Stdout startProgress handle null inputs on colored terminal for $testOs', () async {
context[Logger].startProgress(null, progressId: null,
expectSlowOperation: null,
progressIndicatorPadding: null,
);
final List<String> lines = outputStdout();
expect(outputStderr().length, equals(1));
expect(outputStderr().first, isEmpty);
expect(lines[0], matches(platform.isWindows ? r'[ ]{64} [\b]-' : r'[ ]{64} [\b][\b]🌕'));
}, overrides: <Type, Generator>{
Stdio: () => mockStdio,
Platform: () => FakePlatform(operatingSystem: testOs),
Logger: () => StdoutLogger()..supportsColor = true,
});
testUsingContext('AnsiStatus works when cancelled for $testOs', () async {
ansiStatus.start();
await doWhileAsync(() => ansiStatus.ticks < 10);
List<String> lines = outputStdout();
expect(lines[0], startsWith(platform.isWindows
? 'Hello world \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🌖'));
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);
expect(lines[0], endsWith(platform.isWindows ? '\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>()));
}, overrides: <Type, Generator>{
Stdio: () => mockStdio,
Platform: () => FakePlatform(operatingSystem: testOs),
});
testUsingContext('AnsiStatus works when stopped for $testOs', () async {
ansiStatus.start();
await doWhileAsync(() => ansiStatus.ticks < 10);
List<String> lines = outputStdout();
expect(lines[0], startsWith(platform.isWindows
? 'Hello world \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🌖'));
expect(lines.length, equals(1));
// Verify a stop prints the time.
ansiStatus.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(() { ansiStatus.stop(); }, throwsA(isInstanceOf<AssertionError>()));
expect(() { ansiStatus.cancel(); }, throwsA(isInstanceOf<AssertionError>()));
}, overrides: <Type, Generator>{
Stdio: () => mockStdio,
Platform: () => FakePlatform(operatingSystem: testOs),
});
}
testUsingContext('Error logs are red', () async {
context[Logger].printError('Pants on fire!');
......@@ -144,20 +229,6 @@ void main() {
Logger: () => StdoutLogger()..supportsColor = true,
});
testUsingContext('Stdout startProgress handle null inputs on colored terminal', () async {
context[Logger].startProgress(null, progressId: null,
expectSlowOperation: null,
progressIndicatorPadding: null,
);
final List<String> lines = outputStdout();
expect(outputStderr().length, equals(1));
expect(outputStderr().first, isEmpty);
expect(lines[0], equals(' \b-'));
}, overrides: <Type, Generator>{
Stdio: () => mockStdio,
Logger: () => StdoutLogger()..supportsColor = true,
});
testUsingContext('Stdout printStatus handle null inputs on regular terminal', () async {
context[Logger].printStatus(null, emphasis: null,
color: null,
......@@ -180,59 +251,12 @@ void main() {
final List<String> lines = outputStdout();
expect(outputStderr().length, equals(1));
expect(outputStderr().first, isEmpty);
expect(lines[0], equals(' '));
expect(lines[0], matches('[ ]{64}'));
}, overrides: <Type, Generator>{
Stdio: () => mockStdio,
Logger: () => StdoutLogger()..supportsColor = false,
});
testUsingContext('AnsiStatus works when cancelled', () async {
ansiStatus.start();
await doWhileAsync(() => ansiStatus.ticks < 10);
List<String> lines = outputStdout();
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 = outputStdout();
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(isInstanceOf<AssertionError>()));
expect(() { ansiStatus.stop(); }, throwsA(isInstanceOf<AssertionError>()));
}, overrides: <Type, Generator>{Stdio: () => mockStdio});
testUsingContext('AnsiStatus works when stopped', () async {
ansiStatus.start();
await doWhileAsync(() => ansiStatus.ticks < 10);
List<String> lines = outputStdout();
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 = 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(() { ansiStatus.stop(); }, throwsA(isInstanceOf<AssertionError>()));
expect(() { ansiStatus.cancel(); }, throwsA(isInstanceOf<AssertionError>()));
}, overrides: <Type, Generator>{Stdio: () => mockStdio});
testUsingContext('SummaryStatus works when cancelled', () async {
summaryStatus.start();
List<String> lines = outputStdout();
......
......@@ -116,7 +116,11 @@ Future<Null> _testFile(String testName, String workingDirectory, String testDire
int outputLineNumber = 0;
bool haveSeenStdErrMarker = false;
while (expectationLineNumber < expectations.length) {
expect(output, hasLength(greaterThan(outputLineNumber)));
expect(
output,
hasLength(greaterThan(outputLineNumber)),
reason: 'Failure in $testName to compare to $fullTestExpectation',
);
final String expectationLine = expectations[expectationLineNumber];
final String outputLine = output[outputLineNumber];
if (expectationLine == '<<skip until matching line>>') {
......
......@@ -50,10 +50,11 @@ void main() {
mainPath: '/path/to/main.dart'
);
expect(mockFrontendServerStdIn.getAndClear(), isEmpty);
expect(logger.errorText, equals('compiler message: line1\ncompiler message: line2\n'));
expect(logger.errorText, equals('\nCompiler message:\nline1\nline2\n'));
expect(output.outputFilename, equals('/path/to/main.dart.dill'));
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
Logger: () => BufferLogger()..supportsColor = false,
});
testUsingContext('single dart failed compilation', () async {
......@@ -70,10 +71,11 @@ void main() {
mainPath: '/path/to/main.dart'
);
expect(mockFrontendServerStdIn.getAndClear(), isEmpty);
expect(logger.errorText, equals('compiler message: line1\ncompiler message: line2\n'));
expect(logger.errorText, equals('\nCompiler message:\nline1\nline2\n'));
expect(output, equals(null));
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
Logger: () => BufferLogger()..supportsColor = false,
});
testUsingContext('single dart abnormal compiler termination', () async {
......@@ -92,10 +94,11 @@ void main() {
mainPath: '/path/to/main.dart'
);
expect(mockFrontendServerStdIn.getAndClear(), isEmpty);
expect(logger.errorText, equals('compiler message: line1\ncompiler message: line2\n'));
expect(logger.errorText, equals('\nCompiler message:\nline1\nline2\n'));
expect(output, equals(null));
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
Logger: () => BufferLogger()..supportsColor = false,
});
});
......@@ -146,10 +149,11 @@ void main() {
);
expect(mockFrontendServerStdIn.getAndClear(), 'compile /path/to/main.dart\n');
verifyNoMoreInteractions(mockFrontendServerStdIn);
expect(logger.errorText, equals('compiler message: line1\ncompiler message: line2\n'));
expect(logger.errorText, equals('\nCompiler message:\nline1\nline2\n'));
expect(output.outputFilename, equals('/path/to/main.dart.dill'));
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
Logger: () => BufferLogger()..supportsColor = false,
});
testUsingContext('single dart compile abnormally terminates', () async {
......@@ -163,6 +167,7 @@ void main() {
expect(output, equals(null));
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
Logger: () => BufferLogger()..supportsColor = false,
});
testUsingContext('compile and recompile', () async {
......@@ -181,11 +186,12 @@ void main() {
verifyNoMoreInteractions(mockFrontendServerStdIn);
expect(mockFrontendServerStdIn.getAndClear(), isEmpty);
expect(logger.errorText, equals(
'compiler message: line0\ncompiler message: line1\n'
'compiler message: line1\ncompiler message: line2\n'
'\nCompiler message:\nline0\nline1\n'
'\nCompiler message:\nline1\nline2\n'
));
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
Logger: () => BufferLogger()..supportsColor = false,
});
testUsingContext('compile and recompile twice', () async {
......@@ -208,12 +214,13 @@ void main() {
verifyNoMoreInteractions(mockFrontendServerStdIn);
expect(mockFrontendServerStdIn.getAndClear(), isEmpty);
expect(logger.errorText, equals(
'compiler message: line0\ncompiler message: line1\n'
'compiler message: line1\ncompiler message: line2\n'
'compiler message: line2\ncompiler message: line3\n'
'\nCompiler message:\nline0\nline1\n'
'\nCompiler message:\nline1\nline2\n'
'\nCompiler message:\nline2\nline3\n'
));
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
Logger: () => BufferLogger()..supportsColor = false,
});
});
......@@ -283,7 +290,7 @@ void main() {
'compile /path/to/main.dart\n');
verifyNoMoreInteractions(mockFrontendServerStdIn);
expect(logger.errorText,
equals('compiler message: line1\ncompiler message: line2\n'));
equals('\nCompiler message:\nline1\nline2\n'));
expect(output.outputFilename, equals('/path/to/main.dart.dill'));
compileExpressionResponseCompleter.complete(
......@@ -302,6 +309,7 @@ void main() {
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
Logger: () => BufferLogger()..supportsColor = false,
});
testUsingContext('compile expressions without awaiting', () async {
......@@ -325,7 +333,7 @@ void main() {
'/path/to/main.dart', null /* invalidatedFiles */
).then((CompilerOutput outputCompile) {
expect(logger.errorText,
equals('compiler message: line1\ncompiler message: line2\n'));
equals('\nCompiler message:\nline1\nline2\n'));
expect(outputCompile.outputFilename, equals('/path/to/main.dart.dill'));
compileExpressionResponseCompleter1.complete(Future<List<int>>.value(utf8.encode(
......@@ -363,6 +371,7 @@ void main() {
expect(await lastExpressionCompleted.future, isTrue);
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
Logger: () => BufferLogger()..supportsColor = false,
});
});
}
......
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