diff --git a/dev/missing_dependency_tests/.gitignore b/dev/missing_dependency_tests/.gitignore new file mode 100644 index 0000000000..52808ddc1e --- /dev/null +++ b/dev/missing_dependency_tests/.gitignore @@ -0,0 +1,9 @@ +.atom +.DS_Store +.buildlog +.idea +.packages +.pub/ +build/ +packages +pubspec.lock diff --git a/dev/missing_dependency_tests/pubspec.yaml b/dev/missing_dependency_tests/pubspec.yaml new file mode 100644 index 0000000000..16de1c40a1 --- /dev/null +++ b/dev/missing_dependency_tests/pubspec.yaml @@ -0,0 +1,4 @@ +name: missing_dependency_tests +dependencies: + flutter: + sdk: flutter diff --git a/dev/missing_dependency_tests/trivial_expectation.txt b/dev/missing_dependency_tests/trivial_expectation.txt new file mode 100644 index 0000000000..db9606a323 --- /dev/null +++ b/dev/missing_dependency_tests/trivial_expectation.txt @@ -0,0 +1,4 @@ +<> +<> +<> +Failed to load test harness\. +Are you missing a dependency on flutter_test\? \ No newline at end of file diff --git a/dev/missing_dependency_tests/trivial_test.dart b/dev/missing_dependency_tests/trivial_test.dart new file mode 100644 index 0000000000..b7826d81a0 --- /dev/null +++ b/dev/missing_dependency_tests/trivial_test.dart @@ -0,0 +1,11 @@ +// 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); + }); +} diff --git a/packages/flutter_tools/lib/src/test/flutter_platform.dart b/packages/flutter_tools/lib/src/test/flutter_platform.dart index 93af3f414a..34fa344f4a 100644 --- a/packages/flutter_tools/lib/src/test/flutter_platform.dart +++ b/packages/flutter_tools/lib/src/test/flutter_platform.dart @@ -105,7 +105,7 @@ class FlutterPlatform extends PlatformPlugin { subprocessActive = false; if (!controllerSinkClosed && exitCode != 0) { 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 { case _InitialResult.crashed: int exitCode = await process.exitCode; 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(); await controller.sink.done; break; case _InitialResult.timedOut: String message = _getErrorMessage('Test never connected to test harness.', testPath, shellPath); - controller.sink.addError(new Exception(message)); + controller.sink.addError(message); controller.sink.close(); await controller.sink.done; break; @@ -169,7 +169,7 @@ class FlutterPlatform extends PlatformPlugin { int exitCode = await process.exitCode; subprocessActive = false; 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(); await controller.sink.done; break; @@ -214,8 +214,12 @@ class FlutterPlatform extends PlatformPlugin { import 'dart:convert'; 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:stream_channel/stream_channel.dart'; import 'package:test/src/runner/vm/catch_isolate_errors.dart'; import '$testUrl' as test; @@ -285,7 +289,9 @@ void main() { stream.transform(UTF8.decoder) .transform(const LineSplitter()) .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'); }); } diff --git a/packages/flutter_tools/test/test_test.dart b/packages/flutter_tools/test/test_test.dart index ae5e8a2002..8c0e8413b0 100644 --- a/packages/flutter_tools/test/test_test.dart +++ b/packages/flutter_tools/test/test_test.dart @@ -17,23 +17,30 @@ import 'src/context.dart'; void main() { group('test', () { + final String automatedTestsDirectory = path.join('..', '..', 'dev', 'automated_tests'); + final String flutterTestDirectory = path.join(automatedTestsDirectory, 'flutter_test'); + testUsingContext('TestAsyncUtils guarded function test', () async { Cache.flutterRoot = '../..'; - return _testFile('test_async_utils_guarded', 1); + return _testFile('test_async_utils_guarded', 1, automatedTestsDirectory, flutterTestDirectory); }); testUsingContext('TestAsyncUtils unguarded function test', () async { 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))); } -Future _testFile(String testName, int wantedExitCode) async { - final String manualTestsDirectory = path.join('..', '..', 'dev', 'automated_tests'); - final String fullTestName = path.join(manualTestsDirectory, 'flutter_test', '${testName}_test.dart'); +Future _testFile(String testName, int wantedExitCode, String workingDirectory, String testDirectory) async { + final String fullTestName = path.join(testDirectory, '${testName}_test.dart'); final File testFile = fs.file(fullTestName); 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); expect(expectationFile.existsSync(), true); final ProcessResult exec = await Process.run( @@ -44,14 +51,17 @@ Future _testFile(String testName, int wantedExitCode) async { '--no-color', fullTestName ], - workingDirectory: manualTestsDirectory + workingDirectory: workingDirectory ); expect(exec.exitCode, wantedExitCode); final List output = exec.stdout.split('\n'); + output.add('<>'); + output.addAll(exec.stderr.split('\n')); final List 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))); final String expectationLine = expectations[expectationLineNumber]; @@ -68,10 +78,15 @@ Future _testFile(String testName, int wantedExitCode) async { } allowSkip = false; } + if (expectationLine == '<>') { + expect(haveSeenStdErrMarker, isFalse); + haveSeenStdErrMarker = true; + } expect(outputLine, matches(expectationLine)); expectationLineNumber += 1; outputLineNumber += 1; } expect(allowSkip, isFalse); - expect(exec.stderr, ''); + if (!haveSeenStdErrMarker) + expect(exec.stderr, ''); }