Unverified Commit 5fedad91 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] allow using flutter test for testing the tool too (#69911)

parent a0ec4d67
<<skip until matching line>> <<skip until matching line>>
<<stderr>> <<stderr>>
<<skip until matching line>> <<skip until matching line>>
Error: cannot run without a dependency on "package:flutter_test". Ensure the following lines are present in your pubspec.yaml: Error: cannot run without a dependency on either "package:flutter_test" or "package:test". Ensure the following lines are present in your pubspec.yaml:
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
......
...@@ -15,14 +15,14 @@ which we follow. ...@@ -15,14 +15,14 @@ which we follow.
First, ensure that the Dart SDK and other necessary artifacts are available by First, ensure that the Dart SDK and other necessary artifacts are available by
invoking the Flutter Tools wrapper script. In this directory run: invoking the Flutter Tools wrapper script. In this directory run:
```shell ```shell
$ ../../bin/flutter --version $ flutter --version
``` ```
### Running the Tool ### Running the Tool
To run Flutter Tools from source, in this directory run: To run Flutter Tools from source, in this directory run:
```shell ```shell
$ ../../bin/dart bin/flutter_tools.dart $ dart bin/flutter_tools.dart
``` ```
followed by command-line arguments, as usual. followed by command-line arguments, as usual.
...@@ -31,7 +31,7 @@ followed by command-line arguments, as usual. ...@@ -31,7 +31,7 @@ followed by command-line arguments, as usual.
To run the analyzer on Flutter Tools, in this directory run: To run the analyzer on Flutter Tools, in this directory run:
```shell ```shell
$ ../../bin/flutter analyze $ flutter analyze
``` ```
### Writing tests ### Writing tests
...@@ -50,12 +50,6 @@ In general, the tests for the code in a file called `file.dart` should go in a ...@@ -50,12 +50,6 @@ In general, the tests for the code in a file called `file.dart` should go in a
file called `file_test.dart` in the subdirectory that matches the behavior of file called `file_test.dart` in the subdirectory that matches the behavior of
the test. the test.
We measure [test coverage](https://codecov.io/gh/flutter/flutter) post-submit.
A change that deletes code might decrease test coverage, however, most changes
that add new code should aim to increase coverage. In particular, the coverage
of the diff should be close to the average coverage, and should ideally be
better.
#### Using local engine builds in integration tests #### Using local engine builds in integration tests
The integration tests can be configured to use a specific local engine The integration tests can be configured to use a specific local engine
...@@ -67,15 +61,15 @@ environment variable. This second variable is not necessary if the `flutter` and ...@@ -67,15 +61,15 @@ environment variable. This second variable is not necessary if the `flutter` and
```shell ```shell
export FLUTTER_LOCAL_ENGINE=android_debug_unopt export FLUTTER_LOCAL_ENGINE=android_debug_unopt
../../bin/dart test test/integration.shard/some_test_case flutter test test/integration.shard/some_test_case
``` ```
### Running the tests ### Running the tests
To run the tests in the `test/` directory, first ensure that there are no To run the tests in the `test/` directory:
connected devices. Then, in this directory run:
```shell ```shell
$ ../../bin/dart pub run test $ flutter test
``` ```
The tests in `test/integration.shard` are slower to run than the tests in The tests in `test/integration.shard` are slower to run than the tests in
...@@ -83,12 +77,12 @@ The tests in `test/integration.shard` are slower to run than the tests in ...@@ -83,12 +77,12 @@ The tests in `test/integration.shard` are slower to run than the tests in
to be set and pointing to the root of the Flutter SDK. To run only the tests in `test/general.shard`, in this to be set and pointing to the root of the Flutter SDK. To run only the tests in `test/general.shard`, in this
directory run: directory run:
```shell ```shell
$ ../../bin/dart pub run test test/general.shard $ flutter test test/general.shard
``` ```
To run the tests in a specific file, run: To run the tests in a specific file, run:
```shell ```shell
$ ../../bin/dart pub run test test/general.shard/utils_test.dart $ flutter test test/general.shard/utils_test.dart
``` ```
### Forcing snapshot regeneration ### Forcing snapshot regeneration
......
...@@ -131,6 +131,17 @@ class TestCommand extends FlutterCommand { ...@@ -131,6 +131,17 @@ class TestCommand extends FlutterCommand {
'interact with the vmservice at runtime.\n' 'interact with the vmservice at runtime.\n'
'This flag is ignored if --start-paused or coverage are requested. ' 'This flag is ignored if --start-paused or coverage are requested. '
'The vmservice will be enabled no matter what in those cases.' 'The vmservice will be enabled no matter what in those cases.'
)
..addOption('reporter',
abbr: 'r',
defaultsTo: 'compact',
help: 'Set how to print test results.\n'
'[compact] (default) A single line, updated continuously.\n'
'[expanded] A separate line for each update.\n'
'[json] A machine-readable format (see https://dart.dev/go/test-docs/json_reporter.md).\n')
..addOption('timeout',
help: 'The default test timeout. For example: 15s, 2x, none. Defaults to "30s"',
defaultsTo: '30s',
); );
addDdsOptions(verboseHelp: verboseHelp); addDdsOptions(verboseHelp: verboseHelp);
} }
...@@ -263,6 +274,8 @@ class TestCommand extends FlutterCommand { ...@@ -263,6 +274,8 @@ class TestCommand extends FlutterCommand {
web: stringArg('platform') == 'chrome', web: stringArg('platform') == 'chrome',
randomSeed: stringArg('test-randomize-ordering-seed'), randomSeed: stringArg('test-randomize-ordering-seed'),
nullAssertions: boolArg(FlutterOptions.kNullAssertions), nullAssertions: boolArg(FlutterOptions.kNullAssertions),
reporter: stringArg('reporter'),
timeout: stringArg('timeout'),
); );
if (collector != null) { if (collector != null) {
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:async'; import 'dart:async';
import 'dart:io' as io; // ignore: dart_io_import;
import 'package:dds/dds.dart'; import 'package:dds/dds.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
...@@ -125,6 +126,7 @@ String generateTestBootstrap({ ...@@ -125,6 +126,7 @@ String generateTestBootstrap({
bool updateGoldens = false, bool updateGoldens = false,
String languageVersionHeader = '', String languageVersionHeader = '',
bool nullSafety = false, bool nullSafety = false,
bool flutterTestDep = true,
}) { }) {
assert(testUrl != null); assert(testUrl != null);
assert(host != null); assert(host != null);
...@@ -142,8 +144,13 @@ import 'dart:async'; ...@@ -142,8 +144,13 @@ import 'dart:async';
import 'dart:convert'; // ignore: dart_convert_import import 'dart:convert'; // ignore: dart_convert_import
import 'dart:io'; // ignore: dart_io_import import 'dart:io'; // ignore: dart_io_import
import 'dart:isolate'; import 'dart:isolate';
''');
if (flutterTestDep) {
buffer.write('''
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
''');
}
buffer.write('''
import 'package:test_api/src/remote_listener.dart'; import 'package:test_api/src/remote_listener.dart';
import 'package:stream_channel/stream_channel.dart'; import 'package:stream_channel/stream_channel.dart';
import 'package:stack_trace/stack_trace.dart'; import 'package:stack_trace/stack_trace.dart';
...@@ -186,9 +193,13 @@ void main() { ...@@ -186,9 +193,13 @@ void main() {
String server = Uri.decodeComponent('$encodedWebsocketUrl:\$serverPort'); String server = Uri.decodeComponent('$encodedWebsocketUrl:\$serverPort');
StreamChannel<dynamic> channel = serializeSuite(() { StreamChannel<dynamic> channel = serializeSuite(() {
catchIsolateErrors(); catchIsolateErrors();
goldenFileComparator = new LocalFileComparator(Uri.parse('$testUrl'));
autoUpdateGoldenFiles = $updateGoldens;
'''); ''');
if (flutterTestDep) {
buffer.write('''
goldenFileComparator = LocalFileComparator(Uri.parse('$testUrl'));
autoUpdateGoldenFiles = $updateGoldens;
''');
}
if (testConfigFile != null) { if (testConfigFile != null) {
buffer.write(''' buffer.write('''
return () => test_config.testExecutable(test.main); return () => test_config.testExecutable(test.main);
...@@ -289,23 +300,10 @@ class FlutterPlatform extends PlatformPlugin { ...@@ -289,23 +300,10 @@ class FlutterPlatform extends PlatformPlugin {
// LoadSuite to emit an error, which will be presented to the user. // LoadSuite to emit an error, which will be presented to the user.
// Except for the Declarer error, which is a specific test incompatibility // Except for the Declarer error, which is a specific test incompatibility
// error we need to catch. // error we need to catch.
try {
final StreamChannel<dynamic> channel = loadChannel(path, platform); final StreamChannel<dynamic> channel = loadChannel(path, platform);
final RunnerSuiteController controller = deserializeSuite(path, platform, final RunnerSuiteController controller = deserializeSuite(path, platform,
suiteConfig, const PluginEnvironment(), channel, message); suiteConfig, const PluginEnvironment(), channel, message);
return await controller.suite; return controller.suite;
} on Exception catch (err) {
/// Rethrow a less confusing error if it is a test incompatibility.
if (err.toString().contains("type 'Declarer' is not a subtype of type 'Declarer'")) {
throw UnsupportedError('Package incompatibility between flutter and test packages:\n'
' * flutter is incompatible with test <1.4.0.\n'
' * flutter is incompatible with mockito <4.0.0\n'
"To fix this error, update test to at least '^1.4.0' and mockito to at least '^4.0.0'\n"
);
}
// Guess it was a different error.
rethrow;
}
} }
@override @override
...@@ -457,7 +455,7 @@ class FlutterPlatform extends PlatformPlugin { ...@@ -457,7 +455,7 @@ class FlutterPlatform extends PlatformPlugin {
finalizers.add(() async { finalizers.add(() async {
if (subprocessActive) { if (subprocessActive) {
globals.printTrace('test $ourTestCount: ensuring end-of-process for shell'); globals.printTrace('test $ourTestCount: ensuring end-of-process for shell');
process.kill(); process.kill(io.ProcessSignal.sigkill);
final int exitCode = await process.exitCode; final int exitCode = await process.exitCode;
subprocessActive = false; subprocessActive = false;
if (!controllerSinkClosed && exitCode != -15) { if (!controllerSinkClosed && exitCode != -15) {
...@@ -722,6 +720,7 @@ class FlutterPlatform extends PlatformPlugin { ...@@ -722,6 +720,7 @@ class FlutterPlatform extends PlatformPlugin {
testConfigFile: findTestConfigFile(globals.fs.file(testUrl)), testConfigFile: findTestConfigFile(globals.fs.file(testUrl)),
host: host, host: host,
updateGoldens: updateGoldens, updateGoldens: updateGoldens,
flutterTestDep: _packageConfig['flutter_test'] != null,
languageVersionHeader: '// @dart=${languageVersion.major}.${languageVersion.minor}' languageVersionHeader: '// @dart=${languageVersion.major}.${languageVersion.minor}'
); );
} }
......
...@@ -49,6 +49,8 @@ abstract class FlutterTestRunner { ...@@ -49,6 +49,8 @@ abstract class FlutterTestRunner {
String randomSeed, String randomSeed,
bool nullAssertions = false, bool nullAssertions = false,
@required BuildInfo buildInfo, @required BuildInfo buildInfo,
String reporter,
String timeout,
}); });
} }
...@@ -83,6 +85,8 @@ class _FlutterTestRunnerImpl implements FlutterTestRunner { ...@@ -83,6 +85,8 @@ class _FlutterTestRunnerImpl implements FlutterTestRunner {
String randomSeed, String randomSeed,
bool nullAssertions = false, bool nullAssertions = false,
@required BuildInfo buildInfo, @required BuildInfo buildInfo,
String reporter,
String timeout,
}) async { }) async {
// Configure package:test to use the Flutter engine for child processes. // Configure package:test to use the Flutter engine for child processes.
final String shellPath = globals.artifacts.getArtifactPath(Artifact.flutterTester); final String shellPath = globals.artifacts.getArtifactPath(Artifact.flutterTester);
...@@ -99,7 +103,9 @@ class _FlutterTestRunnerImpl implements FlutterTestRunner { ...@@ -99,7 +103,9 @@ class _FlutterTestRunnerImpl implements FlutterTestRunner {
if (machine) if (machine)
...<String>['-r', 'json'] ...<String>['-r', 'json']
else else
...<String>['-r', 'compact'], ...<String>['-r', reporter ?? 'compact'],
if (timeout != null)
...<String>['--timeout', timeout],
'--concurrency=$concurrency', '--concurrency=$concurrency',
for (final String name in names) for (final String name in names)
...<String>['--name', name], ...<String>['--name', name],
......
...@@ -134,10 +134,10 @@ class TestCompiler { ...@@ -134,10 +134,10 @@ class TestCompiler {
); );
// Compilation will fail if there is no flutter_test dependency, since // Compilation will fail if there is no flutter_test dependency, since
// this library is imported by the generated entrypoint script. // this library is imported by the generated entrypoint script.
if (_packageConfig['flutter_test'] == null) { if (_packageConfig['test_api'] == null) {
globals.printError( globals.printError(
'\n' '\n'
'Error: cannot run without a dependency on "package:flutter_test". ' 'Error: cannot run without a dependency on either "package:flutter_test" or "package:test". '
'Ensure the following lines are present in your pubspec.yaml:' 'Ensure the following lines are present in your pubspec.yaml:'
'\n\n' '\n\n'
'dev_dependencies:\n' 'dev_dependencies:\n'
......
...@@ -188,6 +188,8 @@ class FakeFlutterTestRunner implements FlutterTestRunner { ...@@ -188,6 +188,8 @@ class FakeFlutterTestRunner implements FlutterTestRunner {
@override List<String> extraFrontEndOptions, @override List<String> extraFrontEndOptions,
bool nullAssertions = false, bool nullAssertions = false,
BuildInfo buildInfo, BuildInfo buildInfo,
String reporter,
String timeout,
}) async { }) async {
lastEnableObservatoryValue = enableObservatory; lastEnableObservatoryValue = enableObservatory;
return exitCode; return exitCode;
......
...@@ -33,7 +33,7 @@ void main() { ...@@ -33,7 +33,7 @@ void main() {
fileSystem.file('test/foo.dart').createSync(recursive: true); fileSystem.file('test/foo.dart').createSync(recursive: true);
fileSystem.file('.packages') fileSystem.file('.packages')
..createSync() ..createSync()
..writeAsStringSync('flutter_test:flutter_test/'); ..writeAsStringSync('test_api:test_api/\n');
residentCompiler = MockResidentCompiler(); residentCompiler = MockResidentCompiler();
}); });
...@@ -109,7 +109,7 @@ void main() { ...@@ -109,7 +109,7 @@ void main() {
Logger: () => BufferLogger.test(), Logger: () => BufferLogger.test(),
}); });
testUsingContext('TestCompiler reports an error when there is no dependency on flutter_test', () async { testUsingContext('TestCompiler reports an error when there is no dependency on flutter_test or test', () async {
final FakeTestCompiler testCompiler = FakeTestCompiler( final FakeTestCompiler testCompiler = FakeTestCompiler(
BuildInfo.debug, BuildInfo.debug,
FlutterProject.current(), FlutterProject.current(),
...@@ -118,7 +118,8 @@ void main() { ...@@ -118,7 +118,8 @@ void main() {
fileSystem.file('.packages').writeAsStringSync('\n'); fileSystem.file('.packages').writeAsStringSync('\n');
expect(await testCompiler.compile(Uri.parse('test/foo.dart')), null); expect(await testCompiler.compile(Uri.parse('test/foo.dart')), null);
expect(testLogger.errorText, contains('Error: cannot run without a dependency on "package:flutter_test"')); expect(testLogger.errorText, contains('Error: cannot run without a dependency on '
'either "package:flutter_test" or "package:test'));
verifyNever(residentCompiler.recompile( verifyNever(residentCompiler.recompile(
any, any,
<Uri>[Uri.parse('test/foo.dart')], <Uri>[Uri.parse('test/foo.dart')],
......
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