flutter/packages/flutter_tools/test/commands/test_test.dart
Greg Spencer efcd9a8001
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
2018-09-20 15:45:48 -07:00

187 lines
7.8 KiB
Dart

// Copyright 2015 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 'dart:io' as io;
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/dart/sdk.dart';
import '../src/common.dart';
import '../src/context.dart';
// This test depends on some files in ///dev/automated_tests/flutter_test/*
Future<Null> _testExclusionLock;
void main() {
group('flutter test should', () {
final String automatedTestsDirectory = fs.path.join('..', '..', 'dev', 'automated_tests');
final String flutterTestDirectory = fs.path.join(automatedTestsDirectory, 'flutter_test');
testUsingContext('not have extraneous error messages', () async {
Cache.flutterRoot = '../..';
return _testFile('trivial_widget', automatedTestsDirectory, flutterTestDirectory, exitCode: isZero);
}, skip: io.Platform.isLinux); // Flutter on Linux sometimes has problems with font resolution (#7224)
testUsingContext('report nice errors for exceptions thrown within testWidgets()', () async {
Cache.flutterRoot = '../..';
return _testFile('exception_handling', automatedTestsDirectory, flutterTestDirectory);
}, skip: io.Platform.isWindows); // Dart on Windows has trouble with unicode characters in output
testUsingContext('report a nice error when a guarded function was called without await', () async {
Cache.flutterRoot = '../..';
return _testFile('test_async_utils_guarded', automatedTestsDirectory, flutterTestDirectory);
}, skip: io.Platform.isWindows); // Dart on Windows has trouble with unicode characters in output
testUsingContext('report a nice error when an async function was called without await', () async {
Cache.flutterRoot = '../..';
return _testFile('test_async_utils_unguarded', automatedTestsDirectory, flutterTestDirectory);
}, skip: io.Platform.isWindows); // Dart on Windows has trouble with unicode characters in output
testUsingContext('report a nice error when a Ticker is left running', () async {
Cache.flutterRoot = '../..';
return _testFile('ticker', automatedTestsDirectory, flutterTestDirectory);
}, skip: io.Platform.isWindows); // Dart on Windows has trouble with unicode characters in output
testUsingContext('report a nice error when a pubspec.yaml is missing a flutter_test dependency', () async {
final String missingDependencyTests = fs.path.join('..', '..', 'dev', 'missing_dependency_tests');
Cache.flutterRoot = '../..';
return _testFile('trivial', missingDependencyTests, missingDependencyTests);
}, skip: io.Platform.isWindows); // Dart on Windows has trouble with unicode characters in output
testUsingContext('run a test when its name matches a regexp', () async {
Cache.flutterRoot = '../..';
final ProcessResult result = await _runFlutterTest('filtering', automatedTestsDirectory, flutterTestDirectory,
extraArgs: const <String>['--name', 'inc.*de']);
if (!result.stdout.contains('+1: All tests passed'))
fail('unexpected output from test:\n\n${result.stdout}\n-- end stdout --\n\n');
expect(result.exitCode, 0);
});
testUsingContext('run a test when its name contains a string', () async {
Cache.flutterRoot = '../..';
final ProcessResult result = await _runFlutterTest('filtering', automatedTestsDirectory, flutterTestDirectory,
extraArgs: const <String>['--plain-name', 'include']);
if (!result.stdout.contains('+1: All tests passed'))
fail('unexpected output from test:\n\n${result.stdout}\n-- end stdout --\n\n');
expect(result.exitCode, 0);
});
testUsingContext('test runs to completion', () async {
Cache.flutterRoot = '../..';
final ProcessResult result = await _runFlutterTest('trivial', automatedTestsDirectory, flutterTestDirectory,
extraArgs: const <String>['--verbose']);
if ((!result.stdout.contains('+1: All tests passed')) ||
(!result.stdout.contains('test 0: starting shell process')) ||
(!result.stdout.contains('test 0: deleting temporary directory')) ||
(!result.stdout.contains('test 0: finished')) ||
(!result.stdout.contains('test package returned with exit code 0')))
fail('unexpected output from test:\n\n${result.stdout}\n-- end stdout --\n\n');
if (result.stderr.isNotEmpty)
fail('unexpected error output from test:\n\n${result.stderr}\n-- end stderr --\n\n');
expect(result.exitCode, 0);
});
});
}
Future<Null> _testFile(String testName, String workingDirectory, String testDirectory, {Matcher exitCode}) async {
exitCode ??= isNonZero;
final String fullTestExpectation = fs.path.join(testDirectory, '${testName}_expectation.txt');
final File expectationFile = fs.file(fullTestExpectation);
if (!expectationFile.existsSync())
fail('missing expectation file: $expectationFile');
while (_testExclusionLock != null)
await _testExclusionLock;
final ProcessResult exec = await _runFlutterTest(testName, workingDirectory, testDirectory);
expect(exec.exitCode, exitCode);
final List<String> output = exec.stdout.split('\n');
if (output.first == 'Waiting for another flutter command to release the startup lock...')
output.removeAt(0);
if (output.first.startsWith('Running "flutter packages get" in'))
output.removeAt(0);
output.add('<<stderr>>');
output.addAll(exec.stderr.split('\n'));
final List<String> expectations = fs.file(fullTestExpectation).readAsLinesSync();
bool allowSkip = false;
int expectationLineNumber = 0;
int outputLineNumber = 0;
bool haveSeenStdErrMarker = false;
while (expectationLineNumber < expectations.length) {
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>>') {
allowSkip = true;
expectationLineNumber += 1;
continue;
}
if (allowSkip) {
if (!RegExp(expectationLine).hasMatch(outputLine)) {
outputLineNumber += 1;
continue;
}
allowSkip = false;
}
if (expectationLine == '<<stderr>>') {
expect(haveSeenStdErrMarker, isFalse);
haveSeenStdErrMarker = true;
}
expect(outputLine, matches(expectationLine), reason: 'Full output:\n- - - -----8<----- - - -\n${output.join("\n")}\n- - - -----8<----- - - -');
expectationLineNumber += 1;
outputLineNumber += 1;
}
expect(allowSkip, isFalse);
if (!haveSeenStdErrMarker)
expect(exec.stderr, '');
}
Future<ProcessResult> _runFlutterTest(
String testName,
String workingDirectory,
String testDirectory, {
List<String> extraArgs = const <String>[],
}) async {
final String testFilePath = fs.path.join(testDirectory, '${testName}_test.dart');
final File testFile = fs.file(testFilePath);
if (!testFile.existsSync())
fail('missing test file: $testFile');
final List<String> args = <String>[]
..addAll(dartVmFlags)
..add(fs.path.absolute(fs.path.join('bin', 'flutter_tools.dart')))
..add('test')
..add('--no-color')
..addAll(extraArgs)
..add(testFilePath);
while (_testExclusionLock != null)
await _testExclusionLock;
final Completer<Null> testExclusionCompleter = Completer<Null>();
_testExclusionLock = testExclusionCompleter.future;
try {
return await Process.run(
fs.path.join(dartSdkPath, 'bin', 'dart'),
args,
workingDirectory: workingDirectory,
);
} finally {
_testExclusionLock = null;
testExclusionCompleter.complete();
}
}