| // 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. | |
| import 'dart:async'; | |
| import 'package:file/file.dart'; | |
| 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/build_system/targets/shader_compiler.dart'; | |
| import 'package:flutter_tools/src/compile.dart'; | |
| import 'package:flutter_tools/src/convert.dart'; | |
| import 'package:flutter_tools/src/devfs.dart'; | |
| import 'package:flutter_tools/src/device.dart'; | |
| import 'package:flutter_tools/src/resident_devtools_handler.dart'; | |
| import 'package:flutter_tools/src/resident_runner.dart'; | |
| import 'package:flutter_tools/src/vmservice.dart'; | |
| import 'package:test/fake.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', | |
| pauseEvent: vm_service.Event( | |
| kind: vm_service.EventKind.kResume, | |
| timestamp: 0 | |
| ), | |
| breakpoints: <vm_service.Breakpoint>[], | |
| extensionRPCs: <String>[], | |
| libraries: <vm_service.LibraryRef>[ | |
| 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: <vm_service.IsolateFlag>[], | |
| ); | |
| final FlutterView fakeFlutterView = FlutterView( | |
| id: 'a', | |
| uiIsolate: fakeUnpausedIsolate, | |
| ); | |
| final FakeVmServiceRequest listViews = FakeVmServiceRequest( | |
| method: kListViewsMethod, | |
| jsonResponse: <String, Object>{ | |
| 'views': <Object>[ | |
| fakeFlutterView.toJson(), | |
| ], | |
| }, | |
| ); | |
| void main() { | |
| testWithoutContext('keyboard input handling single help character', () async { | |
| final TestRunner testRunner = TestRunner(); | |
| final Logger logger = BufferLogger.test(); | |
| final Signals signals = Signals.test(); | |
| final Terminal terminal = Terminal.test(); | |
| final MemoryFileSystem fs = MemoryFileSystem.test(); | |
| final ProcessInfo processInfo = ProcessInfo.test(fs); | |
| final TerminalHandler terminalHandler = TerminalHandler( | |
| testRunner, | |
| logger: logger, | |
| signals: signals, | |
| terminal: terminal, | |
| processInfo: processInfo, | |
| reportReady: false, | |
| ); | |
| expect(testRunner.hasHelpBeenPrinted, false); | |
| await terminalHandler.processTerminalInput('h'); | |
| expect(testRunner.hasHelpBeenPrinted, true); | |
| }); | |
| testWithoutContext('keyboard input handling help character surrounded with newlines', () async { | |
| final TestRunner testRunner = TestRunner(); | |
| final Logger logger = BufferLogger.test(); | |
| final Signals signals = Signals.test(); | |
| final Terminal terminal = Terminal.test(); | |
| final MemoryFileSystem fs = MemoryFileSystem.test(); | |
| final ProcessInfo processInfo = ProcessInfo.test(fs); | |
| final TerminalHandler terminalHandler = TerminalHandler( | |
| testRunner, | |
| logger: logger, | |
| signals: signals, | |
| terminal: terminal, | |
| processInfo: processInfo, | |
| reportReady: false, | |
| ); | |
| expect(testRunner.hasHelpBeenPrinted, false); | |
| await terminalHandler.processTerminalInput('\nh\n'); | |
| expect(testRunner.hasHelpBeenPrinted, true); | |
| }); | |
| group('keycode verification, brought to you by the letter', () { | |
| testWithoutContext('a, can handle trailing newlines', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[], supportsServiceProtocol: false); | |
| await terminalHandler.processTerminalInput('a\n'); | |
| expect(terminalHandler.lastReceivedCommand, 'a'); | |
| }); | |
| testWithoutContext('n, can handle trailing only newlines', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[]); | |
| await terminalHandler.processTerminalInput('\n\n'); | |
| expect(terminalHandler.lastReceivedCommand, ''); | |
| }); | |
| testWithoutContext('a - debugToggleProfileWidgetBuilds', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[ | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.profileWidgetBuilds', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| jsonResponse: <String, Object>{ | |
| 'enabled': 'false', | |
| }, | |
| ), | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.profileWidgetBuilds', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| 'enabled': 'true', | |
| }, | |
| jsonResponse: <String, Object>{ | |
| 'enabled': 'true', | |
| }, | |
| ), | |
| ]); | |
| await terminalHandler.processTerminalInput('a'); | |
| }); | |
| testWithoutContext('a - debugToggleProfileWidgetBuilds with web target', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[ | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.profileWidgetBuilds', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| jsonResponse: <String, Object>{ | |
| 'enabled': 'false', | |
| }, | |
| ), | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.profileWidgetBuilds', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| 'enabled': 'true', | |
| }, | |
| jsonResponse: <String, Object>{ | |
| 'enabled': 'true', | |
| }, | |
| ), | |
| ], web: true); | |
| await terminalHandler.processTerminalInput('a'); | |
| }); | |
| testWithoutContext('j unsupported jank metrics for web', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[], web: true); | |
| await terminalHandler.processTerminalInput('j'); | |
| expect(terminalHandler.logger.warningText.contains('Unable to get jank metrics for web'), true); | |
| }); | |
| testWithoutContext('a - debugToggleProfileWidgetBuilds without service protocol is skipped', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[], supportsServiceProtocol: false); | |
| await terminalHandler.processTerminalInput('a'); | |
| }); | |
| testWithoutContext('b - debugToggleBrightness', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[ | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.brightnessOverride', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| jsonResponse: <String, Object>{ | |
| 'value': 'Brightness.light', | |
| } | |
| ), | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.brightnessOverride', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| 'value': 'Brightness.dark', | |
| }, | |
| jsonResponse: <String, Object>{ | |
| 'value': 'Brightness.dark', | |
| } | |
| ), | |
| ]); | |
| await terminalHandler.processTerminalInput('b'); | |
| expect(terminalHandler.logger.statusText, contains('Changed brightness to Brightness.dark')); | |
| }); | |
| testWithoutContext('b - debugToggleBrightness with web target', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[ | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.brightnessOverride', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| jsonResponse: <String, Object>{ | |
| 'value': 'Brightness.light', | |
| } | |
| ), | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.brightnessOverride', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| 'value': 'Brightness.dark', | |
| }, | |
| jsonResponse: <String, Object>{ | |
| '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(<FakeVmServiceRequest>[], supportsServiceProtocol: false); | |
| await terminalHandler.processTerminalInput('b'); | |
| }); | |
| testWithoutContext('d,D - detach', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[]); | |
| final FakeResidentRunner runner = terminalHandler.residentRunner as FakeResidentRunner; | |
| await terminalHandler.processTerminalInput('d'); | |
| expect(runner.calledDetach, true); | |
| runner.calledDetach = false; | |
| await terminalHandler.processTerminalInput('D'); | |
| expect(runner.calledDetach, true); | |
| }); | |
| testWithoutContext('h,H,? - printHelp', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[]); | |
| 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('?'); | |
| expect(runner.calledPrintWithDetails, true); | |
| }); | |
| testWithoutContext('i - debugToggleWidgetInspector', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[ | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.inspector.show', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| ), | |
| ]); | |
| await terminalHandler.processTerminalInput('i'); | |
| }); | |
| testWithoutContext('i - debugToggleWidgetInspector with web target', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[ | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.inspector.show', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| ), | |
| ], web: true); | |
| await terminalHandler.processTerminalInput('i'); | |
| }); | |
| testWithoutContext('i - debugToggleWidgetInspector without service protocol is skipped', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[], supportsServiceProtocol: false); | |
| await terminalHandler.processTerminalInput('i'); | |
| }); | |
| testWithoutContext('I - debugToggleInvertOversizedImages', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[ | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.invertOversizedImages', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| ), | |
| ]); | |
| await terminalHandler.processTerminalInput('I'); | |
| }); | |
| testWithoutContext('I - debugToggleInvertOversizedImages with web target', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[ | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.invertOversizedImages', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| ), | |
| ], web: true); | |
| await terminalHandler.processTerminalInput('I'); | |
| }); | |
| testWithoutContext('I - debugToggleInvertOversizedImages without service protocol is skipped', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[], supportsServiceProtocol: false); | |
| await terminalHandler.processTerminalInput('I'); | |
| }); | |
| testWithoutContext('I - debugToggleInvertOversizedImages in profile mode is skipped', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[], buildMode: BuildMode.profile); | |
| await terminalHandler.processTerminalInput('I'); | |
| }); | |
| testWithoutContext('L - debugDumpLayerTree', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[ | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.debugDumpLayerTree', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| jsonResponse: <String, Object>{ | |
| 'data': 'LAYER TREE', | |
| } | |
| ), | |
| ]); | |
| await terminalHandler.processTerminalInput('L'); | |
| expect(terminalHandler.logger.statusText, contains('LAYER TREE')); | |
| }); | |
| testWithoutContext('L - debugDumpLayerTree with web target', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[ | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.debugDumpLayerTree', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| jsonResponse: <String, Object>{ | |
| '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(<FakeVmServiceRequest>[], buildMode: BuildMode.profile); | |
| await terminalHandler.processTerminalInput('L'); | |
| }); | |
| testWithoutContext('L - debugDumpLayerTree without service protocol is skipped', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[], supportsServiceProtocol: false); | |
| await terminalHandler.processTerminalInput('L'); | |
| }); | |
| testWithoutContext('f - debugDumpFocusTree', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[ | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.debugDumpFocusTree', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| jsonResponse: <String, Object>{ | |
| 'data': 'FOCUS TREE', | |
| } | |
| ), | |
| ]); | |
| await terminalHandler.processTerminalInput('f'); | |
| expect(terminalHandler.logger.statusText, contains('FOCUS TREE')); | |
| }); | |
| testWithoutContext('f - debugDumpLayerTree with web target', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[ | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.debugDumpFocusTree', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| jsonResponse: <String, Object>{ | |
| 'data': 'FOCUS TREE', | |
| } | |
| ), | |
| ], web: true); | |
| await terminalHandler.processTerminalInput('f'); | |
| expect(terminalHandler.logger.statusText, contains('FOCUS TREE')); | |
| }); | |
| testWithoutContext('f - debugDumpFocusTree with service protocol and profile mode is skipped', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[], buildMode: BuildMode.profile); | |
| await terminalHandler.processTerminalInput('f'); | |
| }); | |
| testWithoutContext('f - debugDumpFocusTree without service protocol is skipped', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[], supportsServiceProtocol: false); | |
| await terminalHandler.processTerminalInput('f'); | |
| }); | |
| testWithoutContext('o,O - debugTogglePlatform', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[ | |
| // Request 1. | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.platformOverride', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| jsonResponse: <String, Object>{ | |
| 'value': 'iOS', | |
| }, | |
| ), | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.platformOverride', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| 'value': 'fuchsia', | |
| }, | |
| jsonResponse: <String, Object>{ | |
| 'value': 'fuchsia', | |
| }, | |
| ), | |
| // Request 2. | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.platformOverride', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| jsonResponse: <String, Object>{ | |
| 'value': 'android', | |
| }, | |
| ), | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.platformOverride', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| 'value': 'iOS', | |
| }, | |
| jsonResponse: <String, Object>{ | |
| 'value': 'iOS', | |
| }, | |
| ), | |
| ]); | |
| 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 with web target', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[ | |
| // Request 1. | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.platformOverride', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| jsonResponse: <String, Object>{ | |
| 'value': 'iOS', | |
| }, | |
| ), | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.platformOverride', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| 'value': 'fuchsia', | |
| }, | |
| jsonResponse: <String, Object>{ | |
| 'value': 'fuchsia', | |
| }, | |
| ), | |
| // Request 2. | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.platformOverride', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| jsonResponse: <String, Object>{ | |
| 'value': 'android', | |
| }, | |
| ), | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.platformOverride', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| 'value': 'iOS', | |
| }, | |
| jsonResponse: <String, Object>{ | |
| '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(<FakeVmServiceRequest>[], supportsServiceProtocol: false); | |
| await terminalHandler.processTerminalInput('o'); | |
| await terminalHandler.processTerminalInput('O'); | |
| }); | |
| testWithoutContext('p - debugToggleDebugPaintSizeEnabled', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[ | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.debugPaint', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| ), | |
| ]); | |
| await terminalHandler.processTerminalInput('p'); | |
| }); | |
| testWithoutContext('p - debugToggleDebugPaintSizeEnabled with web target', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[ | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.debugPaint', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| ), | |
| ], web: true); | |
| await terminalHandler.processTerminalInput('p'); | |
| }); | |
| testWithoutContext('p - debugToggleDebugPaintSizeEnabled without service protocol is skipped', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[], supportsServiceProtocol: false); | |
| await terminalHandler.processTerminalInput('p'); | |
| }); | |
| testWithoutContext('P - debugTogglePerformanceOverlayOverride', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[ | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.showPerformanceOverlay', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| ), | |
| ]); | |
| await terminalHandler.processTerminalInput('P'); | |
| }); | |
| testWithoutContext('P - debugTogglePerformanceOverlayOverride with web target is skipped ', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[], web: true); | |
| await terminalHandler.processTerminalInput('P'); | |
| }); | |
| testWithoutContext('P - debugTogglePerformanceOverlayOverride without service protocol is skipped ', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[], supportsServiceProtocol: false); | |
| await terminalHandler.processTerminalInput('P'); | |
| }); | |
| testWithoutContext('S - debugDumpSemanticsTreeInTraversalOrder', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[ | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.debugDumpSemanticsTreeInTraversalOrder', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| jsonResponse: <String, Object>{ | |
| 'data': 'SEMANTICS DATA', | |
| }, | |
| ), | |
| ]); | |
| await terminalHandler.processTerminalInput('S'); | |
| expect(terminalHandler.logger.statusText, contains('SEMANTICS DATA')); | |
| }); | |
| testWithoutContext('S - debugDumpSemanticsTreeInTraversalOrder with web target', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[ | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.debugDumpSemanticsTreeInTraversalOrder', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| jsonResponse: <String, Object>{ | |
| '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(<FakeVmServiceRequest>[], supportsServiceProtocol: false); | |
| await terminalHandler.processTerminalInput('S'); | |
| }); | |
| testWithoutContext('U - debugDumpSemanticsTreeInInverseHitTestOrder', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[ | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.debugDumpSemanticsTreeInInverseHitTestOrder', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| jsonResponse: <String, Object>{ | |
| '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(<FakeVmServiceRequest>[ | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.debugDumpSemanticsTreeInInverseHitTestOrder', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| jsonResponse: <String, Object>{ | |
| '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(<FakeVmServiceRequest>[], supportsServiceProtocol: false); | |
| await terminalHandler.processTerminalInput('U'); | |
| }); | |
| testWithoutContext('t,T - debugDumpRenderTree', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[ | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.debugDumpRenderTree', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| jsonResponse: <String, Object>{ | |
| 'data': 'RENDER DATA 1', | |
| }, | |
| ), | |
| // Request 2. | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.debugDumpRenderTree', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| jsonResponse: <String, Object>{ | |
| 'data': 'RENDER DATA 2', | |
| }, | |
| ), | |
| ]); | |
| await terminalHandler.processTerminalInput('t'); | |
| await terminalHandler.processTerminalInput('T'); | |
| expect(terminalHandler.logger.statusText, contains('RENDER DATA 1')); | |
| expect(terminalHandler.logger.statusText, contains('RENDER DATA 2')); | |
| }); | |
| testWithoutContext('t,T - debugDumpRenderTree with web target', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[ | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.debugDumpRenderTree', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| jsonResponse: <String, Object>{ | |
| 'data': 'RENDER DATA 1', | |
| }, | |
| ), | |
| // Request 2. | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.debugDumpRenderTree', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| jsonResponse: <String, Object>{ | |
| 'data': 'RENDER DATA 2', | |
| }, | |
| ), | |
| ], web: true); | |
| await terminalHandler.processTerminalInput('t'); | |
| await terminalHandler.processTerminalInput('T'); | |
| expect(terminalHandler.logger.statusText, contains('RENDER DATA 1')); | |
| expect(terminalHandler.logger.statusText, contains('RENDER DATA 2')); | |
| }); | |
| testWithoutContext('t,T - debugDumpRenderTree without service protocol is skipped', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[], supportsServiceProtocol: false); | |
| await terminalHandler.processTerminalInput('t'); | |
| await terminalHandler.processTerminalInput('T'); | |
| }); | |
| testWithoutContext('w,W - debugDumpApp', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[ | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.debugDumpApp', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| jsonResponse: <String, Object>{ | |
| 'data': 'WIDGET DATA 1', | |
| }, | |
| ), | |
| // Request 2. | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.debugDumpApp', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| jsonResponse: <String, Object>{ | |
| 'data': 'WIDGET DATA 2', | |
| }, | |
| ), | |
| ]); | |
| 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 with web target', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[ | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.debugDumpApp', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| jsonResponse: <String, Object>{ | |
| 'data': 'WIDGET DATA 1', | |
| }, | |
| ), | |
| // Request 2. | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.debugDumpApp', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| jsonResponse: <String, Object>{ | |
| '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('v - launchDevToolsInBrowser', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[]); | |
| final FakeResidentRunner runner = terminalHandler.residentRunner as FakeResidentRunner; | |
| final FakeResidentDevtoolsHandler devtoolsHandler = runner.residentDevtoolsHandler as FakeResidentDevtoolsHandler; | |
| expect(devtoolsHandler.calledLaunchDevToolsInBrowser, isFalse); | |
| await terminalHandler.processTerminalInput('v'); | |
| expect(devtoolsHandler.calledLaunchDevToolsInBrowser, isTrue); | |
| }); | |
| testWithoutContext('w,W - debugDumpApp without service protocol is skipped', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[], supportsServiceProtocol: false); | |
| await terminalHandler.processTerminalInput('w'); | |
| await terminalHandler.processTerminalInput('W'); | |
| }); | |
| testWithoutContext('z,Z - debugToggleDebugCheckElevationsEnabled', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[ | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.debugCheckElevationsEnabled', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| ), | |
| // Request 2. | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.debugCheckElevationsEnabled', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| ), | |
| ]); | |
| await terminalHandler.processTerminalInput('z'); | |
| await terminalHandler.processTerminalInput('Z'); | |
| }); | |
| testWithoutContext('z,Z - debugToggleDebugCheckElevationsEnabled with web target', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[ | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.debugCheckElevationsEnabled', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| ), | |
| // Request 2. | |
| listViews, | |
| const FakeVmServiceRequest( | |
| method: 'ext.flutter.debugCheckElevationsEnabled', | |
| args: <String, Object>{ | |
| 'isolateId': '1', | |
| }, | |
| ), | |
| ], web: true); | |
| await terminalHandler.processTerminalInput('z'); | |
| await terminalHandler.processTerminalInput('Z'); | |
| }); | |
| testWithoutContext('z,Z - debugToggleDebugCheckElevationsEnabled without service protocol is skipped', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[], supportsServiceProtocol: false); | |
| await terminalHandler.processTerminalInput('z'); | |
| await terminalHandler.processTerminalInput('Z'); | |
| }); | |
| testWithoutContext('q,Q - exit', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[]); | |
| 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(<FakeVmServiceRequest>[], supportsHotReload: false); | |
| await terminalHandler.processTerminalInput('r'); | |
| }); | |
| testWithoutContext('R - hotRestart unsupported', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[], supportsRestart: false); | |
| await terminalHandler.processTerminalInput('R'); | |
| }); | |
| testWithoutContext('r - hotReload', () async { | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[]); | |
| 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(<FakeVmServiceRequest>[]); | |
| 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(<FakeVmServiceRequest>[], 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(<FakeVmServiceRequest>[], 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(<FakeVmServiceRequest>[], 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(<FakeVmServiceRequest>[], 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(<FakeVmServiceRequest>[], 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('s, can take screenshot on debug device that supports screenshot', () async { | |
| final BufferLogger logger = BufferLogger.test(); | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[ | |
| listViews, | |
| FakeVmServiceRequest( | |
| method: 'ext.flutter.debugAllowBanner', | |
| args: <String, Object?>{ | |
| 'isolateId': fakeUnpausedIsolate.id, | |
| 'enabled': 'false', | |
| }, | |
| ), | |
| FakeVmServiceRequest( | |
| method: 'ext.flutter.debugAllowBanner', | |
| args: <String, Object?>{ | |
| 'isolateId': fakeUnpausedIsolate.id, | |
| 'enabled': 'true', | |
| }, | |
| ), | |
| ], logger: logger, supportsScreenshot: true); | |
| await terminalHandler.processTerminalInput('s'); | |
| expect(logger.statusText, contains('Screenshot written to flutter_01.png (0kB)')); | |
| }); | |
| testWithoutContext('s, can take screenshot on debug device that does not support screenshot', () async { | |
| final BufferLogger logger = BufferLogger.test(); | |
| final FileSystem fileSystem = MemoryFileSystem.test(); | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[ | |
| listViews, | |
| FakeVmServiceRequest( | |
| method: 'ext.flutter.debugAllowBanner', | |
| args: <String, Object?>{ | |
| 'isolateId': fakeUnpausedIsolate.id, | |
| 'enabled': 'false', | |
| }, | |
| ), | |
| FakeVmServiceRequest( | |
| method: '_flutter.screenshot', | |
| args: <String, Object>{}, | |
| jsonResponse: <String, Object>{ | |
| 'screenshot': base64.encode(<int>[1, 2, 3, 4]), | |
| }, | |
| ), | |
| FakeVmServiceRequest( | |
| method: 'ext.flutter.debugAllowBanner', | |
| args: <String, Object?>{ | |
| 'isolateId': fakeUnpausedIsolate.id, | |
| 'enabled': 'true', | |
| }, | |
| ), | |
| ], logger: logger, fileSystem: fileSystem); | |
| await terminalHandler.processTerminalInput('s'); | |
| expect(logger.statusText, contains('Screenshot written to flutter_01.png (0kB)')); | |
| expect(fileSystem.currentDirectory.childFile('flutter_01.png').readAsBytesSync(), <int>[1, 2, 3, 4]); | |
| }); | |
| testWithoutContext('s, can take screenshot on debug web device that does not support screenshot', () async { | |
| final BufferLogger logger = BufferLogger.test(); | |
| final FileSystem fileSystem = MemoryFileSystem.test(); | |
| final TerminalHandler terminalHandler = setUpTerminalHandler(<FakeVmServiceRequest>[ | |
| listViews, | |
| FakeVmServiceRequest( | |
| method: 'ext.flutter.debugAllowBanner', | |
| args: <String, Object?>{ | |
| 'isolateId': fakeUnpausedIsolate.id, | |
| 'enabled': 'false', | |
| }, | |
| ), | |
| FakeVmServiceRequest( | |
| method: 'ext.dwds.screenshot', | |
| args: <String, Object>{}, | |
| jsonResponse: <String, Object>{ | |
| 'data': base64.encode(<int>[1, 2, 3, 4]), | |
| }, | |
| ), | |
| FakeVmServiceRequest( | |
| method: 'ext.flutter.debugAllowBanner', | |
| args: <String, Object?>{ | |
| 'isolateId': fakeUnpausedIsolate.id, | |
| 'enabled': 'true', | |
| }, | |
| ), | |
| ], logger: logger, web: true, fileSystem: fileSystem); | |
| await terminalHandler.processTerminalInput('s'); | |
| expect(logger.statusText, contains('Screenshot written to flutter_01.png (0kB)')); | |
| expect(fileSystem.currentDirectory.childFile('flutter_01.png').readAsBytesSync(), <int>[1, 2, 3, 4]); | |
| }); | |
| testWithoutContext('s, can take screenshot on device that does not support service protocol', () async { | |
| final BufferLogger logger = BufferLogger.test(); | |
| final FileSystem fileSystem = MemoryFileSystem.test(); | |
| final TerminalHandler terminalHandler = setUpTerminalHandler( | |
| <FakeVmServiceRequest>[], | |
| logger: logger, | |
| supportsScreenshot: true, | |
| supportsServiceProtocol: false, | |
| fileSystem: fileSystem, | |
| ); | |
| await terminalHandler.processTerminalInput('s'); | |
| expect(logger.statusText, contains('Screenshot written to flutter_01.png (0kB)')); | |
| expect(fileSystem.currentDirectory.childFile('flutter_01.png').readAsBytesSync(), <int>[1, 2, 3, 4]); | |
| }); | |
| testWithoutContext('s, does not take a screenshot on a device that does not support screenshot or the service protocol', () async { | |
| final BufferLogger logger = BufferLogger.test(); | |
| final FileSystem fileSystem = MemoryFileSystem.test(); | |
| final TerminalHandler terminalHandler = setUpTerminalHandler( | |
| <FakeVmServiceRequest>[], | |
| logger: logger, | |
| supportsServiceProtocol: false, | |
| fileSystem: fileSystem, | |
| ); | |
| await terminalHandler.processTerminalInput('s'); | |
| expect(logger.statusText, '\n'); | |
| expect(fileSystem.currentDirectory.childFile('flutter_01.png'), isNot(exists)); | |
| }); | |
| testWithoutContext('s, does not take a screenshot on a web device that does not support screenshot or the service protocol', () async { | |
| final BufferLogger logger = BufferLogger.test(); | |
| final FileSystem fileSystem = MemoryFileSystem.test(); | |
| final TerminalHandler terminalHandler = setUpTerminalHandler( | |
| <FakeVmServiceRequest>[], | |
| logger: logger, | |
| supportsServiceProtocol: false, | |
| web: true, | |
| fileSystem: fileSystem, | |
| ); | |
| await terminalHandler.processTerminalInput('s'); | |
| expect(logger.statusText, '\n'); | |
| expect(fileSystem.currentDirectory.childFile('flutter_01.png'), isNot(exists)); | |
| }); | |
| testWithoutContext('s, bails taking screenshot on debug device if debugAllowBanner throws RpcError', () async { | |
| final BufferLogger logger = BufferLogger.test(); | |
| final FileSystem fileSystem = MemoryFileSystem.test(); | |
| final TerminalHandler terminalHandler = setUpTerminalHandler( | |
| <FakeVmServiceRequest>[ | |
| listViews, | |
| FakeVmServiceRequest( | |
| method: 'ext.flutter.debugAllowBanner', | |
| args: <String, Object?>{ | |
| 'isolateId': fakeUnpausedIsolate.id, | |
| 'enabled': 'false', | |
| }, | |
| // Failed response, | |
| errorCode: RPCErrorCodes.kInternalError, | |
| ), | |
| ], | |
| logger: logger, | |
| fileSystem: fileSystem, | |
| ); | |
| await terminalHandler.processTerminalInput('s'); | |
| expect(logger.errorText, contains('Error')); | |
| }); | |
| testWithoutContext('s, bails taking screenshot on debug device if flutter.screenshot throws RpcError, restoring banner', () async { | |
| final BufferLogger logger = BufferLogger.test(); | |
| final FileSystem fileSystem = MemoryFileSystem.test(); | |
| final TerminalHandler terminalHandler = setUpTerminalHandler( | |
| <FakeVmServiceRequest>[ | |
| listViews, | |
| FakeVmServiceRequest( | |
| method: 'ext.flutter.debugAllowBanner', | |
| args: <String, Object?>{ | |
| 'isolateId': fakeUnpausedIsolate.id, | |
| 'enabled': 'false', | |
| }, | |
| ), | |
| const FakeVmServiceRequest( | |
| method: '_flutter.screenshot', | |
| // Failed response, | |
| errorCode: RPCErrorCodes.kInternalError, | |
| ), | |
| FakeVmServiceRequest( | |
| method: 'ext.flutter.debugAllowBanner', | |
| args: <String, Object?>{ | |
| 'isolateId': fakeUnpausedIsolate.id, | |
| 'enabled': 'true', | |
| }, | |
| ), | |
| ], | |
| logger: logger, | |
| fileSystem: fileSystem, | |
| ); | |
| await terminalHandler.processTerminalInput('s'); | |
| expect(logger.errorText, contains('Error')); | |
| }); | |
| testWithoutContext('s, bails taking screenshot on debug device if dwds.screenshot throws RpcError, restoring banner', () async { | |
| final BufferLogger logger = BufferLogger.test(); | |
| final FileSystem fileSystem = MemoryFileSystem.test(); | |
| final TerminalHandler terminalHandler = setUpTerminalHandler( | |
| <FakeVmServiceRequest>[ | |
| listViews, | |
| FakeVmServiceRequest( | |
| method: 'ext.flutter.debugAllowBanner', | |
| args: <String, Object?>{ | |
| 'isolateId': fakeUnpausedIsolate.id, | |
| 'enabled': 'false', | |
| }, | |
| ), | |
| const FakeVmServiceRequest( | |
| method: 'ext.dwds.screenshot', | |
| // Failed response, | |
| errorCode: RPCErrorCodes.kInternalError, | |
| ), | |
| FakeVmServiceRequest( | |
| method: 'ext.flutter.debugAllowBanner', | |
| args: <String, Object?>{ | |
| 'isolateId': fakeUnpausedIsolate.id, | |
| 'enabled': 'true', | |
| }, | |
| ), | |
| ], | |
| logger: logger, | |
| web: true, | |
| fileSystem: fileSystem, | |
| ); | |
| await terminalHandler.processTerminalInput('s'); | |
| expect(logger.errorText, contains('Error')); | |
| }); | |
| testWithoutContext('s, bails taking screenshot on debug device if debugAllowBanner during second request', () async { | |
| final BufferLogger logger = BufferLogger.test(); | |
| final FileSystem fileSystem = MemoryFileSystem.test(); | |
| final TerminalHandler terminalHandler = setUpTerminalHandler( | |
| <FakeVmServiceRequest>[ | |
| listViews, | |
| FakeVmServiceRequest( | |
| method: 'ext.flutter.debugAllowBanner', | |
| args: <String, Object?>{ | |
| 'isolateId': fakeUnpausedIsolate.id, | |
| 'enabled': 'false', | |
| }, | |
| ), | |
| FakeVmServiceRequest( | |
| method: 'ext.flutter.debugAllowBanner', | |
| args: <String, Object?>{ | |
| 'isolateId': fakeUnpausedIsolate.id, | |
| 'enabled': 'true', | |
| }, | |
| // Failed response, | |
| errorCode: RPCErrorCodes.kInternalError, | |
| ), | |
| ], | |
| logger: logger, | |
| supportsScreenshot: true, | |
| fileSystem: fileSystem, | |
| ); | |
| await terminalHandler.processTerminalInput('s'); | |
| expect(logger.errorText, contains('Error')); | |
| }); | |
| testWithoutContext('pidfile creation', () { | |
| final BufferLogger testLogger = BufferLogger.test(); | |
| final Signals signals = _TestSignals(Signals.defaultExitSignals); | |
| final Terminal terminal = Terminal.test(); | |
| final MemoryFileSystem fs = MemoryFileSystem.test(); | |
| final ProcessInfo processInfo = ProcessInfo.test(fs); | |
| final FakeResidentRunner residentRunner = FakeResidentRunner( | |
| FlutterDevice( | |
| FakeDevice(), | |
| buildInfo: BuildInfo.debug, | |
| generator: FakeResidentCompiler(), | |
| developmentShaderCompiler: const FakeShaderCompiler(), | |
| ), | |
| testLogger, | |
| fs, | |
| ); | |
| residentRunner | |
| ..supportsRestart = true | |
| ..supportsServiceProtocol = true | |
| ..stayResident = true; | |
| const String filename = 'test.pid'; | |
| final TerminalHandler terminalHandler = TerminalHandler( | |
| residentRunner, | |
| logger: testLogger, | |
| signals: signals, | |
| terminal: terminal, | |
| processInfo: processInfo, | |
| reportReady: false, | |
| pidFile: filename, | |
| ); | |
| expect(fs.file(filename), isNot(exists)); | |
| terminalHandler.setupTerminal(); | |
| terminalHandler.registerSignalHandlers(); | |
| expect(fs.file(filename), exists); | |
| terminalHandler.stop(); | |
| expect(fs.file(filename), isNot(exists)); | |
| }); | |
| } | |
| class FakeResidentRunner extends ResidentHandlers { | |
| FakeResidentRunner(FlutterDevice device, this.logger, this.fileSystem) : flutterDevices = <FlutterDevice>[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<FlutterDevice> 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<void> cleanupAfterSignal() async { } | |
| @override | |
| Future<void> detach() async { | |
| calledDetach = true; | |
| } | |
| @override | |
| Future<void> exit() async { | |
| calledExit = true; | |
| } | |
| @override | |
| void printHelp({required bool details}) { | |
| if (details) { | |
| calledPrintWithDetails = true; | |
| } else { | |
| calledPrint = true; | |
| } | |
| } | |
| @override | |
| Future<void> runSourceGenerators() async { } | |
| @override | |
| Future<OperationResult> 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); | |
| } | |
| @override | |
| ResidentDevtoolsHandler get residentDevtoolsHandler => _residentDevtoolsHandler; | |
| final ResidentDevtoolsHandler _residentDevtoolsHandler = FakeResidentDevtoolsHandler(); | |
| } | |
| class FakeResidentDevtoolsHandler extends Fake implements ResidentDevtoolsHandler { | |
| bool calledLaunchDevToolsInBrowser = false; | |
| @override | |
| bool launchDevToolsInBrowser({List<FlutterDevice?>? flutterDevices}) { | |
| return calledLaunchDevToolsInBrowser = true; | |
| } | |
| } | |
| // Unfortunately Device, despite not being immutable, has an `operator ==`. | |
| // Until we fix that, we have to also ignore related lints here. | |
| // ignore: avoid_implementing_value_types | |
| class FakeDevice extends Fake implements Device { | |
| @override | |
| bool isSupported() => true; | |
| @override | |
| bool supportsScreenshot = false; | |
| @override | |
| String get name => 'Fake Device'; | |
| @override | |
| Future<void> takeScreenshot(File file) async { | |
| if (!supportsScreenshot) { | |
| throw StateError('illegal screenshot attempt'); | |
| } | |
| file.writeAsBytesSync(<int>[1, 2, 3, 4]); | |
| } | |
| } | |
| TerminalHandler setUpTerminalHandler(List<FakeVmServiceRequest> requests, { | |
| bool supportsRestart = true, | |
| bool supportsServiceProtocol = true, | |
| bool supportsHotReload = true, | |
| bool web = false, | |
| bool fatalReloadError = false, | |
| bool supportsScreenshot = false, | |
| int reloadExitCode = 0, | |
| BuildMode buildMode = BuildMode.debug, | |
| Logger? logger, | |
| FileSystem? fileSystem, | |
| }) { | |
| final Logger testLogger = logger ?? BufferLogger.test(); | |
| final Signals signals = Signals.test(); | |
| final Terminal terminal = Terminal.test(); | |
| final FileSystem localFileSystem = fileSystem ?? MemoryFileSystem.test(); | |
| final ProcessInfo processInfo = ProcessInfo.test(MemoryFileSystem.test()); | |
| final FlutterDevice device = FlutterDevice( | |
| FakeDevice()..supportsScreenshot = supportsScreenshot, | |
| buildInfo: BuildInfo(buildMode, '', treeShakeIcons: false), | |
| generator: FakeResidentCompiler(), | |
| developmentShaderCompiler: const FakeShaderCompiler(), | |
| targetPlatform: web | |
| ? TargetPlatform.web_javascript | |
| : TargetPlatform.android_arm, | |
| ); | |
| device.vmService = FakeVmServiceHost(requests: requests).vmService; | |
| final FakeResidentRunner residentRunner = FakeResidentRunner(device, testLogger, localFileSystem) | |
| ..supportsServiceProtocol = supportsServiceProtocol | |
| ..supportsRestart = supportsRestart | |
| ..canHotReload = supportsHotReload | |
| ..fatalReloadError = fatalReloadError | |
| ..reloadExitCode = reloadExitCode; | |
| switch (buildMode) { | |
| case BuildMode.debug: | |
| residentRunner | |
| ..isRunningDebug = true | |
| ..isRunningProfile = false | |
| ..isRunningRelease = false; | |
| case BuildMode.profile: | |
| residentRunner | |
| ..isRunningDebug = false | |
| ..isRunningProfile = true | |
| ..isRunningRelease = false; | |
| case BuildMode.release: | |
| residentRunner | |
| ..isRunningDebug = false | |
| ..isRunningProfile = false | |
| ..isRunningRelease = true; | |
| case _: | |
| // NOOP | |
| } | |
| 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 { | |
| bool hasHelpBeenPrinted = false; | |
| @override | |
| Future<void> cleanupAfterSignal() async { } | |
| @override | |
| Future<void> cleanupAtFinish() async { } | |
| @override | |
| void printHelp({ bool? details }) { | |
| hasHelpBeenPrinted = true; | |
| } | |
| @override | |
| Future<int?> run({ | |
| Completer<DebugConnectionInfo>? connectionInfoCompleter, | |
| Completer<void>? appStartedCompleter, | |
| bool enableDevTools = false, | |
| String? route, | |
| }) async => null; | |
| @override | |
| Future<int?> attach({ | |
| Completer<DebugConnectionInfo>? connectionInfoCompleter, | |
| Completer<void>? appStartedCompleter, | |
| bool allowExistingDdsInstance = false, | |
| bool enableDevTools = false, | |
| bool needsFullRestart = true, | |
| }) async => null; | |
| } | |
| class _TestSignals implements Signals { | |
| _TestSignals(this.exitSignals); | |
| final List<ProcessSignal> exitSignals; | |
| final Map<ProcessSignal, Map<Object, SignalHandler>> _handlersTable = | |
| <ProcessSignal, Map<Object, SignalHandler>>{}; | |
| @override | |
| Object addHandler(ProcessSignal signal, SignalHandler handler) { | |
| final Object token = Object(); | |
| _handlersTable.putIfAbsent(signal, () => <Object, SignalHandler>{})[token] = handler; | |
| return token; | |
| } | |
| @override | |
| Future<bool> removeHandler(ProcessSignal signal, Object token) async { | |
| if (!_handlersTable.containsKey(signal)) { | |
| return false; | |
| } | |
| if (!_handlersTable[signal]!.containsKey(token)) { | |
| return false; | |
| } | |
| _handlersTable[signal]!.remove(token); | |
| return true; | |
| } | |
| @override | |
| Stream<Object> get errors => _errors.stream; | |
| final StreamController<Object> _errors = StreamController<Object>(); | |
| } | |
| class FakeShaderCompiler implements DevelopmentShaderCompiler { | |
| const FakeShaderCompiler(); | |
| @override | |
| void configureCompiler( | |
| TargetPlatform? platform, { | |
| required ImpellerStatus impellerStatus, | |
| }) { } | |
| @override | |
| Future<DevFSContent> recompileShader(DevFSContent inputShader) { | |
| throw UnimplementedError(); | |
| } | |
| } |