diff --git a/packages/flutter_tools/lib/src/base/common.dart b/packages/flutter_tools/lib/src/base/common.dart index f2a891c33a5..e124cfab93d 100644 --- a/packages/flutter_tools/lib/src/base/common.dart +++ b/packages/flutter_tools/lib/src/base/common.dart @@ -17,7 +17,7 @@ Never throwToolExit(String message, { int? exitCode }) { class ToolExit implements Exception { ToolExit(this.message, { this.exitCode }); - final String message; + final String? message; final int? exitCode; @override diff --git a/packages/flutter_tools/test/commands.shard/hermetic/analyze_continuously_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/analyze_continuously_test.dart index c0dfc4c452a..773d33ff7fa 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/analyze_continuously_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/analyze_continuously_test.dart @@ -24,6 +24,7 @@ import 'package:process/process.dart'; import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/fake_process_manager.dart'; +import '../../src/test_flutter_command_runner.dart'; void main() { setUpAll(() { diff --git a/packages/flutter_tools/test/commands.shard/hermetic/assemble_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/assemble_test.dart index cdfd4e2b71c..e900bf68792 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/assemble_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/assemble_test.dart @@ -19,6 +19,7 @@ import 'package:flutter_tools/src/globals_null_migrated.dart' as globals; import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/fakes.dart'; +import '../../src/test_flutter_command_runner.dart'; import '../../src/testbed.dart'; void main() { diff --git a/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart index 6caaf95dae7..22bfbc531c3 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart @@ -29,8 +29,10 @@ import 'package:vm_service/vm_service.dart' as vm_service; import '../../src/common.dart'; import '../../src/context.dart'; +import '../../src/fake_vm_services.dart'; import '../../src/fakes.dart'; import '../../src/mocks.dart'; +import '../../src/test_flutter_command_runner.dart'; final vm_service.Isolate fakeUnpausedIsolate = vm_service.Isolate( id: '1', diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_fuchsia_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_fuchsia_test.dart index 8c9af2d5058..cd934bcb00b 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/build_fuchsia_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/build_fuchsia_test.dart @@ -22,6 +22,7 @@ import 'package:test/fake.dart'; import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/fakes.dart'; +import '../../src/test_flutter_command_runner.dart'; // Defined globally for fakes to use. FileSystem fileSystem; diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_ios_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_ios_test.dart index 0bc22fd3c4c..1f613b44ac7 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/build_ios_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/build_ios_test.dart @@ -15,6 +15,7 @@ import 'package:process/process.dart'; import '../../src/common.dart'; import '../../src/context.dart'; +import '../../src/test_flutter_command_runner.dart'; import '../../src/testbed.dart'; class FakeXcodeProjectInterpreterWithBuildSettings extends FakeXcodeProjectInterpreter { diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_ipa_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_ipa_test.dart index 90706fd7ec6..7168bb7eeb3 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/build_ipa_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/build_ipa_test.dart @@ -15,6 +15,7 @@ import 'package:flutter_tools/src/reporting/reporting.dart'; import '../../src/common.dart'; import '../../src/context.dart'; +import '../../src/test_flutter_command_runner.dart'; class FakeXcodeProjectInterpreterWithBuildSettings extends FakeXcodeProjectInterpreter { @override diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_linux_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_linux_test.dart index 39c7b5c1e81..c19dbc866c3 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/build_linux_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/build_linux_test.dart @@ -23,6 +23,7 @@ import 'package:test/fake.dart'; import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/fakes.dart'; +import '../../src/test_flutter_command_runner.dart'; const String _kTestFlutterRoot = '/flutter'; diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_macos_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_macos_test.dart index 7e9b9c962d1..3a1767973e2 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/build_macos_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/build_macos_test.dart @@ -24,6 +24,7 @@ import 'package:flutter_tools/src/reporting/reporting.dart'; import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/fakes.dart'; +import '../../src/test_flutter_command_runner.dart'; class FakeXcodeProjectInterpreterWithProfile extends FakeXcodeProjectInterpreter { @override diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_test.dart index 92168505a32..6a20cf7c7ef 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/build_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/build_test.dart @@ -10,6 +10,7 @@ import 'package:flutter_tools/src/runner/flutter_command.dart'; import '../../src/common.dart'; import '../../src/context.dart'; +import '../../src/test_flutter_command_runner.dart'; void main() { testUsingContext('obfuscate requires split-debug-info', () { diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart index 68a6fefb669..830eb5e2861 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart @@ -22,6 +22,7 @@ import 'package:flutter_tools/src/web/compile.dart'; import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/fakes.dart'; +import '../../src/test_flutter_command_runner.dart'; void main() { FileSystem fileSystem; diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_windows_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_windows_test.dart index 6d7ad3bf5b3..34aa3be994f 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/build_windows_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/build_windows_test.dart @@ -20,6 +20,7 @@ import 'package:process/process.dart'; import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/fakes.dart'; +import '../../src/test_flutter_command_runner.dart'; import '../../src/testbed.dart'; const String flutterRoot = r'C:\flutter'; diff --git a/packages/flutter_tools/test/commands.shard/hermetic/config_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/config_test.dart index 78ba006c59c..7df74986047 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/config_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/config_test.dart @@ -20,6 +20,7 @@ import 'package:test/fake.dart'; import '../../src/common.dart'; import '../../src/context.dart'; +import '../../src/test_flutter_command_runner.dart'; void main() { FakeAndroidStudio fakeAndroidStudio; diff --git a/packages/flutter_tools/test/commands.shard/hermetic/create_usage_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/create_usage_test.dart index e77f0e561e2..962782655e7 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/create_usage_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/create_usage_test.dart @@ -16,6 +16,7 @@ import 'package:flutter_tools/src/globals_null_migrated.dart' as globals; import '../../src/common.dart'; import '../../src/context.dart'; +import '../../src/test_flutter_command_runner.dart'; import '../../src/testbed.dart'; void main() { diff --git a/packages/flutter_tools/test/commands.shard/hermetic/daemon_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/daemon_test.dart index e1f4ee06f4f..6cb2609c753 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/daemon_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/daemon_test.dart @@ -24,6 +24,21 @@ import '../../src/context.dart'; import '../../src/fakes.dart'; import '../../src/mocks.dart'; +/// Runs a callback using FakeAsync.run while continually pumping the +/// microtask queue. This avoids a deadlock when tests `await` a Future +/// which queues a microtask that will not be processed unless the queue +/// is flushed. +Future _runFakeAsync(Future Function(FakeAsync time) f) async { + return FakeAsync().run((FakeAsync time) async { + bool pump = true; + final Future future = f(time).whenComplete(() => pump = false); + while (pump) { + time.flushMicrotasks(); + } + return future; + }); +} + void main() { Daemon daemon; NotifyingLogger notifyingLogger; @@ -426,7 +441,7 @@ void main() { testWithoutContext( 'debounces/merges same operation type and returns same result', () async { - await runFakeAsync((FakeAsync time) async { + await _runFakeAsync((FakeAsync time) async { final List> operations = >[ queue.queueAndDebounce('OP1', debounceDuration, () async => 1), queue.queueAndDebounce('OP1', debounceDuration, () async => 2), @@ -441,7 +456,7 @@ void main() { testWithoutContext('does not merge results outside of the debounce duration', () async { - await runFakeAsync((FakeAsync time) async { + await _runFakeAsync((FakeAsync time) async { final List> operations = >[ queue.queueAndDebounce('OP1', debounceDuration, () async => 1), Future.delayed(debounceDuration * 2).then((_) => @@ -457,7 +472,7 @@ void main() { testWithoutContext('does not merge results of different operations', () async { - await runFakeAsync((FakeAsync time) async { + await _runFakeAsync((FakeAsync time) async { final List> operations = >[ queue.queueAndDebounce('OP1', debounceDuration, () async => 1), queue.queueAndDebounce('OP2', debounceDuration, () async => 2), @@ -484,7 +499,7 @@ void main() { return ret; } - await runFakeAsync((FakeAsync time) async { + await _runFakeAsync((FakeAsync time) async { final List> operations = >[ queue.queueAndDebounce('OP1', debounceDuration, () => f(1)), queue.queueAndDebounce('OP2', debounceDuration, () => f(2)), diff --git a/packages/flutter_tools/test/commands.shard/hermetic/devices_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/devices_test.dart index 9b89a4ac104..95cc7014b34 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/devices_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/devices_test.dart @@ -16,6 +16,7 @@ import 'package:flutter_tools/src/globals.dart' as globals; import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/fake_devices.dart'; +import '../../src/test_flutter_command_runner.dart'; void main() { group('devices', () { diff --git a/packages/flutter_tools/test/commands.shard/hermetic/doctor_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/doctor_test.dart index 84c02051f73..3c7e4a67c5e 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/doctor_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/doctor_test.dart @@ -35,6 +35,7 @@ import 'package:fake_async/fake_async.dart'; import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/fakes.dart'; +import '../../src/test_flutter_command_runner.dart'; FakePlatform _kNoColorOutputPlatform() => FakePlatform( localeName: 'en_US.UTF-8', diff --git a/packages/flutter_tools/test/commands.shard/hermetic/downgrade_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/downgrade_test.dart index 5c710587414..4b69122b404 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/downgrade_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/downgrade_test.dart @@ -17,6 +17,7 @@ import 'package:mockito/mockito.dart'; import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/fakes.dart'; +import '../../src/test_flutter_command_runner.dart'; void main() { FileSystem fileSystem; diff --git a/packages/flutter_tools/test/commands.shard/hermetic/drive_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/drive_test.dart index 73ab79e0c9c..092ef2496ed 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/drive_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/drive_test.dart @@ -17,6 +17,7 @@ import 'package:test/fake.dart'; import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/fakes.dart'; +import '../../src/test_flutter_command_runner.dart'; void main() { FileSystem fileSystem; diff --git a/packages/flutter_tools/test/commands.shard/hermetic/generate_localizations_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/generate_localizations_test.dart index 2a250328e55..94fe77cbc41 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/generate_localizations_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/generate_localizations_test.dart @@ -14,6 +14,7 @@ import 'package:flutter_tools/src/commands/generate_localizations.dart'; import '../../integration.shard/test_data/basic_project.dart'; import '../../src/common.dart'; import '../../src/context.dart'; +import '../../src/test_flutter_command_runner.dart'; void main() { testUsingContext('default l10n settings', () async { diff --git a/packages/flutter_tools/test/commands.shard/hermetic/ide_config_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/ide_config_test.dart index 289fb6a76af..721a509b81f 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/ide_config_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/ide_config_test.dart @@ -13,6 +13,7 @@ import 'package:flutter_tools/src/globals_null_migrated.dart' as globals; import '../../src/common.dart'; import '../../src/context.dart'; +import '../../src/test_flutter_command_runner.dart'; void main() { group('ide_config', () { diff --git a/packages/flutter_tools/test/commands.shard/hermetic/install_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/install_test.dart index e2121b68b1a..6c848c3818a 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/install_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/install_test.dart @@ -17,6 +17,7 @@ import 'package:mockito/mockito.dart'; import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/mocks.dart'; +import '../../src/test_flutter_command_runner.dart'; void main() { group('install', () { diff --git a/packages/flutter_tools/test/commands.shard/hermetic/precache_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/precache_test.dart index bf9101c614a..835689c5dd6 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/precache_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/precache_test.dart @@ -13,6 +13,7 @@ import 'package:mockito/mockito.dart'; import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/fakes.dart'; +import '../../src/test_flutter_command_runner.dart'; void main() { MockCache cache; diff --git a/packages/flutter_tools/test/commands.shard/hermetic/pub_get_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/pub_get_test.dart index b73360ed492..30063e492a6 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/pub_get_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/pub_get_test.dart @@ -16,6 +16,7 @@ import 'package:test/fake.dart'; import '../../src/common.dart'; import '../../src/context.dart'; +import '../../src/test_flutter_command_runner.dart'; void main() { FileSystem fileSystem; diff --git a/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart index 81029893037..1a81e0073bb 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart @@ -32,6 +32,7 @@ import 'package:vm_service/vm_service.dart'; import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/fakes.dart'; +import '../../src/test_flutter_command_runner.dart'; void main() { group('run', () { diff --git a/packages/flutter_tools/test/commands.shard/hermetic/shell_completion_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/shell_completion_test.dart index f98ba6ae608..5aadc2bd4be 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/shell_completion_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/shell_completion_test.dart @@ -15,6 +15,7 @@ import 'package:flutter_tools/src/globals_null_migrated.dart' as globals; import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/fakes.dart'; +import '../../src/test_flutter_command_runner.dart'; void main() { group('shell_completion', () { diff --git a/packages/flutter_tools/test/commands.shard/hermetic/symbolize_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/symbolize_test.dart index 6422c280cbb..493f046fa0a 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/symbolize_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/symbolize_test.dart @@ -21,6 +21,7 @@ import 'package:test/fake.dart'; import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/fakes.dart'; +import '../../src/test_flutter_command_runner.dart'; void main() { MemoryFileSystem fileSystem; 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 d4ff0aa7b2e..13acca790c7 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/test_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/test_test.dart @@ -25,6 +25,7 @@ import 'package:process/process.dart'; import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/fake_devices.dart'; +import '../../src/test_flutter_command_runner.dart'; import '../../src/testbed.dart'; const String _pubspecContents = ''' diff --git a/packages/flutter_tools/test/commands.shard/permeable/analyze_once_test.dart b/packages/flutter_tools/test/commands.shard/permeable/analyze_once_test.dart index 8255af42db1..f0acfdfaec0 100644 --- a/packages/flutter_tools/test/commands.shard/permeable/analyze_once_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/analyze_once_test.dart @@ -23,6 +23,7 @@ import 'package:process/process.dart'; import '../../src/common.dart'; import '../../src/context.dart'; +import '../../src/test_flutter_command_runner.dart'; final Platform _kNoColorTerminalPlatform = FakePlatform(stdoutSupportsAnsi: false); diff --git a/packages/flutter_tools/test/commands.shard/permeable/build_aar_test.dart b/packages/flutter_tools/test/commands.shard/permeable/build_aar_test.dart index bdcba7d5a72..da136cb8e27 100644 --- a/packages/flutter_tools/test/commands.shard/permeable/build_aar_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/build_aar_test.dart @@ -20,6 +20,7 @@ import 'package:test/fake.dart'; import '../../src/android_common.dart'; import '../../src/common.dart'; import '../../src/context.dart'; +import '../../src/test_flutter_command_runner.dart'; void main() { Cache.disableLocking(); diff --git a/packages/flutter_tools/test/commands.shard/permeable/build_apk_test.dart b/packages/flutter_tools/test/commands.shard/permeable/build_apk_test.dart index fd513c5efbc..252034d6389 100644 --- a/packages/flutter_tools/test/commands.shard/permeable/build_apk_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/build_apk_test.dart @@ -23,6 +23,7 @@ import '../../src/android_common.dart'; import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/mocks.dart' hide MockAndroidSdk; +import '../../src/test_flutter_command_runner.dart'; void main() { Cache.disableLocking(); diff --git a/packages/flutter_tools/test/commands.shard/permeable/build_appbundle_test.dart b/packages/flutter_tools/test/commands.shard/permeable/build_appbundle_test.dart index bfc43b4ca5b..427e6834b40 100644 --- a/packages/flutter_tools/test/commands.shard/permeable/build_appbundle_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/build_appbundle_test.dart @@ -18,6 +18,7 @@ import 'package:test/fake.dart'; import '../../src/android_common.dart'; import '../../src/common.dart'; import '../../src/context.dart'; +import '../../src/test_flutter_command_runner.dart'; void main() { Cache.disableLocking(); diff --git a/packages/flutter_tools/test/commands.shard/permeable/build_bundle_test.dart b/packages/flutter_tools/test/commands.shard/permeable/build_bundle_test.dart index 8d5f7897771..afb1278a880 100644 --- a/packages/flutter_tools/test/commands.shard/permeable/build_bundle_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/build_bundle_test.dart @@ -21,6 +21,7 @@ import 'package:flutter_tools/src/globals_null_migrated.dart' as globals; import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/fakes.dart'; +import '../../src/test_flutter_command_runner.dart'; void main() { Cache.disableLocking(); diff --git a/packages/flutter_tools/test/commands.shard/permeable/create_test.dart b/packages/flutter_tools/test/commands.shard/permeable/create_test.dart index 3ea586ea923..6b364f06f21 100755 --- a/packages/flutter_tools/test/commands.shard/permeable/create_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/create_test.dart @@ -32,6 +32,7 @@ import '../../src/context.dart'; import '../../src/fake_http_client.dart'; import '../../src/fakes.dart'; import '../../src/pubspec_schema.dart'; +import '../../src/test_flutter_command_runner.dart'; const String _kNoPlatformsMessage = 'You\'ve created a plugin project that doesn\'t yet support any platforms.\n'; const String frameworkRevision = '12345678'; diff --git a/packages/flutter_tools/test/commands.shard/permeable/devices_test.dart b/packages/flutter_tools/test/commands.shard/permeable/devices_test.dart index 4889eda2ea1..e902c5c6488 100644 --- a/packages/flutter_tools/test/commands.shard/permeable/devices_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/devices_test.dart @@ -21,6 +21,7 @@ import 'package:mockito/mockito.dart'; import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/fakes.dart'; +import '../../src/test_flutter_command_runner.dart'; void main() { setUpAll(() { diff --git a/packages/flutter_tools/test/commands.shard/permeable/format_test.dart b/packages/flutter_tools/test/commands.shard/permeable/format_test.dart index 7e6caf8be19..648849fd894 100644 --- a/packages/flutter_tools/test/commands.shard/permeable/format_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/format_test.dart @@ -12,6 +12,7 @@ import 'package:flutter_tools/src/globals_null_migrated.dart' as globals; import '../../src/common.dart'; import '../../src/context.dart'; +import '../../src/test_flutter_command_runner.dart'; void main() { group('format', () { diff --git a/packages/flutter_tools/test/commands.shard/permeable/packages_test.dart b/packages/flutter_tools/test/commands.shard/permeable/packages_test.dart index 0b6fde03280..cefb32a0b49 100644 --- a/packages/flutter_tools/test/commands.shard/permeable/packages_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/packages_test.dart @@ -24,6 +24,7 @@ import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/fake_process_manager.dart'; import '../../src/fakes.dart'; +import '../../src/test_flutter_command_runner.dart'; void main() { Cache.disableLocking(); diff --git a/packages/flutter_tools/test/commands.shard/permeable/upgrade_test.dart b/packages/flutter_tools/test/commands.shard/permeable/upgrade_test.dart index 8e747b1e077..b6c05159ee5 100644 --- a/packages/flutter_tools/test/commands.shard/permeable/upgrade_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/upgrade_test.dart @@ -19,6 +19,7 @@ import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/fake_process_manager.dart'; import '../../src/fakes.dart'; +import '../../src/test_flutter_command_runner.dart'; void main() { group('UpgradeCommandRunner', () { diff --git a/packages/flutter_tools/test/general.shard/analytics_test.dart b/packages/flutter_tools/test/general.shard/analytics_test.dart index fbbafac2f1c..5923bc1c562 100644 --- a/packages/flutter_tools/test/general.shard/analytics_test.dart +++ b/packages/flutter_tools/test/general.shard/analytics_test.dart @@ -27,6 +27,7 @@ import 'package:usage/usage_io.dart'; import '../src/common.dart'; import '../src/context.dart'; import '../src/fakes.dart'; +import '../src/test_flutter_command_runner.dart'; void main() { setUpAll(() { diff --git a/packages/flutter_tools/test/general.shard/base/logs_test.dart b/packages/flutter_tools/test/general.shard/base/logs_test.dart index 21426b37ed9..2eef269d2a9 100644 --- a/packages/flutter_tools/test/general.shard/base/logs_test.dart +++ b/packages/flutter_tools/test/general.shard/base/logs_test.dart @@ -10,6 +10,7 @@ import 'package:flutter_tools/src/commands/logs.dart'; import '../../src/common.dart'; import '../../src/context.dart'; +import '../../src/test_flutter_command_runner.dart'; void main() { group('logs', () { diff --git a/packages/flutter_tools/test/general.shard/channel_test.dart b/packages/flutter_tools/test/general.shard/channel_test.dart index 6250d460ece..0ce91666488 100644 --- a/packages/flutter_tools/test/general.shard/channel_test.dart +++ b/packages/flutter_tools/test/general.shard/channel_test.dart @@ -14,6 +14,7 @@ import 'package:flutter_tools/src/version.dart'; import '../src/common.dart'; import '../src/context.dart'; +import '../src/test_flutter_command_runner.dart'; void main() { group('channel', () { diff --git a/packages/flutter_tools/test/general.shard/coverage_collector_test.dart b/packages/flutter_tools/test/general.shard/coverage_collector_test.dart index 6ae6a71513d..441a6871390 100644 --- a/packages/flutter_tools/test/general.shard/coverage_collector_test.dart +++ b/packages/flutter_tools/test/general.shard/coverage_collector_test.dart @@ -8,6 +8,7 @@ import 'package:flutter_tools/src/test/coverage_collector.dart'; import 'package:vm_service/vm_service.dart' as vm_service; import '../src/common.dart'; +import '../src/fake_vm_services.dart'; void main() { testWithoutContext('Coverage collector Can handle coverage SentinelException', () async { diff --git a/packages/flutter_tools/test/general.shard/devfs_test.dart b/packages/flutter_tools/test/general.shard/devfs_test.dart index c20683b7a49..8a96296beff 100644 --- a/packages/flutter_tools/test/general.shard/devfs_test.dart +++ b/packages/flutter_tools/test/general.shard/devfs_test.dart @@ -23,6 +23,7 @@ import 'package:package_config/package_config.dart'; import '../src/common.dart'; import '../src/context.dart'; import '../src/fake_http_client.dart'; +import '../src/fake_vm_services.dart'; final FakeVmServiceRequest createDevFSRequest = FakeVmServiceRequest( method: '_createDevFS', diff --git a/packages/flutter_tools/test/general.shard/drive/drive_service_test.dart b/packages/flutter_tools/test/general.shard/drive/drive_service_test.dart index 8bc70decf97..4bc658eb459 100644 --- a/packages/flutter_tools/test/general.shard/drive/drive_service_test.dart +++ b/packages/flutter_tools/test/general.shard/drive/drive_service_test.dart @@ -23,6 +23,7 @@ import 'package:vm_service/vm_service.dart' as vm_service; import '../../src/common.dart'; import '../../src/context.dart'; +import '../../src/fake_vm_services.dart'; import '../../src/fakes.dart'; diff --git a/packages/flutter_tools/test/general.shard/flutter_validator_test.dart b/packages/flutter_tools/test/general.shard/flutter_validator_test.dart index ce5e34a3536..070508aea5e 100644 --- a/packages/flutter_tools/test/general.shard/flutter_validator_test.dart +++ b/packages/flutter_tools/test/general.shard/flutter_validator_test.dart @@ -18,6 +18,18 @@ import '../src/common.dart'; import '../src/context.dart'; import '../src/fakes.dart'; +/// Matches a doctor validation result. +Matcher _matchDoctorValidation({ + ValidationType validationType, + String statusInfo, + dynamic messages +}) { + return const TypeMatcher() + .having((ValidationResult result) => result.type, 'type', validationType) + .having((ValidationResult result) => result.statusInfo, 'statusInfo', statusInfo) + .having((ValidationResult result) => result.messages, 'messages', messages); +} + void main() { testWithoutContext('FlutterValidator shows an error message if gen_snapshot is ' 'downloaded and exits with code 1', () async { @@ -48,7 +60,7 @@ void main() { fileSystem.file(artifacts.getArtifactPath(Artifact.genSnapshot)).createSync(recursive: true); - expect(await flutterValidator.validate(), matchDoctorValidation( + expect(await flutterValidator.validate(), _matchDoctorValidation( validationType: ValidationType.partial, statusInfo: 'Channel unknown, 1.0.0, on Linux, locale en_US.UTF-8', messages: containsAll(const [ @@ -84,7 +96,7 @@ void main() { // gen_snapshot is downloaded on demand, and the doctor should not // fail if the gen_snapshot binary is not present. - expect(await flutterValidator.validate(), matchDoctorValidation( + expect(await flutterValidator.validate(), _matchDoctorValidation( validationType: ValidationType.installed, statusInfo: 'Channel unknown, 1.0.0, on Windows, locale en_US.UTF-8', messages: anything, @@ -103,7 +115,7 @@ void main() { flutterRoot: () => 'sdk/flutter', ); - expect(await flutterValidator.validate(), matchDoctorValidation( + expect(await flutterValidator.validate(), _matchDoctorValidation( validationType: ValidationType.partial, statusInfo: 'Channel unknown, 0.0.0, on Windows, locale en_US.UTF-8', messages: const [ @@ -138,7 +150,7 @@ void main() { flutterRoot: () => 'sdk/flutter' ); - expect(await flutterValidator.validate(), matchDoctorValidation( + expect(await flutterValidator.validate(), _matchDoctorValidation( validationType: ValidationType.installed, statusInfo: 'Channel unknown, 1.0.0, on Windows, locale en_US.UTF-8', messages: containsAll(const [ diff --git a/packages/flutter_tools/test/general.shard/fuchsia/fuchsia_device_test.dart b/packages/flutter_tools/test/general.shard/fuchsia/fuchsia_device_test.dart index 7616831628e..980bd5a5a9e 100644 --- a/packages/flutter_tools/test/general.shard/fuchsia/fuchsia_device_test.dart +++ b/packages/flutter_tools/test/general.shard/fuchsia/fuchsia_device_test.dart @@ -41,6 +41,7 @@ import 'package:vm_service/vm_service.dart' as vm_service; import '../../src/common.dart'; import '../../src/context.dart'; +import '../../src/fake_vm_services.dart'; final vm_service.Isolate fakeIsolate = vm_service.Isolate( id: '1', diff --git a/packages/flutter_tools/test/general.shard/hot_test.dart b/packages/flutter_tools/test/general.shard/hot_test.dart index 230a9d82c79..541a19387a9 100644 --- a/packages/flutter_tools/test/general.shard/hot_test.dart +++ b/packages/flutter_tools/test/general.shard/hot_test.dart @@ -23,6 +23,7 @@ import 'package:mockito/mockito.dart'; import '../src/common.dart'; import '../src/context.dart'; +import '../src/fake_vm_services.dart'; final vm_service.Isolate fakeUnpausedIsolate = vm_service.Isolate( id: '1', diff --git a/packages/flutter_tools/test/general.shard/integration_test_device_test.dart b/packages/flutter_tools/test/general.shard/integration_test_device_test.dart index 898e38d5b8b..7c393074d74 100644 --- a/packages/flutter_tools/test/general.shard/integration_test_device_test.dart +++ b/packages/flutter_tools/test/general.shard/integration_test_device_test.dart @@ -16,6 +16,7 @@ import 'package:vm_service/vm_service.dart' as vm_service; import '../src/common.dart'; import '../src/context.dart'; import '../src/fake_devices.dart'; +import '../src/fake_vm_services.dart'; final Map vm = { 'isolates': [ diff --git a/packages/flutter_tools/test/general.shard/ios/ios_device_logger_test.dart b/packages/flutter_tools/test/general.shard/ios/ios_device_logger_test.dart index 0ed6a0269c9..8851843222e 100644 --- a/packages/flutter_tools/test/general.shard/ios/ios_device_logger_test.dart +++ b/packages/flutter_tools/test/general.shard/ios/ios_device_logger_test.dart @@ -21,6 +21,7 @@ import 'package:vm_service/vm_service.dart'; import '../../src/common.dart'; import '../../src/context.dart'; +import '../../src/fake_vm_services.dart'; void main() { FakeProcessManager processManager; diff --git a/packages/flutter_tools/test/general.shard/resident_devtools_handler_test.dart b/packages/flutter_tools/test/general.shard/resident_devtools_handler_test.dart index 19ed7b37ad5..e090f0d6de6 100644 --- a/packages/flutter_tools/test/general.shard/resident_devtools_handler_test.dart +++ b/packages/flutter_tools/test/general.shard/resident_devtools_handler_test.dart @@ -17,6 +17,7 @@ import 'package:test/fake.dart'; import '../src/common.dart'; import '../src/context.dart'; +import '../src/fake_vm_services.dart'; final vm_service.Isolate isolate = vm_service.Isolate( id: '1', diff --git a/packages/flutter_tools/test/general.shard/resident_runner_test.dart b/packages/flutter_tools/test/general.shard/resident_runner_test.dart index 50be1f68850..64aaf80b0eb 100644 --- a/packages/flutter_tools/test/general.shard/resident_runner_test.dart +++ b/packages/flutter_tools/test/general.shard/resident_runner_test.dart @@ -40,6 +40,7 @@ import 'package:mockito/mockito.dart'; import '../src/common.dart'; import '../src/context.dart'; +import '../src/fake_vm_services.dart'; import '../src/fakes.dart'; import '../src/testbed.dart'; diff --git a/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart b/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart index 1bb58178d66..fbe5f5d0c00 100644 --- a/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart +++ b/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart @@ -34,6 +34,7 @@ import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart'; import '../src/common.dart'; import '../src/context.dart'; +import '../src/fake_vm_services.dart'; import '../src/fakes.dart'; const List kAttachLogExpectations = [ diff --git a/packages/flutter_tools/test/general.shard/runner/flutter_command_runner_test.dart b/packages/flutter_tools/test/general.shard/runner/flutter_command_runner_test.dart index 155b8934ad2..7835cd0d370 100644 --- a/packages/flutter_tools/test/general.shard/runner/flutter_command_runner_test.dart +++ b/packages/flutter_tools/test/general.shard/runner/flutter_command_runner_test.dart @@ -20,6 +20,7 @@ import 'package:flutter_tools/src/version.dart'; import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/fakes.dart'; +import '../../src/test_flutter_command_runner.dart'; import 'utils.dart'; const String _kFlutterRoot = '/flutter/flutter'; diff --git a/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart b/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart index cd36bbecff4..6bf75c90fe2 100644 --- a/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart +++ b/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart @@ -25,6 +25,7 @@ import 'package:mockito/mockito.dart'; import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/fakes.dart'; +import '../../src/test_flutter_command_runner.dart'; import 'utils.dart'; void main() { diff --git a/packages/flutter_tools/test/general.shard/tracing_test.dart b/packages/flutter_tools/test/general.shard/tracing_test.dart index a4d7857eb62..fa0804fee57 100644 --- a/packages/flutter_tools/test/general.shard/tracing_test.dart +++ b/packages/flutter_tools/test/general.shard/tracing_test.dart @@ -14,6 +14,7 @@ import 'package:flutter_tools/src/vmservice.dart'; import 'package:vm_service/vm_service.dart' as vm_service; import '../src/common.dart'; +import '../src/fake_vm_services.dart'; final vm_service.Isolate fakeUnpausedIsolate = vm_service.Isolate( id: '1', diff --git a/packages/flutter_tools/test/general.shard/vmservice_test.dart b/packages/flutter_tools/test/general.shard/vmservice_test.dart index 63a958954e1..a6d3733f0fe 100644 --- a/packages/flutter_tools/test/general.shard/vmservice_test.dart +++ b/packages/flutter_tools/test/general.shard/vmservice_test.dart @@ -19,6 +19,7 @@ import 'package:fake_async/fake_async.dart'; import '../src/common.dart'; import '../src/context.dart' hide testLogger; +import '../src/fake_vm_services.dart'; final Map vm = { 'type': 'VM', diff --git a/packages/flutter_tools/test/integration.shard/generated_plugin_registrant_test.dart b/packages/flutter_tools/test/integration.shard/generated_plugin_registrant_test.dart index 67ca031128f..7fc9e2c35b9 100644 --- a/packages/flutter_tools/test/integration.shard/generated_plugin_registrant_test.dart +++ b/packages/flutter_tools/test/integration.shard/generated_plugin_registrant_test.dart @@ -18,6 +18,7 @@ import 'package:flutter_tools/src/globals.dart' as globals; import '../src/common.dart'; import '../src/context.dart'; +import '../src/test_flutter_command_runner.dart'; void main() { Directory tempDir; diff --git a/packages/flutter_tools/test/src/common.dart b/packages/flutter_tools/test/src/common.dart index f17a06cd79e..46bf41f06be 100644 --- a/packages/flutter_tools/test/src/common.dart +++ b/packages/flutter_tools/test/src/common.dart @@ -2,40 +2,22 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.8 - import 'dart:async'; -import 'package:args/args.dart'; -import 'package:args/command_runner.dart'; -import 'package:fake_async/fake_async.dart'; import 'package:file/memory.dart'; import 'package:flutter_tools/src/base/common.dart'; import 'package:flutter_tools/src/base/context.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/io.dart'; -import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/platform.dart'; -import 'package:flutter_tools/src/base/user_messages.dart'; -import 'package:flutter_tools/src/cache.dart'; -import 'package:flutter_tools/src/commands/create.dart'; -import 'package:flutter_tools/src/convert.dart'; -import 'package:flutter_tools/src/doctor_validator.dart'; import 'package:flutter_tools/src/globals_null_migrated.dart' as globals; -import 'package:flutter_tools/src/runner/flutter_command.dart'; -import 'package:flutter_tools/src/runner/flutter_command_runner.dart'; -import 'package:flutter_tools/src/vmservice.dart'; import 'package:meta/meta.dart'; import 'package:path/path.dart' as path; // flutter_ignore: package_path_import import 'package:test_api/test_api.dart' as test_package show test; // ignore: deprecated_member_use import 'package:test_api/test_api.dart' hide test; // ignore: deprecated_member_use -import 'package:vm_service/vm_service.dart' as vm_service; export 'package:test_api/test_api.dart' hide test, isInstanceOf; // ignore: deprecated_member_use -/// A matcher that compares the type of the actual value to the type argument T. -TypeMatcher isInstanceOf() => isA(); - void tryToDelete(Directory directory) { // This should not be necessary, but it turns out that // on Windows it's common for deletions to fail due to @@ -57,7 +39,7 @@ void tryToDelete(Directory directory) { String getFlutterRoot() { const Platform platform = LocalPlatform(); if (platform.environment.containsKey('FLUTTER_ROOT')) { - return platform.environment['FLUTTER_ROOT']; + return platform.environment['FLUTTER_ROOT']!; } Error invalidScript() => StateError('Could not determine flutter_tools/ path from script URL (${globals.platform.script}); consider setting FLUTTER_ROOT explicitly.'); @@ -69,11 +51,11 @@ String getFlutterRoot() { break; case 'data': final RegExp flutterTools = RegExp(r'(file://[^"]*[/\\]flutter_tools[/\\][^"]+\.dart)', multiLine: true); - final Match match = flutterTools.firstMatch(Uri.decodeFull(platform.script.path)); + final Match? match = flutterTools.firstMatch(Uri.decodeFull(platform.script.path)); if (match == null) { throw invalidScript(); } - scriptUri = Uri.parse(match.group(1)); + scriptUri = Uri.parse(match.group(1)!); break; default: throw invalidScript(); @@ -88,23 +70,6 @@ String getFlutterRoot() { return path.normalize(path.join(toolsPath, '..', '..')); } -/// Gets the path to the root of the Android SDK from the environment variable. -String getAndroidSdkRoot() { - const Platform platform = LocalPlatform(); - if (platform.environment.containsKey('ANDROID_SDK_ROOT')) { - return platform.environment['ANDROID_SDK_ROOT']; - } - throw StateError('ANDROID_SDK_ROOT environment varible not set'); -} - -CommandRunner createTestCommandRunner([ FlutterCommand command ]) { - final FlutterCommandRunner runner = TestFlutterCommandRunner(); - if (command != null) { - runner.addCommand(command); - } - return runner; -} - /// Capture console print events into a string buffer. Future capturedConsolePrint(Future Function() body) async { final StringBuffer buffer = StringBuffer(); @@ -121,8 +86,8 @@ Future capturedConsolePrint(Future Function() body) async { final Matcher throwsAssertionError = throwsA(isA()); /// Matcher for functions that throw [ToolExit]. -Matcher throwsToolExit({ int exitCode, Pattern message }) { - Matcher matcher = isToolExit; +Matcher throwsToolExit({ int? exitCode, Pattern? message }) { + Matcher matcher = _isToolExit; if (exitCode != null) { matcher = allOf(matcher, (ToolExit e) => e.exitCode == exitCode); } @@ -133,33 +98,19 @@ Matcher throwsToolExit({ int exitCode, Pattern message }) { } /// Matcher for [ToolExit]s. -final TypeMatcher isToolExit = isA(); +final TypeMatcher _isToolExit = isA(); /// Matcher for functions that throw [ProcessException]. -Matcher throwsProcessException({ Pattern message }) { - Matcher matcher = isProcessException; +Matcher throwsProcessException({ Pattern? message }) { + Matcher matcher = _isProcessException; if (message != null) { - matcher = allOf(matcher, (ProcessException e) => e.message?.contains(message)); + matcher = allOf(matcher, (ProcessException e) => e.message.contains(message)); } return throwsA(matcher); } /// Matcher for [ProcessException]s. -final TypeMatcher isProcessException = isA(); - -/// Creates a flutter project in the [temp] directory using the -/// [arguments] list if specified, or `--no-pub` if not. -/// Returns the path to the flutter project. -Future createProject(Directory temp, { List arguments }) async { - arguments ??= ['--no-pub']; - final String projectPath = globals.fs.path.join(temp.path, 'flutter_project'); - final CreateCommand command = CreateCommand(); - final CommandRunner runner = createTestCommandRunner(command); - await runner.run(['create', ...arguments, projectPath]); - // Created `.packages` since it's not created when the flag `--no-pub` is passed. - globals.fs.file(globals.fs.path.join(projectPath, '.packages')).createSync(); - return projectPath; -} +final TypeMatcher _isProcessException = isA(); Future expectToolExitLater(Future future, Matcher messageMatcher) async { try { @@ -187,12 +138,12 @@ Matcher containsIgnoringWhitespace(String toSearch) { /// `LocalFileSystem.dispose()`. @isTest void test(String description, FutureOr Function() body, { - String testOn, - Timeout timeout, + String? testOn, + Timeout? timeout, dynamic skip, - List tags, - Map onPlatform, - int retry, + List? tags, + Map? onPlatform, + int? retry, }) { test_package.test( description, @@ -221,17 +172,17 @@ void test(String description, FutureOr Function() body, { /// For more information, see https://github.com/flutter/flutter/issues/47161 @isTest void testWithoutContext(String description, FutureOr Function() body, { - String testOn, - Timeout timeout, + String? testOn, + Timeout? timeout, dynamic skip, - List tags, - Map onPlatform, - int retry, + List? tags, + Map? onPlatform, + int? retry, }) { return test( description, () async { return runZoned(body, zoneValues: { - contextKey: const NoContext(), + contextKey: const _NoContext(), }); }, timeout: timeout, @@ -243,28 +194,13 @@ void testWithoutContext(String description, FutureOr Function() body, { ); } -/// Runs a callback using FakeAsync.run while continually pumping the -/// microtask queue. This avoids a deadlock when tests `await` a Future -/// which queues a microtask that will not be processed unless the queue -/// is flushed. -Future runFakeAsync(Future Function(FakeAsync time) f) async { - return FakeAsync().run((FakeAsync time) async { - bool pump = true; - final Future future = f(time).whenComplete(() => pump = false); - while (pump) { - time.flushMicrotasks(); - } - return future; - }); -} - /// An implementation of [AppContext] that throws if context.get is called in the test. /// /// The intention of the class is to ensure we do not accidentally regress when /// moving towards more explicit dependency injection by accidentally using /// a Zone value in place of a constructor parameter. -class NoContext implements AppContext { - const NoContext(); +class _NoContext implements AppContext { + const _NoContext(); @override T get() { @@ -280,169 +216,16 @@ class NoContext implements AppContext { @override Future run({ - FutureOr Function() body, - String name, - Map overrides, - Map fallbacks, - ZoneSpecification zoneSpecification, + required FutureOr Function() body, + String? name, + Map? overrides, + Map? fallbacks, + ZoneSpecification? zoneSpecification, }) async { return body(); } } -/// A fake implementation of a vm_service that mocks the JSON-RPC request -/// and response structure. -class FakeVmServiceHost { - FakeVmServiceHost({ - @required List requests, - Uri httpAddress, - Uri wsAddress, - }) : _requests = requests { - _vmService = FlutterVmService(vm_service.VmService( - _input.stream, - _output.add, - ), httpAddress: httpAddress, wsAddress: wsAddress); - _applyStreamListen(); - _output.stream.listen((String data) { - final Map request = json.decode(data) as Map; - if (_requests.isEmpty) { - throw Exception('Unexpected request: $request'); - } - final FakeVmServiceRequest fakeRequest = _requests.removeAt(0) as FakeVmServiceRequest; - expect(request, isA>() - .having((Map request) => request['method'], 'method', fakeRequest.method) - .having((Map request) => request['params'], 'args', fakeRequest.args) - ); - if (fakeRequest.close) { - unawaited(_vmService.dispose()); - expect(_requests, isEmpty); - return; - } - if (fakeRequest.errorCode == null) { - _input.add(json.encode({ - 'jsonrpc': '2.0', - 'id': request['id'], - 'result': fakeRequest.jsonResponse ?? {'type': 'Success'}, - })); - } else { - _input.add(json.encode({ - 'jsonrpc': '2.0', - 'id': request['id'], - 'error': { - 'code': fakeRequest.errorCode, - } - })); - } - _applyStreamListen(); - }); - } - - final List _requests; - final StreamController _input = StreamController(); - final StreamController _output = StreamController(); - - FlutterVmService get vmService => _vmService; - FlutterVmService _vmService; - - - bool get hasRemainingExpectations => _requests.isNotEmpty; - - // remove FakeStreamResponse objects from _requests until it is empty - // or until we hit a FakeRequest - void _applyStreamListen() { - while (_requests.isNotEmpty && !_requests.first.isRequest) { - final FakeVmServiceStreamResponse response = _requests.removeAt(0) as FakeVmServiceStreamResponse; - _input.add(json.encode({ - 'jsonrpc': '2.0', - 'method': 'streamNotify', - 'params': { - 'streamId': response.streamId, - 'event': response.event.toJson(), - }, - })); - } - } -} - -abstract class VmServiceExpectation { - bool get isRequest; -} - -class FakeVmServiceRequest implements VmServiceExpectation { - const FakeVmServiceRequest({ - @required this.method, - this.args = const {}, - this.jsonResponse, - this.errorCode, - this.close = false, - }); - - final String method; - - /// When true, the vm service is automatically closed. - final bool close; - - /// If non-null, the error code for a [vm_service.RPCError] in place of a - /// standard response. - final int errorCode; - final Map args; - final Map jsonResponse; - - @override - bool get isRequest => true; -} - -class FakeVmServiceStreamResponse implements VmServiceExpectation { - const FakeVmServiceStreamResponse({ - @required this.event, - @required this.streamId, - }); - - final vm_service.Event event; - final String streamId; - - @override - bool get isRequest => false; -} - -class TestFlutterCommandRunner extends FlutterCommandRunner { - @override - Future runCommand(ArgResults topLevelResults) async { - final Logger topLevelLogger = globals.logger; - final Map contextOverrides = { - if (topLevelResults['verbose'] as bool) - Logger: VerboseLogger(topLevelLogger), - }; - return context.run( - overrides: contextOverrides.map((Type type, dynamic value) { - return MapEntry(type, () => value); - }), - body: () { - Cache.flutterRoot ??= Cache.defaultFlutterRoot( - platform: globals.platform, - fileSystem: globals.fs, - userMessages: UserMessages(), - ); - // For compatibility with tests that set this to a relative path. - Cache.flutterRoot = globals.fs.path.normalize(globals.fs.path.absolute(Cache.flutterRoot)); - return super.runCommand(topLevelResults); - } - ); - } -} - -/// Matches a doctor validation result. -Matcher matchDoctorValidation({ - ValidationType validationType, - String statusInfo, - dynamic messages -}) { - return const TypeMatcher() - .having((ValidationResult result) => result.type, 'type', validationType) - .having((ValidationResult result) => result.statusInfo, 'statusInfo', statusInfo) - .having((ValidationResult result) => result.messages, 'messages', messages); -} - /// Allows inserting file system exceptions into certain /// [MemoryFileSystem] operations by tagging path/op combinations. /// @@ -467,16 +250,16 @@ class FileExceptionHandler { void addError(FileSystemEntity entity, FileSystemOp operation, FileSystemException exception) { final String path = entity.path; _contextErrors[path] ??= {}; - _contextErrors[path][operation] = exception; + _contextErrors[path]![operation] = exception; } // Tear-off this method and pass it to the memory filesystem `opHandle` parameter. void opHandle(String path, FileSystemOp operation) { - final Map exceptions = _contextErrors[path]; + final Map? exceptions = _contextErrors[path]; if (exceptions == null) { return; } - final FileSystemException exception = exceptions[operation]; + final FileSystemException? exception = exceptions[operation]; if (exception == null) { return; } diff --git a/packages/flutter_tools/test/src/fake_vm_services.dart b/packages/flutter_tools/test/src/fake_vm_services.dart new file mode 100644 index 00000000000..299f3e18a18 --- /dev/null +++ b/packages/flutter_tools/test/src/fake_vm_services.dart @@ -0,0 +1,131 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// @dart = 2.8 + +import 'dart:async'; + +import 'package:flutter_tools/src/base/common.dart'; +import 'package:flutter_tools/src/convert.dart'; +import 'package:flutter_tools/src/vmservice.dart'; +import 'package:meta/meta.dart'; +import 'package:test_api/test_api.dart' hide test; // ignore: deprecated_member_use +import 'package:vm_service/vm_service.dart' as vm_service; + +export 'package:test_api/test_api.dart' hide test, isInstanceOf; // ignore: deprecated_member_use + +/// A fake implementation of a vm_service that mocks the JSON-RPC request +/// and response structure. +class FakeVmServiceHost { + FakeVmServiceHost({ + @required List requests, + Uri httpAddress, + Uri wsAddress, + }) : _requests = requests { + _vmService = FlutterVmService(vm_service.VmService( + _input.stream, + _output.add, + ), httpAddress: httpAddress, wsAddress: wsAddress); + _applyStreamListen(); + _output.stream.listen((String data) { + final Map request = json.decode(data) as Map; + if (_requests.isEmpty) { + throw Exception('Unexpected request: $request'); + } + final FakeVmServiceRequest fakeRequest = _requests.removeAt(0) as FakeVmServiceRequest; + expect(request, isA>() + .having((Map request) => request['method'], 'method', fakeRequest.method) + .having((Map request) => request['params'], 'args', fakeRequest.args) + ); + if (fakeRequest.close) { + unawaited(_vmService.dispose()); + expect(_requests, isEmpty); + return; + } + if (fakeRequest.errorCode == null) { + _input.add(json.encode({ + 'jsonrpc': '2.0', + 'id': request['id'], + 'result': fakeRequest.jsonResponse ?? {'type': 'Success'}, + })); + } else { + _input.add(json.encode({ + 'jsonrpc': '2.0', + 'id': request['id'], + 'error': { + 'code': fakeRequest.errorCode, + } + })); + } + _applyStreamListen(); + }); + } + + final List _requests; + final StreamController _input = StreamController(); + final StreamController _output = StreamController(); + + FlutterVmService get vmService => _vmService; + FlutterVmService _vmService; + + + bool get hasRemainingExpectations => _requests.isNotEmpty; + + // remove FakeStreamResponse objects from _requests until it is empty + // or until we hit a FakeRequest + void _applyStreamListen() { + while (_requests.isNotEmpty && !_requests.first.isRequest) { + final FakeVmServiceStreamResponse response = _requests.removeAt(0) as FakeVmServiceStreamResponse; + _input.add(json.encode({ + 'jsonrpc': '2.0', + 'method': 'streamNotify', + 'params': { + 'streamId': response.streamId, + 'event': response.event.toJson(), + }, + })); + } + } +} + +abstract class VmServiceExpectation { + bool get isRequest; +} + +class FakeVmServiceRequest implements VmServiceExpectation { + const FakeVmServiceRequest({ + @required this.method, + this.args = const {}, + this.jsonResponse, + this.errorCode, + this.close = false, + }); + + final String method; + + /// When true, the vm service is automatically closed. + final bool close; + + /// If non-null, the error code for a [vm_service.RPCError] in place of a + /// standard response. + final int errorCode; + final Map args; + final Map jsonResponse; + + @override + bool get isRequest => true; +} + +class FakeVmServiceStreamResponse implements VmServiceExpectation { + const FakeVmServiceStreamResponse({ + @required this.event, + @required this.streamId, + }); + + final vm_service.Event event; + final String streamId; + + @override + bool get isRequest => false; +} diff --git a/packages/flutter_tools/test/src/test_flutter_command_runner.dart b/packages/flutter_tools/test/src/test_flutter_command_runner.dart new file mode 100644 index 00000000000..3080377f3bc --- /dev/null +++ b/packages/flutter_tools/test/src/test_flutter_command_runner.dart @@ -0,0 +1,69 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// @dart = 2.8 + +import 'dart:async'; + +import 'package:args/args.dart'; +import 'package:args/command_runner.dart'; +import 'package:flutter_tools/src/base/context.dart'; +import 'package:flutter_tools/src/base/file_system.dart'; +import 'package:flutter_tools/src/base/logger.dart'; +import 'package:flutter_tools/src/base/user_messages.dart'; +import 'package:flutter_tools/src/cache.dart'; +import 'package:flutter_tools/src/commands/create.dart'; +import 'package:flutter_tools/src/globals_null_migrated.dart' as globals; +import 'package:flutter_tools/src/runner/flutter_command.dart'; +import 'package:flutter_tools/src/runner/flutter_command_runner.dart'; + +export 'package:test_api/test_api.dart' hide test, isInstanceOf; // ignore: deprecated_member_use + +CommandRunner createTestCommandRunner([ FlutterCommand command ]) { + final FlutterCommandRunner runner = TestFlutterCommandRunner(); + if (command != null) { + runner.addCommand(command); + } + return runner; +} + +/// Creates a flutter project in the [temp] directory using the +/// [arguments] list if specified, or `--no-pub` if not. +/// Returns the path to the flutter project. +Future createProject(Directory temp, { List arguments }) async { + arguments ??= ['--no-pub']; + final String projectPath = globals.fs.path.join(temp.path, 'flutter_project'); + final CreateCommand command = CreateCommand(); + final CommandRunner runner = createTestCommandRunner(command); + await runner.run(['create', ...arguments, projectPath]); + // Created `.packages` since it's not created when the flag `--no-pub` is passed. + globals.fs.file(globals.fs.path.join(projectPath, '.packages')).createSync(); + return projectPath; +} + +class TestFlutterCommandRunner extends FlutterCommandRunner { + @override + Future runCommand(ArgResults topLevelResults) async { + final Logger topLevelLogger = globals.logger; + final Map contextOverrides = { + if (topLevelResults['verbose'] as bool) + Logger: VerboseLogger(topLevelLogger), + }; + return context.run( + overrides: contextOverrides.map((Type type, dynamic value) { + return MapEntry(type, () => value); + }), + body: () { + Cache.flutterRoot ??= Cache.defaultFlutterRoot( + platform: globals.platform, + fileSystem: globals.fs, + userMessages: UserMessages(), + ); + // For compatibility with tests that set this to a relative path. + Cache.flutterRoot = globals.fs.path.normalize(globals.fs.path.absolute(Cache.flutterRoot)); + return super.runCommand(topLevelResults); + } + ); + } +}