From c09ace2724d1c0fc52753d39b71d15cb7fc4816a Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Sun, 18 Apr 2021 09:24:02 -0700 Subject: [PATCH] [flutter_tools] Remove web specific vm_service handlers, move handler tests to single location (#80440) --- .../lib/src/isolated/resident_web_runner.dart | 241 +--- .../test/general.shard/hot_test.dart | 239 ---- .../resident_web_runner_test.dart | 407 ------ .../general.shard/terminal_handler_test.dart | 1171 ++++++++++++++--- 4 files changed, 983 insertions(+), 1075 deletions(-) diff --git a/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart b/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart index 13ee47afcea..b390ecef2d2 100644 --- a/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart +++ b/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart @@ -29,7 +29,6 @@ import '../cache.dart'; import '../dart/language_version.dart'; import '../devfs.dart'; import '../device.dart'; -import '../features.dart'; import '../flutter_plugins.dart'; import '../platform_plugins.dart'; import '../plugins.dart'; @@ -60,7 +59,6 @@ class DwdsWebRunnerFactory extends WebRunnerFactory { @required FileSystem fileSystem, @required SystemClock systemClock, @required Usage usage, - @required FeatureFlags featureFlags, bool machine = false, }) { return ResidentWebRunner( @@ -120,6 +118,12 @@ class ResidentWebRunner extends ResidentRunner { final Usage _usage; final UrlTunneller _urlTunneller; + @override + Logger get logger => _logger; + + @override + FileSystem get fileSystem => _fileSystem; + FlutterDevice get device => flutterDevices.first; final FlutterProject flutterProject; DateTime firstBuildTime; @@ -162,9 +166,6 @@ class ResidentWebRunner extends ResidentRunner { } FlutterVmService _instance; - @override - bool get supportsRestart => true; - @override Future cleanupAfterSignal() async { await _cleanup(); @@ -219,242 +220,12 @@ class ResidentWebRunner extends ResidentRunner { printDebuggerList(); } - @override - Future debugDumpApp() async { - if (!supportsServiceProtocol || _vmService == null) { - return false; - } - try { - final String data = await _vmService - .flutterDebugDumpApp( - isolateId: null, - ); - _logger.printStatus(data); - } on vmservice.RPCError { - // do nothing. - } - return true; - } - - @override - Future debugDumpRenderTree() async { - if (!supportsServiceProtocol || _vmService == null) { - return false; - } - try { - final String data = await _vmService - .flutterDebugDumpRenderTree( - isolateId: null, - ); - _logger.printStatus(data); - } on vmservice.RPCError { - // do nothing. - } - return true; - } - - @override - Future debugDumpLayerTree() async { - if (!supportsServiceProtocol || _vmService == null) { - return false; - } - try { - final String data = await _vmService - .flutterDebugDumpLayerTree( - isolateId: null, - ); - _logger.printStatus(data); - } on vmservice.RPCError { - // do nothing. - } - return true; - } - - @override - Future debugDumpSemanticsTreeInTraversalOrder() async { - if (!supportsServiceProtocol) { - return false; - } - try { - await _vmService - ?.flutterDebugDumpSemanticsTreeInTraversalOrder( - isolateId: null, - ); - } on vmservice.RPCError { - // do nothing. - } - return true; - } - - @override - Future debugTogglePlatform() async { - if (!supportsServiceProtocol) { - return false; - } - try { - final String currentPlatform = await _vmService - ?.flutterPlatformOverride( - isolateId: null, - ); - final String platform = nextPlatform(currentPlatform); - await _vmService - ?.flutterPlatformOverride( - platform: platform, - isolateId: null, - ); - _logger.printStatus('Switched operating system to $platform'); - } on vmservice.RPCError { - // do nothing. - } - return true; - } - - @override - Future debugToggleBrightness() async { - if (!supportsServiceProtocol) { - return false; - } - try { - final Brightness currentBrightness = await _vmService - ?.flutterBrightnessOverride( - isolateId: null, - ); - Brightness next; - if (currentBrightness == Brightness.light) { - next = Brightness.dark; - } else if (currentBrightness == Brightness.dark) { - next = Brightness.light; - } - next = await _vmService - ?.flutterBrightnessOverride( - brightness: next, - isolateId: null, - ); - _logger.printStatus('Changed brightness to $next.'); - } on vmservice.RPCError { - // do nothing. - } - return true; - } - @override Future stopEchoingDeviceLog() async { // Do nothing for ResidentWebRunner await device.stopEchoingDeviceLog(); } - @override - Future debugDumpSemanticsTreeInInverseHitTestOrder() async { - if (!supportsServiceProtocol) { - return false; - } - try { - await _vmService - ?.flutterDebugDumpSemanticsTreeInInverseHitTestOrder( - isolateId: null, - ); - } on vmservice.RPCError { - // do nothing. - } - return true; - } - - @override - Future debugToggleDebugPaintSizeEnabled() async { - if (!supportsServiceProtocol) { - return false; - } - try { - await _vmService - ?.flutterToggleDebugPaintSizeEnabled( - isolateId: null, - ); - } on vmservice.RPCError { - // do nothing. - } - return true; - } - - @override - Future debugToggleDebugCheckElevationsEnabled() async { - if (!supportsServiceProtocol) { - return false; - } - try { - await _vmService - ?.flutterToggleDebugCheckElevationsEnabled( - isolateId: null, - ); - } on vmservice.RPCError { - // do nothing. - } - return true; - } - - @override - Future debugTogglePerformanceOverlayOverride() async { - if (!supportsServiceProtocol) { - return false; - } - try { - await _vmService - ?.flutterTogglePerformanceOverlayOverride( - isolateId: null, - ); - } on vmservice.RPCError { - // do nothing. - } - return true; - } - - @override - Future debugToggleWidgetInspector() async { - if (!supportsServiceProtocol) { - return false; - } - try { - await _vmService - ?.flutterToggleWidgetInspector( - isolateId: null, - ); - } on vmservice.RPCError { - // do nothing. - } - return true; - } - - @override - Future debugToggleInvertOversizedImages() async { - if (!supportsServiceProtocol) { - return false; - } - try { - await _vmService - ?.flutterToggleInvertOversizedImages( - isolateId: null, - ); - } on vmservice.RPCError { - // do nothing. - } - return true; - } - - @override - Future debugToggleProfileWidgetBuilds() async { - if (!supportsServiceProtocol) { - return false; - } - try { - await _vmService - ?.flutterToggleProfileWidgetBuilds( - isolateId: null, - ); - } on vmservice.RPCError { - // do nothing. - } - return true; - } - @override Future run({ Completer connectionInfoCompleter, diff --git a/packages/flutter_tools/test/general.shard/hot_test.dart b/packages/flutter_tools/test/general.shard/hot_test.dart index 541a19387a9..02110c035d4 100644 --- a/packages/flutter_tools/test/general.shard/hot_test.dart +++ b/packages/flutter_tools/test/general.shard/hot_test.dart @@ -172,175 +172,6 @@ void main() { fileSystem = MemoryFileSystem.test(); }); - testUsingContext('Does not hot restart when device does not support it', () async { - fileSystem.file('.packages') - ..createSync(recursive: true) - ..writeAsStringSync('\n'); - // Setup mocks - final MockDevice mockDevice = MockDevice(); - when(mockDevice.supportsHotReload).thenReturn(true); - when(mockDevice.supportsHotRestart).thenReturn(false); - when(mockDevice.targetPlatform).thenAnswer((Invocation _) async => TargetPlatform.tester); - // Trigger hot restart. - final List devices = [ - FlutterDevice(mockDevice, generator: residentCompiler, buildInfo: BuildInfo.debug)..devFS = mockDevFs, - ]; - final OperationResult result = await HotRunner( - devices, - debuggingOptions: DebuggingOptions.disabled(BuildInfo.debug), - target: 'main.dart', - devtoolsHandler: createNoOpHandler, - ).restart(fullRestart: true); - // Expect hot restart failed. - expect(result.isOk, false); - expect(result.message, 'hotRestart not supported'); - }, overrides: { - HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true), - Artifacts: () => Artifacts.test(), - FileSystem: () => fileSystem, - Platform: () => FakePlatform(operatingSystem: 'linux'), - ProcessManager: () => FakeProcessManager.any(), - }); - - testUsingContext('Does not hot restart when one of many devices does not support it', () async { - fileSystem.file('.packages') - ..createSync(recursive: true) - ..writeAsStringSync('\n'); - // Setup mocks - final MockDevice mockDevice = MockDevice(); - final MockDevice mockHotDevice = MockDevice(); - when(mockDevice.supportsHotReload).thenReturn(true); - when(mockDevice.supportsHotRestart).thenReturn(false); - when(mockHotDevice.supportsHotReload).thenReturn(true); - when(mockHotDevice.supportsHotRestart).thenReturn(true); - // Trigger hot restart. - final List devices = [ - FlutterDevice(mockDevice, generator: residentCompiler, buildInfo: BuildInfo.debug)..devFS = mockDevFs, - FlutterDevice(mockHotDevice, generator: residentCompiler, buildInfo: BuildInfo.debug)..devFS = mockDevFs, - ]; - final OperationResult result = await HotRunner( - devices, - debuggingOptions: DebuggingOptions.disabled(BuildInfo.debug), - target: 'main.dart', - devtoolsHandler: createNoOpHandler, - ).restart(fullRestart: true); - // Expect hot restart failed. - expect(result.isOk, false); - expect(result.message, 'hotRestart not supported'); - }, overrides: { - HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true), - Artifacts: () => Artifacts.test(), - FileSystem: () => fileSystem, - Platform: () => FakePlatform(operatingSystem: 'linux'), - ProcessManager: () => FakeProcessManager.any(), - }); - - testUsingContext('Does hot restarts when all devices support it', () async { - final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(requests: [ - listViews, - FakeVmServiceRequest( - method: 'getIsolate', - args: { - 'isolateId': fakeUnpausedIsolate.id, - }, - jsonResponse: fakeUnpausedIsolate.toJson(), - ), - FakeVmServiceRequest( - method: 'getVM', - jsonResponse: vm_service.VM.parse({}).toJson() - ), - listViews, - FakeVmServiceRequest( - method: 'getIsolate', - args: { - 'isolateId': fakeUnpausedIsolate.id, - }, - jsonResponse: fakeUnpausedIsolate.toJson(), - ), - FakeVmServiceRequest( - method: 'getVM', - jsonResponse: vm_service.VM.parse({}).toJson() - ), - listViews, - listViews, - const FakeVmServiceRequest( - method: 'streamListen', - args: { - 'streamId': 'Isolate', - } - ), - const FakeVmServiceRequest( - method: 'streamListen', - args: { - 'streamId': 'Isolate', - } - ), - FakeVmServiceStreamResponse( - streamId: 'Isolate', - event: vm_service.Event( - timestamp: 0, - kind: vm_service.EventKind.kIsolateRunnable, - ) - ), - FakeVmServiceStreamResponse( - streamId: 'Isolate', - event: vm_service.Event( - timestamp: 0, - kind: vm_service.EventKind.kIsolateRunnable, - ) - ), - FakeVmServiceRequest( - method: kRunInViewMethod, - args: { - 'viewId': fakeFlutterView.id, - 'mainScript': 'main.dart.dill', - 'assetDirectory': 'build/flutter_assets', - } - ), - FakeVmServiceRequest( - method: kRunInViewMethod, - args: { - 'viewId': fakeFlutterView.id, - 'mainScript': 'main.dart.dill', - 'assetDirectory': 'build/flutter_assets', - } - ), - ]); - // Setup mocks - final MockDevice mockDevice = MockDevice(); - final MockDevice mockHotDevice = MockDevice(); - when(mockDevice.supportsHotReload).thenReturn(true); - when(mockDevice.supportsHotRestart).thenReturn(true); - when(mockHotDevice.supportsHotReload).thenReturn(true); - when(mockHotDevice.supportsHotRestart).thenReturn(true); - // Trigger a restart. - final List devices = [ - FlutterDevice(mockDevice, generator: residentCompiler, buildInfo: BuildInfo.debug) - ..vmService = fakeVmServiceHost.vmService - ..devFS = mockDevFs, - FlutterDevice(mockHotDevice, generator: residentCompiler, buildInfo: BuildInfo.debug) - ..vmService = fakeVmServiceHost.vmService - ..devFS = mockDevFs, - ]; - final HotRunner hotRunner = HotRunner( - devices, - debuggingOptions: DebuggingOptions.disabled(BuildInfo.debug), - target: 'main.dart', - devtoolsHandler: createNoOpHandler, - ); - final OperationResult result = await hotRunner.restart(fullRestart: true); - // Expect hot restart was successful. - expect(hotRunner.uri, mockDevFs.baseUri); - expect(result.isOk, true); - expect(result.message, isNot('hotRestart not supported')); - }, overrides: { - HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true), - Artifacts: () => Artifacts.test(), - FileSystem: () => fileSystem, - Platform: () => FakePlatform(operatingSystem: 'linux'), - ProcessManager: () => FakeProcessManager.any(), - }); - testUsingContext('setup function fails', () async { fileSystem.file('.packages') ..createSync(recursive: true) @@ -368,76 +199,6 @@ void main() { ProcessManager: () => FakeProcessManager.any(), }); - testUsingContext('hot restart supported', () async { - fileSystem.file('.packages') - ..createSync(recursive: true) - ..writeAsStringSync('\n'); - // Setup mocks - final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(requests: [ - listViews, - FakeVmServiceRequest( - method: 'getIsolate', - args: { - 'isolateId': fakeUnpausedIsolate.id, - }, - jsonResponse: fakeUnpausedIsolate.toJson(), - ), - FakeVmServiceRequest( - method: 'getVM', - jsonResponse: vm_service.VM.parse({}).toJson(), - ), - listViews, - const FakeVmServiceRequest( - method: 'streamListen', - args: { - 'streamId': 'Isolate', - } - ), - FakeVmServiceRequest( - method: kRunInViewMethod, - args: { - 'viewId': fakeFlutterView.id, - 'mainScript': 'main.dart.dill', - 'assetDirectory': 'build/flutter_assets', - } - ), - FakeVmServiceStreamResponse( - streamId: 'Isolate', - event: vm_service.Event( - timestamp: 0, - kind: vm_service.EventKind.kIsolateRunnable, - ) - ), - ]); - final MockDevice mockDevice = MockDevice(); - when(mockDevice.supportsHotReload).thenReturn(true); - when(mockDevice.supportsHotRestart).thenReturn(true); - when(mockDevice.targetPlatform).thenAnswer((Invocation _) async => TargetPlatform.tester); - // Trigger hot restart. - final List devices = [ - FlutterDevice(mockDevice, generator: residentCompiler, buildInfo: BuildInfo.debug) - ..vmService = fakeVmServiceHost.vmService - ..devFS = mockDevFs, - ]; - final HotRunner hotRunner = HotRunner( - devices, - debuggingOptions: DebuggingOptions.disabled(BuildInfo.debug), - target: 'main.dart', - devtoolsHandler: createNoOpHandler, - ); - final OperationResult result = await hotRunner.restart(fullRestart: true); - // Expect hot restart successful. - expect(hotRunner.uri, mockDevFs.baseUri); - expect(result.isOk, true); - expect(result.message, isNot('setupHotRestart failed')); - }, overrides: { - HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true), - Artifacts: () => Artifacts.test(), - FileSystem: () => fileSystem, - Platform: () => FakePlatform(operatingSystem: 'linux'), - ProcessManager: () => FakeProcessManager.any(), - }); - group('shutdown hook tests', () { TestHotRunnerConfig shutdownTestingConfig; 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 f65a4a4bfc4..2df93ad2947 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 @@ -228,7 +228,6 @@ void main() { fileSystem: fileSystem, logger: BufferLogger.test(), usage: globals.flutterUsage, - systemClock: globals.systemClock, ); @@ -908,412 +907,6 @@ void main() { ProcessManager: () => processManager, }); - testUsingContext('debugDumpApp', () async { - final BufferLogger logger = BufferLogger.test(); - final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice, logger: logger); - fakeVmServiceHost = FakeVmServiceHost(requests: [ - ...kAttachExpectations, - const FakeVmServiceRequest( - method: 'ext.flutter.debugDumpApp', - args: { - 'isolateId': null, - }, - ), - ]); - _setupMocks(); - final Completer connectionInfoCompleter = Completer(); - unawaited(residentWebRunner.run( - connectionInfoCompleter: connectionInfoCompleter, - )); - await connectionInfoCompleter.future; - await residentWebRunner.debugDumpApp(); - - expect(fakeVmServiceHost.hasRemainingExpectations, false); - }, overrides: { - FileSystem: () => fileSystem, - ProcessManager: () => processManager, - }); - - testUsingContext('debugDumpLayerTree', () async { - final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice); - fakeVmServiceHost = FakeVmServiceHost(requests: [ - ...kAttachExpectations, - const FakeVmServiceRequest( - method: 'ext.flutter.debugDumpLayerTree', - args: { - 'isolateId': null, - }, - ), - ]); - _setupMocks(); - final Completer connectionInfoCompleter = Completer(); - unawaited(residentWebRunner.run( - connectionInfoCompleter: connectionInfoCompleter, - )); - await connectionInfoCompleter.future; - await residentWebRunner.debugDumpLayerTree(); - - expect(fakeVmServiceHost.hasRemainingExpectations, false); - }, overrides: { - FileSystem: () => fileSystem, - ProcessManager: () => processManager, - }); - - testUsingContext('debugDumpRenderTree', () async { - final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice); - fakeVmServiceHost = FakeVmServiceHost(requests: [ - ...kAttachExpectations, - const FakeVmServiceRequest( - method: 'ext.flutter.debugDumpRenderTree', - args: { - 'isolateId': null, - }, - ), - ]); - _setupMocks(); - final Completer connectionInfoCompleter = Completer(); - unawaited(residentWebRunner.run( - connectionInfoCompleter: connectionInfoCompleter, - )); - await connectionInfoCompleter.future; - await residentWebRunner.debugDumpRenderTree(); - - expect(fakeVmServiceHost.hasRemainingExpectations, false); - }, overrides: { - FileSystem: () => fileSystem, - ProcessManager: () => processManager, - }); - - testUsingContext('debugDumpSemanticsTreeInTraversalOrder', () async { - final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice); - fakeVmServiceHost = FakeVmServiceHost(requests: [ - ...kAttachExpectations, - const FakeVmServiceRequest( - method: 'ext.flutter.debugDumpSemanticsTreeInTraversalOrder', - args: { - 'isolateId': null, - }, - ), - ]); - _setupMocks(); - final Completer connectionInfoCompleter = Completer(); - unawaited(residentWebRunner.run( - connectionInfoCompleter: connectionInfoCompleter, - )); - await connectionInfoCompleter.future; - await residentWebRunner.debugDumpSemanticsTreeInTraversalOrder(); - - expect(fakeVmServiceHost.hasRemainingExpectations, false); - }, overrides: { - FileSystem: () => fileSystem, - ProcessManager: () => processManager, - }); - - testUsingContext('debugDumpSemanticsTreeInInverseHitTestOrder', () async { - final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice); - fakeVmServiceHost = FakeVmServiceHost(requests: [ - ...kAttachExpectations, - const FakeVmServiceRequest( - method: 'ext.flutter.debugDumpSemanticsTreeInInverseHitTestOrder', - args: { - 'isolateId': null, - }, - ), - ]); - _setupMocks(); - final Completer connectionInfoCompleter = Completer(); - unawaited(residentWebRunner.run( - connectionInfoCompleter: connectionInfoCompleter, - )); - - await connectionInfoCompleter.future; - await residentWebRunner.debugDumpSemanticsTreeInInverseHitTestOrder(); - - expect(fakeVmServiceHost.hasRemainingExpectations, false); - }, overrides: { - FileSystem: () => fileSystem, - ProcessManager: () => processManager, - }); - - testUsingContext('debugToggleDebugPaintSizeEnabled', () async { - final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice); - fakeVmServiceHost = FakeVmServiceHost(requests: [ - ...kAttachExpectations, - const FakeVmServiceRequest( - method: 'ext.flutter.debugPaint', - args: { - 'isolateId': null, - }, - jsonResponse: { - 'enabled': 'false' - }, - ), - const FakeVmServiceRequest( - method: 'ext.flutter.debugPaint', - args: { - 'isolateId': null, - 'enabled': 'true', - }, - jsonResponse: { - 'value': 'true' - }, - ) - ]); - _setupMocks(); - final Completer connectionInfoCompleter = Completer(); - unawaited(residentWebRunner.run( - connectionInfoCompleter: connectionInfoCompleter, - )); - await connectionInfoCompleter.future; - - await residentWebRunner.debugToggleDebugPaintSizeEnabled(); - - expect(fakeVmServiceHost.hasRemainingExpectations, false); - }, overrides: { - FileSystem: () => fileSystem, - ProcessManager: () => processManager, - }); - - testUsingContext('debugTogglePerformanceOverlayOverride', () async { - final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice); - fakeVmServiceHost = FakeVmServiceHost(requests: [ - ...kAttachExpectations, - const FakeVmServiceRequest( - method: 'ext.flutter.showPerformanceOverlay', - args: { - 'isolateId': null, - }, - jsonResponse: { - 'enabled': 'false' - }, - ), - const FakeVmServiceRequest( - method: 'ext.flutter.showPerformanceOverlay', - args: { - 'isolateId': null, - 'enabled': 'true', - }, - jsonResponse: { - 'enabled': 'true' - }, - ) - ]); - _setupMocks(); - final Completer connectionInfoCompleter = Completer(); - unawaited(residentWebRunner.run( - connectionInfoCompleter: connectionInfoCompleter, - )); - await connectionInfoCompleter.future; - - await residentWebRunner.debugTogglePerformanceOverlayOverride(); - - expect(fakeVmServiceHost.hasRemainingExpectations, false); - }, overrides: { - FileSystem: () => fileSystem, - ProcessManager: () => processManager, - }); - - testUsingContext('debugToggleInvertOversizedImagesOverride', () async { - final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice); - fakeVmServiceHost = FakeVmServiceHost(requests: [ - ...kAttachExpectations, - const FakeVmServiceRequest( - method: 'ext.flutter.invertOversizedImages', - args: { - 'isolateId': null, - }, - jsonResponse: { - 'enabled': 'false' - }, - ), - const FakeVmServiceRequest( - method: 'ext.flutter.invertOversizedImages', - args: { - 'isolateId': null, - 'enabled': 'true', - }, - jsonResponse: { - 'enabled': 'true' - }, - ) - ]); - _setupMocks(); - final Completer connectionInfoCompleter = Completer(); - unawaited(residentWebRunner.run( - connectionInfoCompleter: connectionInfoCompleter, - )); - await connectionInfoCompleter.future; - - await residentWebRunner.debugToggleInvertOversizedImages(); - - expect(fakeVmServiceHost.hasRemainingExpectations, false); - }, overrides: { - FileSystem: () => fileSystem, - ProcessManager: () => processManager, - }); - - testUsingContext('debugToggleWidgetInspector', () async { - final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice); - fakeVmServiceHost = FakeVmServiceHost(requests: [ - ...kAttachExpectations, - const FakeVmServiceRequest( - method: 'ext.flutter.inspector.show', - args: { - 'isolateId': null, - }, - jsonResponse: { - 'enabled': 'false' - }, - ), - const FakeVmServiceRequest( - method: 'ext.flutter.inspector.show', - args: { - 'isolateId': null, - 'enabled': 'true', - }, - jsonResponse: { - 'enabled': 'true' - }, - ) - ]); - _setupMocks(); - final Completer connectionInfoCompleter = Completer(); - unawaited(residentWebRunner.run( - connectionInfoCompleter: connectionInfoCompleter, - )); - await connectionInfoCompleter.future; - - await residentWebRunner.debugToggleWidgetInspector(); - - expect(fakeVmServiceHost.hasRemainingExpectations, false); - }, overrides: { - FileSystem: () => fileSystem, - ProcessManager: () => processManager, - }); - - testUsingContext('debugToggleProfileWidgetBuilds', () async { - final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice); - fakeVmServiceHost = FakeVmServiceHost(requests: [ - ...kAttachExpectations, - const FakeVmServiceRequest( - method: 'ext.flutter.profileWidgetBuilds', - args: { - 'isolateId': null, - }, - jsonResponse: { - 'enabled': 'false' - }, - ), - const FakeVmServiceRequest( - method: 'ext.flutter.profileWidgetBuilds', - args: { - 'isolateId': null, - 'enabled': 'true', - }, - jsonResponse: { - 'enabled': 'true' - }, - ) - ]); - _setupMocks(); - final Completer connectionInfoCompleter = Completer(); - unawaited(residentWebRunner.run( - connectionInfoCompleter: connectionInfoCompleter, - )); - await connectionInfoCompleter.future; - - await residentWebRunner.debugToggleProfileWidgetBuilds(); - - expect(fakeVmServiceHost.hasRemainingExpectations, false); - }, overrides: { - FileSystem: () => fileSystem, - ProcessManager: () => processManager, - }); - - testUsingContext('debugTogglePlatform', () async { - final BufferLogger logger = BufferLogger.test(); - final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice, logger: logger); - fakeVmServiceHost = FakeVmServiceHost(requests: [ - ...kAttachExpectations, - const FakeVmServiceRequest( - method: 'ext.flutter.platformOverride', - args: { - 'isolateId': null, - }, - jsonResponse: { - 'value': 'iOS' - }, - ), - const FakeVmServiceRequest( - method: 'ext.flutter.platformOverride', - args: { - 'isolateId': null, - 'value': 'fuchsia', - }, - jsonResponse: { - 'value': 'fuchsia' - }, - ), - ]); - _setupMocks(); - final Completer connectionInfoCompleter = Completer(); - unawaited(residentWebRunner.run( - connectionInfoCompleter: connectionInfoCompleter, - )); - await connectionInfoCompleter.future; - - await residentWebRunner.debugTogglePlatform(); - - expect(logger.statusText, - contains('Switched operating system to fuchsia')); - expect(fakeVmServiceHost.hasRemainingExpectations, false); - }, overrides: { - FileSystem: () => fileSystem, - ProcessManager: () => processManager, - }); - - testUsingContext('debugToggleBrightness', () async { - final BufferLogger logger = BufferLogger.test(); - final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice, logger: logger); - fakeVmServiceHost = FakeVmServiceHost(requests: [ - ...kAttachExpectations, - const FakeVmServiceRequest( - method: 'ext.flutter.brightnessOverride', - args: { - 'isolateId': null, - }, - jsonResponse: { - 'value': 'Brightness.light' - }, - ), - const FakeVmServiceRequest( - method: 'ext.flutter.brightnessOverride', - args: { - 'isolateId': null, - 'value': 'Brightness.dark', - }, - jsonResponse: { - 'value': 'Brightness.dark' - }, - ), - ]); - _setupMocks(); - final Completer connectionInfoCompleter = Completer(); - unawaited(residentWebRunner.run( - connectionInfoCompleter: connectionInfoCompleter, - )); - await connectionInfoCompleter.future; - - await residentWebRunner.debugToggleBrightness(); - - expect(logger.statusText, - contains('Changed brightness to Brightness.dark.')); - expect(fakeVmServiceHost.hasRemainingExpectations, false); - }, overrides: { - FileSystem: () => fileSystem, - ProcessManager: () => processManager, - }); - testUsingContext('cleanup of resources is safe to call multiple times', () async { final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice); fakeVmServiceHost = FakeVmServiceHost(requests: [ diff --git a/packages/flutter_tools/test/general.shard/terminal_handler_test.dart b/packages/flutter_tools/test/general.shard/terminal_handler_test.dart index 8a4408af5e3..3c2abf419b6 100644 --- a/packages/flutter_tools/test/general.shard/terminal_handler_test.dart +++ b/packages/flutter_tools/test/general.shard/terminal_handler_test.dart @@ -6,17 +6,83 @@ import 'dart:async'; +import 'package:file/file.dart'; +import 'package:vm_service/vm_service.dart' as vm_service; import 'package:file/memory.dart'; +import 'package:file_testing/file_testing.dart'; import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/signals.dart'; import 'package:flutter_tools/src/base/terminal.dart'; +import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/compile.dart'; import 'package:flutter_tools/src/device.dart'; import 'package:flutter_tools/src/resident_runner.dart'; -import 'package:mockito/mockito.dart'; +import 'package:flutter_tools/src/vmservice.dart'; +import 'package:test/fake.dart'; import '../src/common.dart'; +import '../src/fake_vm_services.dart'; + +final vm_service.Isolate fakeUnpausedIsolate = vm_service.Isolate( + id: '1', + pauseEvent: vm_service.Event( + kind: vm_service.EventKind.kResume, + timestamp: 0 + ), + breakpoints: [], + exceptionPauseMode: null, + extensionRPCs: [], + libraries: [ + vm_service.LibraryRef( + id: '1', + uri: 'file:///hello_world/main.dart', + name: '', + ), + ], + livePorts: 0, + name: 'test', + number: '1', + pauseOnExit: false, + runnable: true, + startTime: 0, + isSystemIsolate: false, + isolateFlags: [], +); + +final vm_service.VM fakeVM = vm_service.VM( + isolates: [fakeUnpausedIsolate], + pid: 1, + hostCPU: '', + isolateGroups: [], + targetCPU: '', + startTime: 0, + name: 'dart', + architectureBits: 64, + operatingSystem: '', + version: '', + systemIsolateGroups: [], + systemIsolates: [], +); + +final FlutterView fakeFlutterView = FlutterView( + id: 'a', + uiIsolate: fakeUnpausedIsolate, +); + +final FakeVmServiceRequest listViews = FakeVmServiceRequest( + method: kListViewsMethod, + jsonResponse: { + 'views': [ + fakeFlutterView.toJson(), + ], + }, +); + +final FakeVmServiceRequest getVM = FakeVmServiceRequest( + method: 'getVM', + jsonResponse: fakeVM.toJson(), +); void main() { testWithoutContext('keyboard input handling single help character', () async { @@ -62,278 +128,829 @@ void main() { }); group('keycode verification, brought to you by the letter', () { - MockResidentRunner mockResidentRunner; - TerminalHandler terminalHandler; - BufferLogger testLogger; - - setUp(() { - testLogger = BufferLogger.test(); - final Signals signals = Signals.test(); - final Terminal terminal = Terminal.test(); - final MemoryFileSystem fs = MemoryFileSystem.test(); - final ProcessInfo processInfo = ProcessInfo.test(fs); - mockResidentRunner = MockResidentRunner(); - terminalHandler = TerminalHandler( - mockResidentRunner, - logger: testLogger, - signals: signals, - terminal: terminal, - processInfo: processInfo, - reportReady: false, - ); - when(mockResidentRunner.supportsServiceProtocol).thenReturn(true); - }); - testWithoutContext('a, can handle trailing newlines', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([], supportsServiceProtocol: false); await terminalHandler.processTerminalInput('a\n'); expect(terminalHandler.lastReceivedCommand, 'a'); }); testWithoutContext('n, can handle trailing only newlines', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([]); await terminalHandler.processTerminalInput('\n\n'); expect(terminalHandler.lastReceivedCommand, ''); }); - testWithoutContext('a - debugToggleProfileWidgetBuilds with service protocol', () async { - await terminalHandler.processTerminalInput('a'); + testWithoutContext('a - debugToggleProfileWidgetBuilds', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([ + listViews, + const FakeVmServiceRequest( + method: 'ext.flutter.profileWidgetBuilds', + args: { + 'isolateId': '1', + }, + jsonResponse: { + 'enabled': 'false' + }, + ), + const FakeVmServiceRequest( + method: 'ext.flutter.profileWidgetBuilds', + args: { + 'isolateId': '1', + 'enabled': 'true' + }, + jsonResponse: { + 'enabled': 'true' + }, + ), + ]); - verify(mockResidentRunner.debugToggleProfileWidgetBuilds()).called(1); + await terminalHandler.processTerminalInput('a'); }); - testWithoutContext('a - debugToggleProfileWidgetBuilds', () async { - when(mockResidentRunner.supportsServiceProtocol).thenReturn(true); - await terminalHandler.processTerminalInput('a'); + testWithoutContext('a - debugToggleProfileWidgetBuilds with web target', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([ + getVM, + const FakeVmServiceRequest( + method: 'ext.flutter.profileWidgetBuilds', + args: { + 'isolateId': '1', + }, + jsonResponse: { + 'enabled': 'false' + }, + ), + const FakeVmServiceRequest( + method: 'ext.flutter.profileWidgetBuilds', + args: { + 'isolateId': '1', + 'enabled': 'true' + }, + jsonResponse: { + 'enabled': 'true' + }, + ), + ], web: true); - verify(mockResidentRunner.debugToggleProfileWidgetBuilds()).called(1); + await terminalHandler.processTerminalInput('a'); + }); + + testWithoutContext('a - debugToggleProfileWidgetBuilds without service protocol is skipped', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([], supportsServiceProtocol: false); + + await terminalHandler.processTerminalInput('a'); }); testWithoutContext('b - debugToggleBrightness', () async { - when(mockResidentRunner.supportsServiceProtocol).thenReturn(true); + final TerminalHandler terminalHandler = setUpTerminalHandler([ + listViews, + const FakeVmServiceRequest( + method: 'ext.flutter.brightnessOverride', + args: { + 'isolateId': '1', + }, + jsonResponse: { + 'value': 'Brightness.light', + } + ), + listViews, + const FakeVmServiceRequest( + method: 'ext.flutter.brightnessOverride', + args: { + 'isolateId': '1', + 'value': 'Brightness.dark', + }, + jsonResponse: { + 'value': 'Brightness.dark', + } + ), + ]); await terminalHandler.processTerminalInput('b'); - verify(mockResidentRunner.debugToggleBrightness()).called(1); + expect(terminalHandler.logger.statusText, contains('Changed brightness to Brightness.dark')); + }); + + testWithoutContext('b - debugToggleBrightness with web target', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([ + getVM, + const FakeVmServiceRequest( + method: 'ext.flutter.brightnessOverride', + args: { + 'isolateId': '1', + }, + jsonResponse: { + 'value': 'Brightness.light', + } + ), + getVM, + const FakeVmServiceRequest( + method: 'ext.flutter.brightnessOverride', + args: { + 'isolateId': '1', + 'value': 'Brightness.dark', + }, + jsonResponse: { + 'value': 'Brightness.dark', + } + ), + ], web: true); + await terminalHandler.processTerminalInput('b'); + + expect(terminalHandler.logger.statusText, contains('Changed brightness to Brightness.dark')); + }); + + testWithoutContext('b - debugToggleBrightness without service protocol is skipped', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([], supportsServiceProtocol: false); + + await terminalHandler.processTerminalInput('b'); }); testWithoutContext('d,D - detach', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([]); + final FakeResidentRunner runner = terminalHandler.residentRunner as FakeResidentRunner; await terminalHandler.processTerminalInput('d'); + + expect(runner.calledDetach, true); + runner.calledDetach = false; + await terminalHandler.processTerminalInput('D'); - verify(mockResidentRunner.detach()).called(2); + expect(runner.calledDetach, true); }); testWithoutContext('h,H,? - printHelp', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([]); + final FakeResidentRunner runner = terminalHandler.residentRunner as FakeResidentRunner; await terminalHandler.processTerminalInput('h'); + + expect(runner.calledPrintWithDetails, true); + runner.calledPrintWithDetails = false; + await terminalHandler.processTerminalInput('H'); + + expect(runner.calledPrintWithDetails, true); + runner.calledPrintWithDetails = false; + await terminalHandler.processTerminalInput('?'); - verify(mockResidentRunner.printHelp(details: true)).called(3); + expect(runner.calledPrintWithDetails, true); }); - testWithoutContext('i - debugToggleWidgetInspector with service protocol', () async { + testWithoutContext('i - debugToggleWidgetInspector', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([ + listViews, + const FakeVmServiceRequest( + method: 'ext.flutter.inspector.show', + args: { + 'isolateId': '1', + }, + ), + ]); + await terminalHandler.processTerminalInput('i'); - - verify(mockResidentRunner.debugToggleWidgetInspector()).called(1); }); - testWithoutContext('I - debugToggleInvertOversizedImages with service protocol/debug', () async { - when(mockResidentRunner.isRunningDebug).thenReturn(true); + testWithoutContext('i - debugToggleWidgetInspector with web target', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([ + getVM, + const FakeVmServiceRequest( + method: 'ext.flutter.inspector.show', + args: { + 'isolateId': '1', + }, + ), + ], web: true); + + await terminalHandler.processTerminalInput('i'); + }); + + testWithoutContext('i - debugToggleWidgetInspector without service protocol is skipped', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([], supportsServiceProtocol: false); + + await terminalHandler.processTerminalInput('i'); + }); + + testWithoutContext('I - debugToggleInvertOversizedImages', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([ + listViews, + const FakeVmServiceRequest( + method: 'ext.flutter.invertOversizedImages', + args: { + 'isolateId': '1', + }, + ), + ]); await terminalHandler.processTerminalInput('I'); - - verify(mockResidentRunner.debugToggleInvertOversizedImages()).called(1); }); - testWithoutContext('L - debugDumpLayerTree with service protocol', () async { + testWithoutContext('I - debugToggleInvertOversizedImages with web target', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([ + getVM, + const FakeVmServiceRequest( + method: 'ext.flutter.invertOversizedImages', + args: { + 'isolateId': '1', + }, + ), + ], web: true); + await terminalHandler.processTerminalInput('I'); + }); + + testWithoutContext('I - debugToggleInvertOversizedImages without service protocol is skipped', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([], supportsServiceProtocol: false); + await terminalHandler.processTerminalInput('I'); + }); + + testWithoutContext('I - debugToggleInvertOversizedImages in profile mode is skipped', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([], buildMode: BuildMode.profile); + await terminalHandler.processTerminalInput('I'); + }); + + testWithoutContext('L - debugDumpLayerTree', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([ + listViews, + const FakeVmServiceRequest( + method: 'ext.flutter.debugDumpLayerTree', + args: { + 'isolateId': '1', + }, + jsonResponse: { + 'data': 'LAYER TREE', + } + ), + ]); await terminalHandler.processTerminalInput('L'); - verify(mockResidentRunner.debugDumpLayerTree()).called(1); + expect(terminalHandler.logger.statusText, contains('LAYER TREE')); }); - testWithoutContext('o,O - debugTogglePlatform with service protocol and debug mode', () async { - when(mockResidentRunner.isRunningDebug).thenReturn(true); + testWithoutContext('L - debugDumpLayerTree with web target', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([ + getVM, + const FakeVmServiceRequest( + method: 'ext.flutter.debugDumpLayerTree', + args: { + 'isolateId': '1', + }, + jsonResponse: { + 'data': 'LAYER TREE', + } + ), + ], web: true); + await terminalHandler.processTerminalInput('L'); + + expect(terminalHandler.logger.statusText, contains('LAYER TREE')); + }); + + testWithoutContext('L - debugDumpLayerTree with service protocol and profile mode is skipped', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([], buildMode: BuildMode.profile); + await terminalHandler.processTerminalInput('L'); + }); + + testWithoutContext('L - debugDumpLayerTree without service protocol is skipped', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([], supportsServiceProtocol: false); + await terminalHandler.processTerminalInput('L'); + }); + + testWithoutContext('o,O - debugTogglePlatform', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([ + // Request 1. + listViews, + const FakeVmServiceRequest( + method: 'ext.flutter.platformOverride', + args: { + 'isolateId': '1', + }, + jsonResponse: { + 'value': 'iOS' + }, + ), + listViews, + const FakeVmServiceRequest( + method: 'ext.flutter.platformOverride', + args: { + 'isolateId': '1', + 'value': 'fuchsia', + }, + jsonResponse: { + 'value': 'fuchsia' + }, + ), + // Request 2. + listViews, + const FakeVmServiceRequest( + method: 'ext.flutter.platformOverride', + args: { + 'isolateId': '1', + }, + jsonResponse: { + 'value': 'android' + }, + ), + listViews, + const FakeVmServiceRequest( + method: 'ext.flutter.platformOverride', + args: { + 'isolateId': '1', + 'value': 'iOS', + }, + jsonResponse: { + 'value': 'iOS' + }, + ), + ]); await terminalHandler.processTerminalInput('o'); await terminalHandler.processTerminalInput('O'); - verify(mockResidentRunner.debugTogglePlatform()).called(2); + expect(terminalHandler.logger.statusText, contains('Switched operating system to fuchsia')); + expect(terminalHandler.logger.statusText, contains('Switched operating system to iOS')); }); - testWithoutContext('p - debugToggleDebugPaintSizeEnabled with service protocol and debug mode', () async { - when(mockResidentRunner.isRunningDebug).thenReturn(true); + testWithoutContext('o,O - debugTogglePlatform with web target', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([ + // Request 1. + getVM, + const FakeVmServiceRequest( + method: 'ext.flutter.platformOverride', + args: { + 'isolateId': '1', + }, + jsonResponse: { + 'value': 'iOS' + }, + ), + getVM, + const FakeVmServiceRequest( + method: 'ext.flutter.platformOverride', + args: { + 'isolateId': '1', + 'value': 'fuchsia', + }, + jsonResponse: { + 'value': 'fuchsia' + }, + ), + // Request 2. + getVM, + const FakeVmServiceRequest( + method: 'ext.flutter.platformOverride', + args: { + 'isolateId': '1', + }, + jsonResponse: { + 'value': 'android' + }, + ), + getVM, + const FakeVmServiceRequest( + method: 'ext.flutter.platformOverride', + args: { + 'isolateId': '1', + 'value': 'iOS', + }, + jsonResponse: { + 'value': 'iOS' + }, + ), + ], web: true); + await terminalHandler.processTerminalInput('o'); + await terminalHandler.processTerminalInput('O'); + + expect(terminalHandler.logger.statusText, contains('Switched operating system to fuchsia')); + expect(terminalHandler.logger.statusText, contains('Switched operating system to iOS')); + }); + + testWithoutContext('o,O - debugTogglePlatform without service protocol is skipped', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([], supportsServiceProtocol: false); + await terminalHandler.processTerminalInput('o'); + await terminalHandler.processTerminalInput('O'); + }); + + testWithoutContext('p - debugToggleDebugPaintSizeEnabled', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([ + listViews, + const FakeVmServiceRequest( + method: 'ext.flutter.debugPaint', + args: { + 'isolateId': '1', + }, + ), + ]); await terminalHandler.processTerminalInput('p'); - - verify(mockResidentRunner.debugToggleDebugPaintSizeEnabled()).called(1); }); - testWithoutContext('p - debugToggleDebugPaintSizeEnabled with service protocol and debug mode', () async { - when(mockResidentRunner.isRunningDebug).thenReturn(true); + testWithoutContext('p - debugToggleDebugPaintSizeEnabled with web target', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([ + getVM, + const FakeVmServiceRequest( + method: 'ext.flutter.debugPaint', + args: { + 'isolateId': '1', + }, + ), + ], web: true); await terminalHandler.processTerminalInput('p'); - - verify(mockResidentRunner.debugToggleDebugPaintSizeEnabled()).called(1); }); - testWithoutContext('P - debugTogglePerformanceOverlayOverride with service protocol', () async { + testWithoutContext('p - debugToggleDebugPaintSizeEnabled without service protocol is skipped', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([], supportsServiceProtocol: false); + await terminalHandler.processTerminalInput('p'); + }); + + testWithoutContext('P - debugTogglePerformanceOverlayOverride', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([ + listViews, + const FakeVmServiceRequest( + method: 'ext.flutter.showPerformanceOverlay', + args: { + 'isolateId': '1', + }, + ), + ]); await terminalHandler.processTerminalInput('P'); - - verify(mockResidentRunner.debugTogglePerformanceOverlayOverride()).called(1); }); - testWithoutContext('q,Q - exit', () async { - await terminalHandler.processTerminalInput('q'); - await terminalHandler.processTerminalInput('Q'); + testWithoutContext('P - debugTogglePerformanceOverlayOverride with web target is skipped ', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([], web: true); + await terminalHandler.processTerminalInput('P'); + }); - verify(mockResidentRunner.exit()).called(2); + testWithoutContext('P - debugTogglePerformanceOverlayOverride without service protocol is skipped ', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([], supportsServiceProtocol: false); + await terminalHandler.processTerminalInput('P'); }); testWithoutContext('s - screenshot', () async { - final MockDevice mockDevice = MockDevice(); - final MockFlutterDevice mockFlutterDevice = MockFlutterDevice(); - when(mockResidentRunner.isRunningDebug).thenReturn(true); - when(mockResidentRunner.flutterDevices).thenReturn([mockFlutterDevice]); - when(mockFlutterDevice.device).thenReturn(mockDevice); - when(mockDevice.supportsScreenshot).thenReturn(true); + final TerminalHandler terminalHandler = setUpTerminalHandler([]); await terminalHandler.processTerminalInput('s'); - - verify(mockResidentRunner.screenshot(mockFlutterDevice)).called(1); }); - testWithoutContext('r - hotReload supported and succeeds', () async { - when(mockResidentRunner.canHotReload).thenReturn(true); - when(mockResidentRunner.restart(fullRestart: false)) - .thenAnswer((Invocation invocation) async { - return OperationResult(0, ''); - }); - await terminalHandler.processTerminalInput('r'); - - verify(mockResidentRunner.restart(fullRestart: false)).called(1); - }); - - testWithoutContext('r - hotReload supported and fails', () async { - when(mockResidentRunner.canHotReload).thenReturn(true); - when(mockResidentRunner.restart(fullRestart: false)) - .thenAnswer((Invocation invocation) async { - return OperationResult(1, ''); - }); - await terminalHandler.processTerminalInput('r'); - - verify(mockResidentRunner.restart(fullRestart: false)).called(1); - - expect(testLogger.statusText, contains('Try again after fixing the above error(s).')); - }); - - testWithoutContext('r - hotReload supported and fails fatally', () async { - when(mockResidentRunner.canHotReload).thenReturn(true); - when(mockResidentRunner.hotMode).thenReturn(true); - when(mockResidentRunner.restart(fullRestart: false)) - .thenAnswer((Invocation invocation) async { - return OperationResult(1, 'fail', fatal: true); - }); - expect(terminalHandler.processTerminalInput('r'), throwsToolExit()); - }); - - testWithoutContext('r - hotReload unsupported', () async { - when(mockResidentRunner.canHotReload).thenReturn(false); - await terminalHandler.processTerminalInput('r'); - - verifyNever(mockResidentRunner.restart(fullRestart: false)); - }); - - testWithoutContext('R - hotRestart supported and succeeds', () async { - when(mockResidentRunner.supportsRestart).thenReturn(true); - when(mockResidentRunner.hotMode).thenReturn(true); - when(mockResidentRunner.restart(fullRestart: true)) - .thenAnswer((Invocation invocation) async { - return OperationResult(0, ''); - }); - await terminalHandler.processTerminalInput('R'); - - verify(mockResidentRunner.restart(fullRestart: true)).called(1); - }); - - testWithoutContext('R - hotRestart supported and fails', () async { - when(mockResidentRunner.supportsRestart).thenReturn(true); - when(mockResidentRunner.hotMode).thenReturn(true); - when(mockResidentRunner.restart(fullRestart: true)) - .thenAnswer((Invocation invocation) async { - return OperationResult(1, 'fail'); - }); - await terminalHandler.processTerminalInput('R'); - - verify(mockResidentRunner.restart(fullRestart: true)).called(1); - - expect(testLogger.statusText, contains('Try again after fixing the above error(s).')); - }); - - testWithoutContext('R - hotRestart supported and fails fatally', () async { - when(mockResidentRunner.supportsRestart).thenReturn(true); - when(mockResidentRunner.hotMode).thenReturn(true); - when(mockResidentRunner.restart(fullRestart: true)) - .thenAnswer((Invocation invocation) async { - return OperationResult(1, 'fail', fatal: true); - }); - expect(() => terminalHandler.processTerminalInput('R'), throwsToolExit()); - }); - - testWithoutContext('R - hot restart unsupported', () async { - when(mockResidentRunner.supportsRestart).thenReturn(false); - await terminalHandler.processTerminalInput('R'); - - verifyNever(mockResidentRunner.restart(fullRestart: true)); - }); - - testWithoutContext('ResidentRunner clears the screen when it should', () async { - const String message = 'This should be cleared'; - - expect(testLogger.statusText, equals('')); - testLogger.printStatus(message); - expect(testLogger.statusText, equals(message + '\n')); // printStatus makes a newline - - await terminalHandler.processTerminalInput('c'); - expect(testLogger.statusText, equals('')); - }); - - testWithoutContext('S - debugDumpSemanticsTreeInTraversalOrder with service protocol', () async { + testWithoutContext('S - debugDumpSemanticsTreeInTraversalOrder', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([ + listViews, + const FakeVmServiceRequest( + method: 'ext.flutter.debugDumpSemanticsTreeInTraversalOrder', + args: { + 'isolateId': '1', + }, + jsonResponse: { + 'data': 'SEMANTICS DATA', + }, + ), + ]); await terminalHandler.processTerminalInput('S'); - verify(mockResidentRunner.debugDumpSemanticsTreeInTraversalOrder()).called(1); + expect(terminalHandler.logger.statusText, contains('SEMANTICS DATA')); }); - testWithoutContext('t,T - debugDumpRenderTree with service protocol', () async { + testWithoutContext('S - debugDumpSemanticsTreeInTraversalOrder with web target', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([ + getVM, + const FakeVmServiceRequest( + method: 'ext.flutter.debugDumpSemanticsTreeInTraversalOrder', + args: { + 'isolateId': '1', + }, + jsonResponse: { + 'data': 'SEMANTICS DATA', + }, + ), + ], web: true); + await terminalHandler.processTerminalInput('S'); + + expect(terminalHandler.logger.statusText, contains('SEMANTICS DATA')); + }); + + testWithoutContext('S - debugDumpSemanticsTreeInTraversalOrder without service protocol is skipped', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([], supportsServiceProtocol: false); + await terminalHandler.processTerminalInput('S'); + }); + + testWithoutContext('U - debugDumpSemanticsTreeInInverseHitTestOrder', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([ + listViews, + const FakeVmServiceRequest( + method: 'ext.flutter.debugDumpSemanticsTreeInInverseHitTestOrder', + args: { + 'isolateId': '1', + }, + jsonResponse: { + 'data': 'SEMANTICS DATA', + }, + ), + ]); + await terminalHandler.processTerminalInput('U'); + + expect(terminalHandler.logger.statusText, contains('SEMANTICS DATA')); + }); + + testWithoutContext('U - debugDumpSemanticsTreeInInverseHitTestOrder with web target', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([ + getVM, + const FakeVmServiceRequest( + method: 'ext.flutter.debugDumpSemanticsTreeInInverseHitTestOrder', + args: { + 'isolateId': '1', + }, + jsonResponse: { + 'data': 'SEMANTICS DATA', + }, + ), + ], web: true); + await terminalHandler.processTerminalInput('U'); + + expect(terminalHandler.logger.statusText, contains('SEMANTICS DATA')); + }); + + testWithoutContext('U - debugDumpSemanticsTreeInInverseHitTestOrder without service protocol is skipped', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([], supportsServiceProtocol: false); + await terminalHandler.processTerminalInput('U'); + }); + + testWithoutContext('t,T - debugDumpRenderTree', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([ + listViews, + const FakeVmServiceRequest( + method: 'ext.flutter.debugDumpRenderTree', + args: { + 'isolateId': '1', + }, + jsonResponse: { + 'data': 'RENDER DATA 1', + }, + ), + // Request 2. + listViews, + const FakeVmServiceRequest( + method: 'ext.flutter.debugDumpRenderTree', + args: { + 'isolateId': '1', + }, + jsonResponse: { + 'data': 'RENDER DATA 2', + }, + ), + ]); await terminalHandler.processTerminalInput('t'); await terminalHandler.processTerminalInput('T'); - verify(mockResidentRunner.debugDumpRenderTree()).called(2); + expect(terminalHandler.logger.statusText, contains('RENDER DATA 1')); + expect(terminalHandler.logger.statusText, contains('RENDER DATA 2')); }); - testWithoutContext('U - debugDumpRenderTree with service protocol', () async { - await terminalHandler.processTerminalInput('U'); + testWithoutContext('t,T - debugDumpRenderTree with web target', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([ + getVM, + const FakeVmServiceRequest( + method: 'ext.flutter.debugDumpRenderTree', + args: { + 'isolateId': '1', + }, + jsonResponse: { + 'data': 'RENDER DATA 1', + }, + ), + // Request 2. + getVM, + const FakeVmServiceRequest( + method: 'ext.flutter.debugDumpRenderTree', + args: { + 'isolateId': '1', + }, + jsonResponse: { + 'data': 'RENDER DATA 2', + }, + ), + ], web: true); + await terminalHandler.processTerminalInput('t'); + await terminalHandler.processTerminalInput('T'); - verify(mockResidentRunner.debugDumpSemanticsTreeInInverseHitTestOrder()).called(1); + expect(terminalHandler.logger.statusText, contains('RENDER DATA 1')); + expect(terminalHandler.logger.statusText, contains('RENDER DATA 2')); }); - testWithoutContext('w,W - debugDumpApp with service protocol', () async { + testWithoutContext('t,T - debugDumpRenderTree without service protocol is skipped', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([], supportsServiceProtocol: false); + await terminalHandler.processTerminalInput('t'); + await terminalHandler.processTerminalInput('T'); + }); + + testWithoutContext('w,W - debugDumpApp', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([ + listViews, + const FakeVmServiceRequest( + method: 'ext.flutter.debugDumpApp', + args: { + 'isolateId': '1', + }, + jsonResponse: { + 'data': 'WIDGET DATA 1', + }, + ), + // Request 2. + listViews, + const FakeVmServiceRequest( + method: 'ext.flutter.debugDumpApp', + args: { + 'isolateId': '1', + }, + jsonResponse: { + 'data': 'WIDGET DATA 2', + }, + ), + ]); await terminalHandler.processTerminalInput('w'); await terminalHandler.processTerminalInput('W'); - verify(mockResidentRunner.debugDumpApp()).called(2); + expect(terminalHandler.logger.statusText, contains('WIDGET DATA 1')); + expect(terminalHandler.logger.statusText, contains('WIDGET DATA 2')); }); - testWithoutContext('z,Z - debugToggleDebugCheckElevationsEnabled with service protocol', () async { + testWithoutContext('w,W - debugDumpApp with web target', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([ + getVM, + const FakeVmServiceRequest( + method: 'ext.flutter.debugDumpApp', + args: { + 'isolateId': '1', + }, + jsonResponse: { + 'data': 'WIDGET DATA 1', + }, + ), + // Request 2. + getVM, + const FakeVmServiceRequest( + method: 'ext.flutter.debugDumpApp', + args: { + 'isolateId': '1', + }, + jsonResponse: { + 'data': 'WIDGET DATA 2', + }, + ), + ], web: true); + await terminalHandler.processTerminalInput('w'); + await terminalHandler.processTerminalInput('W'); + + expect(terminalHandler.logger.statusText, contains('WIDGET DATA 1')); + expect(terminalHandler.logger.statusText, contains('WIDGET DATA 2')); + }); + + testWithoutContext('w,W - debugDumpApp without service protocol is skipped', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([], supportsServiceProtocol: false); + await terminalHandler.processTerminalInput('w'); + await terminalHandler.processTerminalInput('W'); + }); + + testWithoutContext('z,Z - debugToggleDebugCheckElevationsEnabled', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([ + listViews, + const FakeVmServiceRequest( + method: 'ext.flutter.debugCheckElevationsEnabled', + args: { + 'isolateId': '1', + }, + ), + // Request 2. + listViews, + const FakeVmServiceRequest( + method: 'ext.flutter.debugCheckElevationsEnabled', + args: { + 'isolateId': '1', + }, + ), + ]); await terminalHandler.processTerminalInput('z'); await terminalHandler.processTerminalInput('Z'); - - verify(mockResidentRunner.debugToggleDebugCheckElevationsEnabled()).called(2); }); - testWithoutContext('z,Z - debugToggleDebugCheckElevationsEnabled without service protocol', () async { - when(mockResidentRunner.supportsServiceProtocol).thenReturn(false); + testWithoutContext('z,Z - debugToggleDebugCheckElevationsEnabled with web target', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([ + getVM, + const FakeVmServiceRequest( + method: 'ext.flutter.debugCheckElevationsEnabled', + args: { + 'isolateId': '1', + }, + ), + // Request 2. + getVM, + const FakeVmServiceRequest( + method: 'ext.flutter.debugCheckElevationsEnabled', + args: { + 'isolateId': '1', + }, + ), + ], web: true); await terminalHandler.processTerminalInput('z'); await terminalHandler.processTerminalInput('Z'); - - // This should probably be disable when the service protocol is not enabled. - verify(mockResidentRunner.debugToggleDebugCheckElevationsEnabled()).called(2); }); + + testWithoutContext('z,Z - debugToggleDebugCheckElevationsEnabled without service protocol is skipped', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([], supportsServiceProtocol: false); + await terminalHandler.processTerminalInput('z'); + await terminalHandler.processTerminalInput('Z'); + }); + + testWithoutContext('q,Q - exit', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([]); + final FakeResidentRunner runner = terminalHandler.residentRunner as FakeResidentRunner; + await terminalHandler.processTerminalInput('q'); + + expect(runner.calledExit, true); + runner.calledExit = false; + + await terminalHandler.processTerminalInput('Q'); + + expect(runner.calledExit, true); + }); + + testWithoutContext('r - hotReload unsupported', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([], supportsHotReload: false); + await terminalHandler.processTerminalInput('r'); + }); + + testWithoutContext('R - hotRestart unsupported', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([], supportsRestart: false); + await terminalHandler.processTerminalInput('R'); + }); + + testWithoutContext('r - hotReload', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([]); + final FakeResidentRunner runner = terminalHandler.residentRunner as FakeResidentRunner; + + await terminalHandler.processTerminalInput('r'); + + expect(runner.calledReload, true); + expect(runner.calledRestart, false); + }); + + testWithoutContext('R - hotRestart', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([]); + final FakeResidentRunner runner = terminalHandler.residentRunner as FakeResidentRunner; + + await terminalHandler.processTerminalInput('R'); + + expect(runner.calledReload, false); + expect(runner.calledRestart, true); + }); + + testWithoutContext('r - hotReload with non-fatal error', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([], reloadExitCode: 1); + final FakeResidentRunner runner = terminalHandler.residentRunner as FakeResidentRunner; + + await terminalHandler.processTerminalInput('r'); + + expect(runner.calledReload, true); + expect(runner.calledRestart, false); + expect(terminalHandler.logger.statusText, contains('Try again after fixing the above error(s).')); + }); + + testWithoutContext('R - hotRestart with non-fatal error', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([], reloadExitCode: 1); + final FakeResidentRunner runner = terminalHandler.residentRunner as FakeResidentRunner; + + await terminalHandler.processTerminalInput('R'); + + expect(runner.calledReload, false); + expect(runner.calledRestart, true); + expect(terminalHandler.logger.statusText, contains('Try again after fixing the above error(s).')); + }); + + testWithoutContext('r - hotReload with fatal error', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([], reloadExitCode: 1, fatalReloadError: true); + final FakeResidentRunner runner = terminalHandler.residentRunner as FakeResidentRunner; + + await expectLater(() => terminalHandler.processTerminalInput('r'), throwsToolExit()); + + expect(runner.calledReload, true); + expect(runner.calledRestart, false); + }); + + testWithoutContext('R - hotRestart with fatal error', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([], reloadExitCode: 1, fatalReloadError: true); + final FakeResidentRunner runner = terminalHandler.residentRunner as FakeResidentRunner; + + await expectLater(() => terminalHandler.processTerminalInput('R'), throwsToolExit()); + + expect(runner.calledReload, false); + expect(runner.calledRestart, true); + }); + }); + + testWithoutContext('ResidentRunner clears the screen when it should', () async { + final TerminalHandler terminalHandler = setUpTerminalHandler([], reloadExitCode: 1, fatalReloadError: true); + const String message = 'This should be cleared'; + + expect(terminalHandler.logger.statusText, equals('')); + terminalHandler.logger.printStatus(message); + expect(terminalHandler.logger.statusText, equals(message + '\n')); // printStatus makes a newline + + await terminalHandler.processTerminalInput('c'); + expect(terminalHandler.logger.statusText, equals('')); }); testWithoutContext('pidfile creation', () { @@ -342,13 +959,19 @@ void main() { final Terminal terminal = Terminal.test(); final MemoryFileSystem fs = MemoryFileSystem.test(); final ProcessInfo processInfo = ProcessInfo.test(fs); - final ResidentRunner mockResidentRunner = MockResidentRunner(); - when(mockResidentRunner.stayResident).thenReturn(true); - when(mockResidentRunner.supportsServiceProtocol).thenReturn(true); - when(mockResidentRunner.supportsRestart).thenReturn(true); + final FakeResidentRunner residentRunner = FakeResidentRunner( + FlutterDevice(FakeDevice(), buildInfo: BuildInfo.debug, generator: FakeResidentCompiler()), + testLogger, + fs, + ); + residentRunner + ..supportsRestart = true + ..supportsServiceProtocol = true + ..stayResident = true; + const String filename = 'test.pid'; final TerminalHandler terminalHandler = TerminalHandler( - mockResidentRunner, + residentRunner, logger: testLogger, signals: signals, terminal: terminal, @@ -356,23 +979,183 @@ void main() { reportReady: false, pidFile: filename, ); - expect(fs.file(filename).existsSync(), isFalse); + + expect(fs.file(filename), isNot(exists)); terminalHandler.setupTerminal(); terminalHandler.registerSignalHandlers(); - expect(fs.file(filename).existsSync(), isTrue); + expect(fs.file(filename), exists); terminalHandler.stop(); - expect(fs.file(filename).existsSync(), isFalse); + expect(fs.file(filename), isNot(exists)); }); } -class MockDevice extends Mock implements Device { - MockDevice() { - when(isSupported()).thenReturn(true); +class FakeResidentRunner extends ResidentHandlers { + FakeResidentRunner(FlutterDevice device, this.logger, this.fileSystem) : flutterDevices = [device]; + + bool calledDetach = false; + bool calledPrint = false; + bool calledExit = false; + bool calledPrintWithDetails = false; + bool calledReload = false; + bool calledRestart = false; + int reloadExitCode = 0; + bool fatalReloadError = false; + + @override + final Logger logger; + + @override + final FileSystem fileSystem; + + @override + final List flutterDevices; + + @override + bool canHotReload = true; + + @override + bool hotMode = true; + + @override + bool isRunningDebug = true; + + @override + bool isRunningProfile = false; + + @override + bool isRunningRelease = false; + + @override + bool stayResident = true; + + @override + bool supportsRestart = true; + + @override + bool supportsServiceProtocol = true; + + @override + bool supportsWriteSkSL = true; + + @override + Future cleanupAfterSignal() async { } + + @override + Future detach() async { + calledDetach = true; + } + + @override + Future exit() async { + calledExit = true; + } + + @override + void printHelp({bool details}) { + if (details) { + calledPrintWithDetails = true; + } else { + calledPrint = true; + } + } + + @override + Future runSourceGenerators() async { } + + @override + Future restart({bool fullRestart = false, bool pause = false, String reason}) async { + if (fullRestart && !supportsRestart) { + throw StateError('illegal restart'); + } + if (!fullRestart && !canHotReload) { + throw StateError('illegal reload'); + } + if (fullRestart) { + calledRestart = true; + } else { + calledReload = true; + } + return OperationResult(reloadExitCode, '', fatal: fatalReloadError); } } -class MockResidentRunner extends Mock implements ResidentRunner {} -class MockFlutterDevice extends Mock implements FlutterDevice {} +class FakeDevice extends Fake implements Device { + @override + bool isSupported() => true; + + @override + bool supportsScreenshot = false; + + @override + Future takeScreenshot(File file) async { + if (!supportsScreenshot) { + throw StateError('illegal screenshot attempt'); + } + file.writeAsBytesSync([1, 2, 3, 4]); + } + +} + +TerminalHandler setUpTerminalHandler(List requests, { + bool supportsRestart = true, + bool supportsServiceProtocol = true, + bool supportsHotReload = true, + bool web = false, + bool fatalReloadError = false, + int reloadExitCode = 0, + BuildMode buildMode = BuildMode.debug, +}) { + final BufferLogger testLogger = BufferLogger.test(); + final Signals signals = Signals.test(); + final Terminal terminal = Terminal.test(); + final FileSystem fileSystem = MemoryFileSystem.test(); + final ProcessInfo processInfo = ProcessInfo.test(MemoryFileSystem.test()); + final FlutterDevice device = FlutterDevice( + FakeDevice(), + buildInfo: BuildInfo(buildMode, '', treeShakeIcons: false), + generator: FakeResidentCompiler(), + targetPlatform: web + ? TargetPlatform.web_javascript + : TargetPlatform.android_arm, + ); + device.vmService = FakeVmServiceHost(requests: requests).vmService; + final FakeResidentRunner residentRunner = FakeResidentRunner(device, testLogger, fileSystem) + ..supportsServiceProtocol = supportsServiceProtocol + ..supportsRestart = supportsRestart + ..canHotReload = supportsHotReload + ..fatalReloadError = fatalReloadError + ..reloadExitCode = reloadExitCode; + + switch (buildMode) { + case BuildMode.debug: + residentRunner + ..isRunningDebug = true + ..isRunningProfile = false + ..isRunningRelease = false; + break; + case BuildMode.profile: + residentRunner + ..isRunningDebug = false + ..isRunningProfile = true + ..isRunningRelease = false; + break; + case BuildMode.release: + residentRunner + ..isRunningDebug = false + ..isRunningProfile = false + ..isRunningRelease = true; + break; + } + return TerminalHandler( + residentRunner, + logger: testLogger, + signals: signals, + terminal: terminal, + processInfo: processInfo, + reportReady: false, + ); +} + class FakeResidentCompiler extends Fake implements ResidentCompiler {} class TestRunner extends Fake implements ResidentRunner {