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>>
<<stderr>>
<<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:
flutter_test:
......
......@@ -15,14 +15,14 @@ which we follow.
First, ensure that the Dart SDK and other necessary artifacts are available by
invoking the Flutter Tools wrapper script. In this directory run:
```shell
$ ../../bin/flutter --version
$ flutter --version
```
### Running the Tool
To run Flutter Tools from source, in this directory run:
```shell
$ ../../bin/dart bin/flutter_tools.dart
$ dart bin/flutter_tools.dart
```
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:
```shell
$ ../../bin/flutter analyze
$ flutter analyze
```
### Writing tests
......@@ -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
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
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
```shell
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
To run the tests in the `test/` directory, first ensure that there are no
connected devices. Then, in this directory run:
To run the tests in the `test/` directory:
```shell
$ ../../bin/dart pub run test
$ flutter test
```
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
directory run:
```shell
$ ../../bin/dart pub run test test/general.shard
$ flutter test test/general.shard
```
To run the tests in a specific file, run:
```shell
$ ../../bin/dart pub run test test/general.shard/utils_test.dart
$ flutter test test/general.shard/utils_test.dart
```
### Forcing snapshot regeneration
......
......@@ -131,6 +131,17 @@ class TestCommand extends FlutterCommand {
'interact with the vmservice at runtime.\n'
'This flag is ignored if --start-paused or coverage are requested. '
'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);
}
......@@ -263,6 +274,8 @@ class TestCommand extends FlutterCommand {
web: stringArg('platform') == 'chrome',
randomSeed: stringArg('test-randomize-ordering-seed'),
nullAssertions: boolArg(FlutterOptions.kNullAssertions),
reporter: stringArg('reporter'),
timeout: stringArg('timeout'),
);
if (collector != null) {
......
......@@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'dart:async';
import 'dart:io' as io; // ignore: dart_io_import;
import 'package:dds/dds.dart';
import 'package:meta/meta.dart';
......@@ -125,6 +126,7 @@ String generateTestBootstrap({
bool updateGoldens = false,
String languageVersionHeader = '',
bool nullSafety = false,
bool flutterTestDep = true,
}) {
assert(testUrl != null);
assert(host != null);
......@@ -142,8 +144,13 @@ import 'dart:async';
import 'dart:convert'; // ignore: dart_convert_import
import 'dart:io'; // ignore: dart_io_import
import 'dart:isolate';
''');
if (flutterTestDep) {
buffer.write('''
import 'package:flutter_test/flutter_test.dart';
''');
}
buffer.write('''
import 'package:test_api/src/remote_listener.dart';
import 'package:stream_channel/stream_channel.dart';
import 'package:stack_trace/stack_trace.dart';
......@@ -186,9 +193,13 @@ void main() {
String server = Uri.decodeComponent('$encodedWebsocketUrl:\$serverPort');
StreamChannel<dynamic> channel = serializeSuite(() {
catchIsolateErrors();
goldenFileComparator = new LocalFileComparator(Uri.parse('$testUrl'));
autoUpdateGoldenFiles = $updateGoldens;
''');
if (flutterTestDep) {
buffer.write('''
goldenFileComparator = LocalFileComparator(Uri.parse('$testUrl'));
autoUpdateGoldenFiles = $updateGoldens;
''');
}
if (testConfigFile != null) {
buffer.write('''
return () => test_config.testExecutable(test.main);
......@@ -289,23 +300,10 @@ class FlutterPlatform extends PlatformPlugin {
// LoadSuite to emit an error, which will be presented to the user.
// Except for the Declarer error, which is a specific test incompatibility
// error we need to catch.
try {
final StreamChannel<dynamic> channel = loadChannel(path, platform);
final RunnerSuiteController controller = deserializeSuite(path, platform,
suiteConfig, const PluginEnvironment(), channel, message);
return await 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;
}
return controller.suite;
}
@override
......@@ -457,7 +455,7 @@ class FlutterPlatform extends PlatformPlugin {
finalizers.add(() async {
if (subprocessActive) {
globals.printTrace('test $ourTestCount: ensuring end-of-process for shell');
process.kill();
process.kill(io.ProcessSignal.sigkill);
final int exitCode = await process.exitCode;
subprocessActive = false;
if (!controllerSinkClosed && exitCode != -15) {
......@@ -722,6 +720,7 @@ class FlutterPlatform extends PlatformPlugin {
testConfigFile: findTestConfigFile(globals.fs.file(testUrl)),
host: host,
updateGoldens: updateGoldens,
flutterTestDep: _packageConfig['flutter_test'] != null,
languageVersionHeader: '// @dart=${languageVersion.major}.${languageVersion.minor}'
);
}
......
......@@ -49,6 +49,8 @@ abstract class FlutterTestRunner {
String randomSeed,
bool nullAssertions = false,
@required BuildInfo buildInfo,
String reporter,
String timeout,
});
}
......@@ -83,6 +85,8 @@ class _FlutterTestRunnerImpl implements FlutterTestRunner {
String randomSeed,
bool nullAssertions = false,
@required BuildInfo buildInfo,
String reporter,
String timeout,
}) async {
// Configure package:test to use the Flutter engine for child processes.
final String shellPath = globals.artifacts.getArtifactPath(Artifact.flutterTester);
......@@ -99,7 +103,9 @@ class _FlutterTestRunnerImpl implements FlutterTestRunner {
if (machine)
...<String>['-r', 'json']
else
...<String>['-r', 'compact'],
...<String>['-r', reporter ?? 'compact'],
if (timeout != null)
...<String>['--timeout', timeout],
'--concurrency=$concurrency',
for (final String name in names)
...<String>['--name', name],
......
......@@ -134,10 +134,10 @@ class TestCompiler {
);
// Compilation will fail if there is no flutter_test dependency, since
// this library is imported by the generated entrypoint script.
if (_packageConfig['flutter_test'] == null) {
if (_packageConfig['test_api'] == null) {
globals.printError(
'\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:'
'\n\n'
'dev_dependencies:\n'
......
......@@ -188,6 +188,8 @@ class FakeFlutterTestRunner implements FlutterTestRunner {
@override List<String> extraFrontEndOptions,
bool nullAssertions = false,
BuildInfo buildInfo,
String reporter,
String timeout,
}) async {
lastEnableObservatoryValue = enableObservatory;
return exitCode;
......
......@@ -33,7 +33,7 @@ void main() {
fileSystem.file('test/foo.dart').createSync(recursive: true);
fileSystem.file('.packages')
..createSync()
..writeAsStringSync('flutter_test:flutter_test/');
..writeAsStringSync('test_api:test_api/\n');
residentCompiler = MockResidentCompiler();
});
......@@ -109,7 +109,7 @@ void main() {
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(
BuildInfo.debug,
FlutterProject.current(),
......@@ -118,7 +118,8 @@ void main() {
fileSystem.file('.packages').writeAsStringSync('\n');
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(
any,
<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