mirror of
https://github.com/flutter/flutter
synced 2024-10-13 19:52:53 +00:00
430 lines
16 KiB
Dart
430 lines
16 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
// @dart = 2.8
|
|
|
|
import 'dart:async';
|
|
|
|
import 'package:file/memory.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/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 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', () {
|
|
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 {
|
|
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('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);
|
|
});
|
|
});
|
|
|
|
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 ResidentRunner mockResidentRunner = MockResidentRunner();
|
|
when(mockResidentRunner.stayResident).thenReturn(true);
|
|
when(mockResidentRunner.supportsServiceProtocol).thenReturn(true);
|
|
when(mockResidentRunner.supportsRestart).thenReturn(true);
|
|
const String filename = 'test.pid';
|
|
final TerminalHandler terminalHandler = TerminalHandler(
|
|
mockResidentRunner,
|
|
logger: testLogger,
|
|
signals: signals,
|
|
terminal: terminal,
|
|
processInfo: processInfo,
|
|
reportReady: false,
|
|
pidFile: filename,
|
|
);
|
|
expect(fs.file(filename).existsSync(), isFalse);
|
|
terminalHandler.setupTerminal();
|
|
terminalHandler.registerSignalHandlers();
|
|
expect(fs.file(filename).existsSync(), isTrue);
|
|
terminalHandler.stop();
|
|
expect(fs.file(filename).existsSync(), isFalse);
|
|
});
|
|
}
|
|
|
|
class MockDevice extends Mock implements Device {
|
|
MockDevice() {
|
|
when(isSupported()).thenReturn(true);
|
|
}
|
|
}
|
|
|
|
class MockResidentRunner extends Mock implements ResidentRunner {}
|
|
class MockFlutterDevice extends Mock implements FlutterDevice {}
|
|
class FakeResidentCompiler extends Fake implements ResidentCompiler {}
|
|
|
|
class TestRunner extends Fake 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,
|
|
bool enableDevTools = false,
|
|
String route,
|
|
}) async => null;
|
|
|
|
@override
|
|
Future<int> attach({
|
|
Completer<DebugConnectionInfo> connectionInfoCompleter,
|
|
Completer<void> appStartedCompleter,
|
|
bool allowExistingDdsInstance = false,
|
|
bool enableDevTools = false,
|
|
}) 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>();
|
|
}
|