Commit b9f49a40 authored by Adam Barth's avatar Adam Barth Committed by GitHub

Handle missing flutter_test dependency cleanly (#7421)

We now produce a more reasonable error message when we're missing the
flutter_test dependency in a test. Also, remove the flutter_tools stack traces
when the engine dies.

Fixes #6187
parent 2155fb74
.atom
.DS_Store
.buildlog
.idea
.packages
.pub/
build/
packages
pubspec.lock
name: missing_dependency_tests
dependencies:
flutter:
sdk: flutter
<<skip until matching line>>
<<stderr>>
<<skip until matching line>>
Failed to load test harness\. +Are you missing a dependency on flutter_test\?
\ No newline at end of file
// 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 'package:test/test.dart';
void main() {
test('Trival test', () {
expect(42, 42);
});
}
...@@ -105,7 +105,7 @@ class FlutterPlatform extends PlatformPlugin { ...@@ -105,7 +105,7 @@ class FlutterPlatform extends PlatformPlugin {
subprocessActive = false; subprocessActive = false;
if (!controllerSinkClosed && exitCode != 0) { if (!controllerSinkClosed && exitCode != 0) {
String message = _getErrorMessage(_getExitCodeMessage(exitCode, 'after tests finished'), testPath, shellPath); String message = _getErrorMessage(_getExitCodeMessage(exitCode, 'after tests finished'), testPath, shellPath);
controller.sink.addError(new Exception(message)); controller.sink.addError(message);
} }
}); });
...@@ -127,13 +127,13 @@ class FlutterPlatform extends PlatformPlugin { ...@@ -127,13 +127,13 @@ class FlutterPlatform extends PlatformPlugin {
case _InitialResult.crashed: case _InitialResult.crashed:
int exitCode = await process.exitCode; int exitCode = await process.exitCode;
String message = _getErrorMessage(_getExitCodeMessage(exitCode, 'before connecting to test harness'), testPath, shellPath); String message = _getErrorMessage(_getExitCodeMessage(exitCode, 'before connecting to test harness'), testPath, shellPath);
controller.sink.addError(new Exception(message)); controller.sink.addError(message);
controller.sink.close(); controller.sink.close();
await controller.sink.done; await controller.sink.done;
break; break;
case _InitialResult.timedOut: case _InitialResult.timedOut:
String message = _getErrorMessage('Test never connected to test harness.', testPath, shellPath); String message = _getErrorMessage('Test never connected to test harness.', testPath, shellPath);
controller.sink.addError(new Exception(message)); controller.sink.addError(message);
controller.sink.close(); controller.sink.close();
await controller.sink.done; await controller.sink.done;
break; break;
...@@ -169,7 +169,7 @@ class FlutterPlatform extends PlatformPlugin { ...@@ -169,7 +169,7 @@ class FlutterPlatform extends PlatformPlugin {
int exitCode = await process.exitCode; int exitCode = await process.exitCode;
subprocessActive = false; subprocessActive = false;
String message = _getErrorMessage(_getExitCodeMessage(exitCode, 'before test harness closed its WebSocket'), testPath, shellPath); String message = _getErrorMessage(_getExitCodeMessage(exitCode, 'before test harness closed its WebSocket'), testPath, shellPath);
controller.sink.addError(new Exception(message)); controller.sink.addError(message);
controller.sink.close(); controller.sink.close();
await controller.sink.done; await controller.sink.done;
break; break;
...@@ -214,8 +214,12 @@ class FlutterPlatform extends PlatformPlugin { ...@@ -214,8 +214,12 @@ class FlutterPlatform extends PlatformPlugin {
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; // ignore: dart_io_import import 'dart:io'; // ignore: dart_io_import
import 'package:stream_channel/stream_channel.dart'; // We import this library first in order to trigger an import error for
// package:test (rather than package:stream_channel) when the developer forgets
// to add a dependency on package:test.
import 'package:test/src/runner/plugin/remote_platform_helpers.dart'; import 'package:test/src/runner/plugin/remote_platform_helpers.dart';
import 'package:stream_channel/stream_channel.dart';
import 'package:test/src/runner/vm/catch_isolate_errors.dart'; import 'package:test/src/runner/vm/catch_isolate_errors.dart';
import '$testUrl' as test; import '$testUrl' as test;
...@@ -285,7 +289,9 @@ void main() { ...@@ -285,7 +289,9 @@ void main() {
stream.transform(UTF8.decoder) stream.transform(UTF8.decoder)
.transform(const LineSplitter()) .transform(const LineSplitter())
.listen((String line) { .listen((String line) {
if (line != null) if (line.startsWith('error: Unable to read Dart source \'package:test/'))
printError('\n\nFailed to load test harness. Are you missing a dependency on flutter_test?\n');
else if (line != null)
printStatus('Shell: $line'); printStatus('Shell: $line');
}); });
} }
......
...@@ -17,23 +17,30 @@ import 'src/context.dart'; ...@@ -17,23 +17,30 @@ import 'src/context.dart';
void main() { void main() {
group('test', () { group('test', () {
final String automatedTestsDirectory = path.join('..', '..', 'dev', 'automated_tests');
final String flutterTestDirectory = path.join(automatedTestsDirectory, 'flutter_test');
testUsingContext('TestAsyncUtils guarded function test', () async { testUsingContext('TestAsyncUtils guarded function test', () async {
Cache.flutterRoot = '../..'; Cache.flutterRoot = '../..';
return _testFile('test_async_utils_guarded', 1); return _testFile('test_async_utils_guarded', 1, automatedTestsDirectory, flutterTestDirectory);
}); });
testUsingContext('TestAsyncUtils unguarded function test', () async { testUsingContext('TestAsyncUtils unguarded function test', () async {
Cache.flutterRoot = '../..'; Cache.flutterRoot = '../..';
return _testFile('test_async_utils_unguarded', 1); return _testFile('test_async_utils_unguarded', 1, automatedTestsDirectory, flutterTestDirectory);
});
testUsingContext('Missing flutter_test dependency', () async {
final String missingDependencyTests = path.join('..', '..', 'dev', 'missing_dependency_tests');
Cache.flutterRoot = '../..';
return _testFile('trivial', 1, missingDependencyTests, missingDependencyTests);
}); });
}, timeout: new Timeout(const Duration(seconds: 5))); }, timeout: new Timeout(const Duration(seconds: 5)));
} }
Future<Null> _testFile(String testName, int wantedExitCode) async { Future<Null> _testFile(String testName, int wantedExitCode, String workingDirectory, String testDirectory) async {
final String manualTestsDirectory = path.join('..', '..', 'dev', 'automated_tests'); final String fullTestName = path.join(testDirectory, '${testName}_test.dart');
final String fullTestName = path.join(manualTestsDirectory, 'flutter_test', '${testName}_test.dart');
final File testFile = fs.file(fullTestName); final File testFile = fs.file(fullTestName);
expect(testFile.existsSync(), true); expect(testFile.existsSync(), true);
final String fullTestExpectation = path.join(manualTestsDirectory, 'flutter_test', '${testName}_expectation.txt'); final String fullTestExpectation = path.join(testDirectory, '${testName}_expectation.txt');
final File expectationFile = fs.file(fullTestExpectation); final File expectationFile = fs.file(fullTestExpectation);
expect(expectationFile.existsSync(), true); expect(expectationFile.existsSync(), true);
final ProcessResult exec = await Process.run( final ProcessResult exec = await Process.run(
...@@ -44,14 +51,17 @@ Future<Null> _testFile(String testName, int wantedExitCode) async { ...@@ -44,14 +51,17 @@ Future<Null> _testFile(String testName, int wantedExitCode) async {
'--no-color', '--no-color',
fullTestName fullTestName
], ],
workingDirectory: manualTestsDirectory workingDirectory: workingDirectory
); );
expect(exec.exitCode, wantedExitCode); expect(exec.exitCode, wantedExitCode);
final List<String> output = exec.stdout.split('\n'); final List<String> output = exec.stdout.split('\n');
output.add('<<stderr>>');
output.addAll(exec.stderr.split('\n'));
final List<String> expectations = fs.file(fullTestExpectation).readAsLinesSync(); final List<String> expectations = fs.file(fullTestExpectation).readAsLinesSync();
bool allowSkip = false; bool allowSkip = false;
int expectationLineNumber = 0; int expectationLineNumber = 0;
int outputLineNumber = 0; int outputLineNumber = 0;
bool haveSeenStdErrMarker = false;
while (expectationLineNumber < expectations.length) { while (expectationLineNumber < expectations.length) {
expect(output, hasLength(greaterThan(outputLineNumber))); expect(output, hasLength(greaterThan(outputLineNumber)));
final String expectationLine = expectations[expectationLineNumber]; final String expectationLine = expectations[expectationLineNumber];
...@@ -68,10 +78,15 @@ Future<Null> _testFile(String testName, int wantedExitCode) async { ...@@ -68,10 +78,15 @@ Future<Null> _testFile(String testName, int wantedExitCode) async {
} }
allowSkip = false; allowSkip = false;
} }
if (expectationLine == '<<stderr>>') {
expect(haveSeenStdErrMarker, isFalse);
haveSeenStdErrMarker = true;
}
expect(outputLine, matches(expectationLine)); expect(outputLine, matches(expectationLine));
expectationLineNumber += 1; expectationLineNumber += 1;
outputLineNumber += 1; outputLineNumber += 1;
} }
expect(allowSkip, isFalse); expect(allowSkip, isFalse);
expect(exec.stderr, ''); if (!haveSeenStdErrMarker)
expect(exec.stderr, '');
} }
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