| // 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: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/compile.dart'; |
| import 'package:flutter_tools/src/device.dart'; |
| import 'package:flutter_tools/src/resident_runner.dart'; |
| import 'package:mockito/mockito.dart'; |
| |
| import '../src/common.dart'; |
| |
| 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 TerminalHandler terminalHandler = TerminalHandler( |
| testRunner, |
| logger: logger, |
| signals: signals, |
| terminal: terminal, |
| ); |
| |
| 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 TerminalHandler terminalHandler = TerminalHandler( |
| testRunner, |
| logger: logger, |
| signals: signals, |
| terminal: terminal, |
| ); |
| |
| expect(testRunner.hasHelpBeenPrinted, false); |
| await terminalHandler.processTerminalInput('\nh\n'); |
| expect(testRunner.hasHelpBeenPrinted, true); |
| }); |
| |
| 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(); |
| mockResidentRunner = MockResidentRunner(); |
| terminalHandler = TerminalHandler( |
| mockResidentRunner, |
| logger: testLogger, |
| signals: signals, |
| terminal: terminal, |
| ); |
| when(mockResidentRunner.supportsServiceProtocol).thenReturn(true); |
| }); |
| |
| testWithoutContext('a, can handle trailing newlines', () async { |
| await terminalHandler.processTerminalInput('a\n'); |
| |
| expect(terminalHandler.lastReceivedCommand, 'a'); |
| }); |
| |
| testWithoutContext('n, can handle trailing only newlines', () async { |
| await terminalHandler.processTerminalInput('\n\n'); |
| |
| expect(terminalHandler.lastReceivedCommand, ''); |
| }); |
| |
| testWithoutContext('a - debugToggleProfileWidgetBuilds with service protocol', () async { |
| await terminalHandler.processTerminalInput('a'); |
| |
| verify(mockResidentRunner.debugToggleProfileWidgetBuilds()).called(1); |
| }); |
| |
| testWithoutContext('a - debugToggleProfileWidgetBuilds', () async { |
| when(mockResidentRunner.supportsServiceProtocol).thenReturn(true); |
| await terminalHandler.processTerminalInput('a'); |
| |
| verify(mockResidentRunner.debugToggleProfileWidgetBuilds()).called(1); |
| }); |
| |
| testWithoutContext('b - debugToggleBrightness', () async { |
| when(mockResidentRunner.supportsServiceProtocol).thenReturn(true); |
| await terminalHandler.processTerminalInput('b'); |
| |
| verify(mockResidentRunner.debugToggleBrightness()).called(1); |
| }); |
| |
| testWithoutContext('d,D - detach', () async { |
| await terminalHandler.processTerminalInput('d'); |
| await terminalHandler.processTerminalInput('D'); |
| |
| verify(mockResidentRunner.detach()).called(2); |
| }); |
| |
| testWithoutContext('h,H,? - printHelp', () async { |
| await terminalHandler.processTerminalInput('h'); |
| await terminalHandler.processTerminalInput('H'); |
| await terminalHandler.processTerminalInput('?'); |
| |
| verify(mockResidentRunner.printHelp(details: true)).called(3); |
| }); |
| |
| testWithoutContext('i - debugToggleWidgetInspector with service protocol', () async { |
| await terminalHandler.processTerminalInput('i'); |
| |
| verify(mockResidentRunner.debugToggleWidgetInspector()).called(1); |
| }); |
| |
| testWithoutContext('I - debugToggleInvertOversizedImages with service protocol/debug', () async { |
| when(mockResidentRunner.isRunningDebug).thenReturn(true); |
| await terminalHandler.processTerminalInput('I'); |
| |
| verify(mockResidentRunner.debugToggleInvertOversizedImages()).called(1); |
| }); |
| |
| testWithoutContext('L - debugDumpLayerTree with service protocol', () async { |
| await terminalHandler.processTerminalInput('L'); |
| |
| verify(mockResidentRunner.debugDumpLayerTree()).called(1); |
| }); |
| |
| testWithoutContext('o,O - debugTogglePlatform with service protocol and debug mode', () async { |
| when(mockResidentRunner.isRunningDebug).thenReturn(true); |
| await terminalHandler.processTerminalInput('o'); |
| await terminalHandler.processTerminalInput('O'); |
| |
| verify(mockResidentRunner.debugTogglePlatform()).called(2); |
| }); |
| |
| testWithoutContext('p - debugToggleDebugPaintSizeEnabled with service protocol and debug mode', () async { |
| when(mockResidentRunner.isRunningDebug).thenReturn(true); |
| await terminalHandler.processTerminalInput('p'); |
| |
| verify(mockResidentRunner.debugToggleDebugPaintSizeEnabled()).called(1); |
| }); |
| |
| testWithoutContext('p - debugToggleDebugPaintSizeEnabled with service protocol and debug mode', () async { |
| when(mockResidentRunner.isRunningDebug).thenReturn(true); |
| await terminalHandler.processTerminalInput('p'); |
| |
| verify(mockResidentRunner.debugToggleDebugPaintSizeEnabled()).called(1); |
| }); |
| |
| testWithoutContext('P - debugTogglePerformanceOverlayOverride with service protocol', () async { |
| await terminalHandler.processTerminalInput('P'); |
| |
| verify(mockResidentRunner.debugTogglePerformanceOverlayOverride()).called(1); |
| }); |
| |
| testWithoutContext('q,Q - exit', () async { |
| await terminalHandler.processTerminalInput('q'); |
| await terminalHandler.processTerminalInput('Q'); |
| |
| verify(mockResidentRunner.exit()).called(2); |
| }); |
| |
| testWithoutContext('s - screenshot', () async { |
| final MockDevice mockDevice = MockDevice(); |
| final MockFlutterDevice mockFlutterDevice = MockFlutterDevice(); |
| when(mockResidentRunner.isRunningDebug).thenReturn(true); |
| when(mockResidentRunner.flutterDevices).thenReturn(<FlutterDevice>[mockFlutterDevice]); |
| when(mockFlutterDevice.device).thenReturn(mockDevice); |
| when(mockDevice.supportsScreenshot).thenReturn(true); |
| |
| 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.canHotRestart).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.canHotRestart).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.canHotRestart).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.canHotRestart).thenReturn(false); |
| await terminalHandler.processTerminalInput('R'); |
| |
| verifyNever(mockResidentRunner.restart(fullRestart: true)); |
| }); |
| |
| testWithoutContext('S - debugDumpSemanticsTreeInTraversalOrder with service protocol', () async { |
| await terminalHandler.processTerminalInput('S'); |
| |
| verify(mockResidentRunner.debugDumpSemanticsTreeInTraversalOrder()).called(1); |
| }); |
| |
| testWithoutContext('t,T - debugDumpRenderTree with service protocol', () async { |
| await terminalHandler.processTerminalInput('t'); |
| await terminalHandler.processTerminalInput('T'); |
| |
| verify(mockResidentRunner.debugDumpRenderTree()).called(2); |
| }); |
| |
| testWithoutContext('U - debugDumpRenderTree with service protocol', () async { |
| await terminalHandler.processTerminalInput('U'); |
| |
| verify(mockResidentRunner.debugDumpSemanticsTreeInInverseHitTestOrder()).called(1); |
| }); |
| |
| testWithoutContext('v - launchDevTools', () async { |
| when(mockResidentRunner.supportsServiceProtocol).thenReturn(true); |
| await terminalHandler.processTerminalInput('v'); |
| |
| verify(mockResidentRunner.launchDevTools()).called(1); |
| }); |
| |
| testWithoutContext('w,W - debugDumpApp with service protocol', () async { |
| await terminalHandler.processTerminalInput('w'); |
| await terminalHandler.processTerminalInput('W'); |
| |
| verify(mockResidentRunner.debugDumpApp()).called(2); |
| }); |
| |
| testWithoutContext('z,Z - debugToggleDebugCheckElevationsEnabled with service protocol', () async { |
| 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); |
| 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); |
| }); |
| }); |
| } |
| |
| class MockDevice extends Mock implements Device { |
| MockDevice() { |
| when(isSupported()).thenReturn(true); |
| } |
| } |
| |
| class MockResidentRunner extends Mock implements ResidentRunner {} |
| class MockFlutterDevice extends Mock implements FlutterDevice {} |
| class MockResidentCompiler extends Mock implements ResidentCompiler {} |
| |
| class TestRunner extends Mock implements ResidentRunner { |
| bool hasHelpBeenPrinted = false; |
| String receivedCommand; |
| |
| @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, |
| String route, |
| }) async => null; |
| |
| @override |
| Future<int> attach({ |
| Completer<DebugConnectionInfo> connectionInfoCompleter, |
| Completer<void> appStartedCompleter, |
| }) async => null; |
| } |