diff --git a/dev/missing_dependency_tests/trivial_expectation.txt b/dev/missing_dependency_tests/trivial_expectation.txt index 79bf7d320f..cfd88b3648 100644 --- a/dev/missing_dependency_tests/trivial_expectation.txt +++ b/dev/missing_dependency_tests/trivial_expectation.txt @@ -1,7 +1,7 @@ <> <> <> -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: diff --git a/packages/flutter_tools/README.md b/packages/flutter_tools/README.md index 7bd4deabef..2f6b29b296 100644 --- a/packages/flutter_tools/README.md +++ b/packages/flutter_tools/README.md @@ -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 diff --git a/packages/flutter_tools/lib/src/commands/test.dart b/packages/flutter_tools/lib/src/commands/test.dart index 246f28793e..f6ae0791dc 100644 --- a/packages/flutter_tools/lib/src/commands/test.dart +++ b/packages/flutter_tools/lib/src/commands/test.dart @@ -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) { diff --git a/packages/flutter_tools/lib/src/test/flutter_platform.dart b/packages/flutter_tools/lib/src/test/flutter_platform.dart index 4d35eb9073..408ba646ce 100644 --- a/packages/flutter_tools/lib/src/test/flutter_platform.dart +++ b/packages/flutter_tools/lib/src/test/flutter_platform.dart @@ -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 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 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; - } + final StreamChannel channel = loadChannel(path, platform); + final RunnerSuiteController controller = deserializeSuite(path, platform, + suiteConfig, const PluginEnvironment(), channel, message); + 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}' ); } diff --git a/packages/flutter_tools/lib/src/test/runner.dart b/packages/flutter_tools/lib/src/test/runner.dart index fb71684fde..dfc23252f4 100644 --- a/packages/flutter_tools/lib/src/test/runner.dart +++ b/packages/flutter_tools/lib/src/test/runner.dart @@ -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) ...['-r', 'json'] else - ...['-r', 'compact'], + ...['-r', reporter ?? 'compact'], + if (timeout != null) + ...['--timeout', timeout], '--concurrency=$concurrency', for (final String name in names) ...['--name', name], diff --git a/packages/flutter_tools/lib/src/test/test_compiler.dart b/packages/flutter_tools/lib/src/test/test_compiler.dart index e8ac393f94..fbb79ad9b5 100644 --- a/packages/flutter_tools/lib/src/test/test_compiler.dart +++ b/packages/flutter_tools/lib/src/test/test_compiler.dart @@ -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' diff --git a/packages/flutter_tools/test/commands.shard/hermetic/test_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/test_test.dart index 31a9953c1d..ea891244a8 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/test_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/test_test.dart @@ -188,6 +188,8 @@ class FakeFlutterTestRunner implements FlutterTestRunner { @override List extraFrontEndOptions, bool nullAssertions = false, BuildInfo buildInfo, + String reporter, + String timeout, }) async { lastEnableObservatoryValue = enableObservatory; return exitCode; diff --git a/packages/flutter_tools/test/general.shard/test_compiler_test.dart b/packages/flutter_tools/test/general.shard/test_compiler_test.dart index 1597ea3962..1c6bc87062 100644 --- a/packages/flutter_tools/test/general.shard/test_compiler_test.dart +++ b/packages/flutter_tools/test/general.shard/test_compiler_test.dart @@ -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.parse('test/foo.dart')],