blob: 57c1395d096c596733a69625e72a0d8685ca8acc [file] [log] [blame]
Ian Hickson449f4a62019-11-27 15:04:02 -08001// Copyright 2014 The Flutter Authors. All rights reserved.
Jonah Williamsadf45d12019-07-09 13:10:26 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'dart:async';
Alexandre Ardhuin980f14e2019-11-24 06:54:43 +01006
Jonah Williamsadf45d12019-07-09 13:10:26 -07007import 'package:flutter_tools/src/build_info.dart';
8import 'package:flutter_tools/src/device.dart';
9import 'package:flutter_tools/src/resident_runner.dart';
Jonah Williams7b150f82019-07-12 08:48:14 -070010import 'package:flutter_tools/src/vmservice.dart';
Jonah Williamsadf45d12019-07-09 13:10:26 -070011import 'package:mockito/mockito.dart';
12
Ian Hicksond919e692019-07-13 11:51:44 -070013import '../src/common.dart';
14import '../src/context.dart';
Jonah Williamsadf45d12019-07-09 13:10:26 -070015
16void main() {
17 TestRunner createTestRunner() {
18 // TODO(jacobr): make these tests run with `trackWidgetCreation: true` as
19 // well as the default flags.
20 return TestRunner(
21 <FlutterDevice>[FlutterDevice(MockDevice(), trackWidgetCreation: false, buildMode: BuildMode.debug)],
22 );
23 }
24
25 group('keyboard input handling', () {
26 testUsingContext('single help character', () async {
27 final TestRunner testRunner = createTestRunner();
28 final TerminalHandler terminalHandler = TerminalHandler(testRunner);
Jonah Williams7b150f82019-07-12 08:48:14 -070029 expect(testRunner.hasHelpBeenPrinted, false);
Jonah Williamsadf45d12019-07-09 13:10:26 -070030 await terminalHandler.processTerminalInput('h');
Jonah Williams7b150f82019-07-12 08:48:14 -070031 expect(testRunner.hasHelpBeenPrinted, true);
Jonah Williamsadf45d12019-07-09 13:10:26 -070032 });
33
34 testUsingContext('help character surrounded with newlines', () async {
35 final TestRunner testRunner = createTestRunner();
36 final TerminalHandler terminalHandler = TerminalHandler(testRunner);
Jonah Williams7b150f82019-07-12 08:48:14 -070037 expect(testRunner.hasHelpBeenPrinted, false);
Jonah Williamsadf45d12019-07-09 13:10:26 -070038 await terminalHandler.processTerminalInput('\nh\n');
Jonah Williams7b150f82019-07-12 08:48:14 -070039 expect(testRunner.hasHelpBeenPrinted, true);
Jonah Williamsadf45d12019-07-09 13:10:26 -070040 });
41 });
42
Jonah Williams7b150f82019-07-12 08:48:14 -070043 group('keycode verification, brought to you by the letter', () {
Jonah Williamsadf45d12019-07-09 13:10:26 -070044 MockResidentRunner mockResidentRunner;
45 TerminalHandler terminalHandler;
46
47 setUp(() {
48 mockResidentRunner = MockResidentRunner();
49 terminalHandler = TerminalHandler(mockResidentRunner);
50 when(mockResidentRunner.supportsServiceProtocol).thenReturn(true);
Jonah Williams7b150f82019-07-12 08:48:14 -070051 });
52
53 testUsingContext('a, can handle trailing newlines', () async {
54 await terminalHandler.processTerminalInput('a\n');
55
56 expect(terminalHandler.lastReceivedCommand, 'a');
57 });
58
59 testUsingContext('n, can handle trailing only newlines', () async {
60 await terminalHandler.processTerminalInput('\n\n');
61
62 expect(terminalHandler.lastReceivedCommand, '');
Jonah Williamsadf45d12019-07-09 13:10:26 -070063 });
64
65 testUsingContext('a - debugToggleProfileWidgetBuilds with service protocol', () async {
66 await terminalHandler.processTerminalInput('a');
67
68 verify(mockResidentRunner.debugToggleProfileWidgetBuilds()).called(1);
69 });
70
71 testUsingContext('a - debugToggleProfileWidgetBuilds without service protocol', () async {
Alexandre Ardhuinb8731622019-09-24 21:03:37 +020072 when(mockResidentRunner.supportsServiceProtocol).thenReturn(false);
Jonah Williamsadf45d12019-07-09 13:10:26 -070073 await terminalHandler.processTerminalInput('a');
74
75 verifyNever(mockResidentRunner.debugToggleProfileWidgetBuilds());
76 });
77
78
79 testUsingContext('a - debugToggleProfileWidgetBuilds', () async {
80 when(mockResidentRunner.supportsServiceProtocol).thenReturn(true);
81 await terminalHandler.processTerminalInput('a');
82
83 verify(mockResidentRunner.debugToggleProfileWidgetBuilds()).called(1);
84 });
85
86 testUsingContext('d,D - detach', () async {
87 await terminalHandler.processTerminalInput('d');
88 await terminalHandler.processTerminalInput('D');
89
90 verify(mockResidentRunner.detach()).called(2);
91 });
92
93 testUsingContext('h,H,? - printHelp', () async {
94 await terminalHandler.processTerminalInput('h');
95 await terminalHandler.processTerminalInput('H');
96 await terminalHandler.processTerminalInput('?');
97
98 verify(mockResidentRunner.printHelp(details: true)).called(3);
99 });
100
101 testUsingContext('i, I - debugToggleWidgetInspector with service protocol', () async {
102 await terminalHandler.processTerminalInput('i');
103 await terminalHandler.processTerminalInput('I');
104
105 verify(mockResidentRunner.debugToggleWidgetInspector()).called(2);
106 });
107
108 testUsingContext('i, I - debugToggleWidgetInspector without service protocol', () async {
109 when(mockResidentRunner.supportsServiceProtocol).thenReturn(false);
110 await terminalHandler.processTerminalInput('i');
111 await terminalHandler.processTerminalInput('I');
112
113 verifyNever(mockResidentRunner.debugToggleWidgetInspector());
114 });
115
Jonah Williams7b150f82019-07-12 08:48:14 -0700116 testUsingContext('l - list flutter views', () async {
117 final MockFlutterDevice mockFlutterDevice = MockFlutterDevice();
118 when(mockResidentRunner.isRunningDebug).thenReturn(true);
119 when(mockResidentRunner.flutterDevices).thenReturn(<FlutterDevice>[mockFlutterDevice]);
120 when(mockFlutterDevice.views).thenReturn(<FlutterView>[]);
121
122 await terminalHandler.processTerminalInput('l');
123
Alexandre Ardhuin980f14e2019-11-24 06:54:43 +0100124 expect(testLogger.statusText, contains('Connected views:\n'));
Jonah Williams7b150f82019-07-12 08:48:14 -0700125 });
126
Jonah Williamsadf45d12019-07-09 13:10:26 -0700127 testUsingContext('L - debugDumpLayerTree with service protocol', () async {
128 await terminalHandler.processTerminalInput('L');
129
130 verify(mockResidentRunner.debugDumpLayerTree()).called(1);
131 });
132
133 testUsingContext('L - debugDumpLayerTree without service protocol', () async {
134 when(mockResidentRunner.supportsServiceProtocol).thenReturn(false);
135 await terminalHandler.processTerminalInput('L');
136
137 verifyNever(mockResidentRunner.debugDumpLayerTree());
138 });
139
140 testUsingContext('o,O - debugTogglePlatform with service protocol and debug mode', () async {
141 when(mockResidentRunner.isRunningDebug).thenReturn(true);
142 await terminalHandler.processTerminalInput('o');
143 await terminalHandler.processTerminalInput('O');
144
145 verify(mockResidentRunner.debugTogglePlatform()).called(2);
146 });
147
148 testUsingContext('o,O - debugTogglePlatform without service protocol', () async {
149 when(mockResidentRunner.supportsServiceProtocol).thenReturn(false);
150 when(mockResidentRunner.isRunningDebug).thenReturn(true);
151 await terminalHandler.processTerminalInput('o');
152 await terminalHandler.processTerminalInput('O');
153
154 verifyNever(mockResidentRunner.debugTogglePlatform());
155 });
156
157 testUsingContext('p - debugToggleDebugPaintSizeEnabled with service protocol and debug mode', () async {
158 when(mockResidentRunner.isRunningDebug).thenReturn(true);
159 await terminalHandler.processTerminalInput('p');
160
161 verify(mockResidentRunner.debugToggleDebugPaintSizeEnabled()).called(1);
162 });
163
164 testUsingContext('p - debugTogglePlatform without service protocol', () async {
165 when(mockResidentRunner.supportsServiceProtocol).thenReturn(false);
166 when(mockResidentRunner.isRunningDebug).thenReturn(true);
167 await terminalHandler.processTerminalInput('p');
168
169 verifyNever(mockResidentRunner.debugToggleDebugPaintSizeEnabled());
170 });
171
172 testUsingContext('p - debugToggleDebugPaintSizeEnabled with service protocol and debug mode', () async {
173 when(mockResidentRunner.isRunningDebug).thenReturn(true);
174 await terminalHandler.processTerminalInput('p');
175
176 verify(mockResidentRunner.debugToggleDebugPaintSizeEnabled()).called(1);
177 });
178
179 testUsingContext('p - debugTogglePlatform without service protocol', () async {
180 when(mockResidentRunner.supportsServiceProtocol).thenReturn(false);
181 when(mockResidentRunner.isRunningDebug).thenReturn(true);
182 await terminalHandler.processTerminalInput('p');
183
184 verifyNever(mockResidentRunner.debugToggleDebugPaintSizeEnabled());
185 });
186
187 testUsingContext('P - debugTogglePerformanceOverlayOverride with service protocol', () async {
188 await terminalHandler.processTerminalInput('P');
189
190 verify(mockResidentRunner.debugTogglePerformanceOverlayOverride()).called(1);
191 });
192
193 testUsingContext('P - debugTogglePerformanceOverlayOverride without service protocol', () async {
194 when(mockResidentRunner.supportsServiceProtocol).thenReturn(false);
195 await terminalHandler.processTerminalInput('P');
196
197 verifyNever(mockResidentRunner.debugTogglePerformanceOverlayOverride());
198 });
199
200 testUsingContext('q,Q - exit', () async {
201 await terminalHandler.processTerminalInput('q');
202 await terminalHandler.processTerminalInput('Q');
203
204 verify(mockResidentRunner.exit()).called(2);
205 });
206
207 testUsingContext('s - screenshot', () async {
208 final MockDevice mockDevice = MockDevice();
209 final MockFlutterDevice mockFlutterDevice = MockFlutterDevice();
210 when(mockResidentRunner.isRunningDebug).thenReturn(true);
211 when(mockResidentRunner.flutterDevices).thenReturn(<FlutterDevice>[mockFlutterDevice]);
212 when(mockFlutterDevice.device).thenReturn(mockDevice);
213 when(mockDevice.supportsScreenshot).thenReturn(true);
214
215 await terminalHandler.processTerminalInput('s');
216
217 verify(mockResidentRunner.screenshot(mockFlutterDevice)).called(1);
218 });
219
Jonah Williams7b150f82019-07-12 08:48:14 -0700220 testUsingContext('r - hotReload supported and succeeds', () async {
221 when(mockResidentRunner.canHotReload).thenReturn(true);
222 when(mockResidentRunner.restart(fullRestart: false))
223 .thenAnswer((Invocation invocation) async {
224 return OperationResult(0, '');
225 });
226 await terminalHandler.processTerminalInput('r');
227
228 verify(mockResidentRunner.restart(fullRestart: false)).called(1);
229 });
230
231 testUsingContext('r - hotReload supported and fails', () async {
232 when(mockResidentRunner.canHotReload).thenReturn(true);
233 when(mockResidentRunner.restart(fullRestart: false))
234 .thenAnswer((Invocation invocation) async {
235 return OperationResult(1, '');
236 });
237 await terminalHandler.processTerminalInput('r');
238
239 verify(mockResidentRunner.restart(fullRestart: false)).called(1);
240
Alexandre Ardhuin980f14e2019-11-24 06:54:43 +0100241 expect(testLogger.statusText, contains('Try again after fixing the above error(s).'));
Jonah Williams7b150f82019-07-12 08:48:14 -0700242 });
243
Jonah Williamse3ee5c62019-07-13 16:02:09 -0700244 testUsingContext('r - hotReload supported and fails fatally', () async {
245 when(mockResidentRunner.canHotReload).thenReturn(true);
246 when(mockResidentRunner.hotMode).thenReturn(true);
247 when(mockResidentRunner.restart(fullRestart: false))
248 .thenAnswer((Invocation invocation) async {
249 return OperationResult(1, 'fail', fatal: true);
250 });
Dan Field8b299332020-01-27 14:36:02 -0800251 expect(terminalHandler.processTerminalInput('r'), throwsToolExit());
Jonah Williamse3ee5c62019-07-13 16:02:09 -0700252 });
253
Jonah Williams7b150f82019-07-12 08:48:14 -0700254 testUsingContext('r - hotReload unsupported', () async {
255 when(mockResidentRunner.canHotReload).thenReturn(false);
256 await terminalHandler.processTerminalInput('r');
257
258 verifyNever(mockResidentRunner.restart(fullRestart: false));
259 });
260
261 testUsingContext('R - hotRestart supported and succeeds', () async {
262 when(mockResidentRunner.canHotRestart).thenReturn(true);
263 when(mockResidentRunner.hotMode).thenReturn(true);
264 when(mockResidentRunner.restart(fullRestart: true))
265 .thenAnswer((Invocation invocation) async {
266 return OperationResult(0, '');
267 });
268 await terminalHandler.processTerminalInput('R');
269
270 verify(mockResidentRunner.restart(fullRestart: true)).called(1);
271 });
272
273 testUsingContext('R - hotRestart supported and fails', () async {
274 when(mockResidentRunner.canHotRestart).thenReturn(true);
275 when(mockResidentRunner.hotMode).thenReturn(true);
276 when(mockResidentRunner.restart(fullRestart: true))
277 .thenAnswer((Invocation invocation) async {
278 return OperationResult(1, 'fail');
279 });
280 await terminalHandler.processTerminalInput('R');
281
282 verify(mockResidentRunner.restart(fullRestart: true)).called(1);
283
Alexandre Ardhuin980f14e2019-11-24 06:54:43 +0100284 expect(testLogger.statusText, contains('Try again after fixing the above error(s).'));
Jonah Williams7b150f82019-07-12 08:48:14 -0700285 });
286
Jonah Williamse3ee5c62019-07-13 16:02:09 -0700287 testUsingContext('R - hotRestart supported and fails fatally', () async {
288 when(mockResidentRunner.canHotRestart).thenReturn(true);
289 when(mockResidentRunner.hotMode).thenReturn(true);
290 when(mockResidentRunner.restart(fullRestart: true))
291 .thenAnswer((Invocation invocation) async {
292 return OperationResult(1, 'fail', fatal: true);
293 });
Dan Field8b299332020-01-27 14:36:02 -0800294 expect(() => terminalHandler.processTerminalInput('R'), throwsToolExit());
Jonah Williamse3ee5c62019-07-13 16:02:09 -0700295 });
296
Jonah Williams7b150f82019-07-12 08:48:14 -0700297 testUsingContext('R - hot restart unsupported', () async {
298 when(mockResidentRunner.canHotRestart).thenReturn(false);
299 await terminalHandler.processTerminalInput('R');
300
301 verifyNever(mockResidentRunner.restart(fullRestart: true));
302 });
303
Jonah Williamsadf45d12019-07-09 13:10:26 -0700304 testUsingContext('S - debugDumpSemanticsTreeInTraversalOrder with service protocol', () async {
305 await terminalHandler.processTerminalInput('S');
306
307 verify(mockResidentRunner.debugDumpSemanticsTreeInTraversalOrder()).called(1);
308 });
309
310 testUsingContext('S - debugDumpSemanticsTreeInTraversalOrder without service protocol', () async {
311 when(mockResidentRunner.supportsServiceProtocol).thenReturn(false);
312 await terminalHandler.processTerminalInput('S');
313
314 verifyNever(mockResidentRunner.debugDumpSemanticsTreeInTraversalOrder());
315 });
316
317 testUsingContext('t,T - debugDumpRenderTree with service protocol', () async {
318 await terminalHandler.processTerminalInput('t');
319 await terminalHandler.processTerminalInput('T');
320
321 verify(mockResidentRunner.debugDumpRenderTree()).called(2);
322 });
323
324 testUsingContext('t,T - debugDumpSemanticsTreeInTraversalOrder without service protocol', () async {
325 when(mockResidentRunner.supportsServiceProtocol).thenReturn(false);
326 await terminalHandler.processTerminalInput('t');
327 await terminalHandler.processTerminalInput('T');
328
329 verifyNever(mockResidentRunner.debugDumpRenderTree());
330 });
331
332 testUsingContext('U - debugDumpRenderTree with service protocol', () async {
333 await terminalHandler.processTerminalInput('U');
334
335 verify(mockResidentRunner.debugDumpSemanticsTreeInInverseHitTestOrder()).called(1);
336 });
337
338 testUsingContext('U - debugDumpSemanticsTreeInTraversalOrder without service protocol', () async {
339 when(mockResidentRunner.supportsServiceProtocol).thenReturn(false);
340 await terminalHandler.processTerminalInput('U');
341
342 verifyNever(mockResidentRunner.debugDumpSemanticsTreeInInverseHitTestOrder());
343 });
344
345 testUsingContext('w,W - debugDumpApp with service protocol', () async {
346 await terminalHandler.processTerminalInput('w');
347 await terminalHandler.processTerminalInput('W');
348
349 verify(mockResidentRunner.debugDumpApp()).called(2);
350 });
351
352 testUsingContext('w,W - debugDumpApp without service protocol', () async {
353 when(mockResidentRunner.supportsServiceProtocol).thenReturn(false);
354 await terminalHandler.processTerminalInput('w');
355 await terminalHandler.processTerminalInput('W');
356
357 verifyNever(mockResidentRunner.debugDumpApp());
358 });
359
360 testUsingContext('z,Z - debugToggleDebugCheckElevationsEnabled with service protocol', () async {
361 await terminalHandler.processTerminalInput('z');
362 await terminalHandler.processTerminalInput('Z');
363
364 verify(mockResidentRunner.debugToggleDebugCheckElevationsEnabled()).called(2);
365 });
366
367 testUsingContext('z,Z - debugToggleDebugCheckElevationsEnabled without service protocol', () async {
368 when(mockResidentRunner.supportsServiceProtocol).thenReturn(false);
369 await terminalHandler.processTerminalInput('z');
370 await terminalHandler.processTerminalInput('Z');
371
372 // This should probably be disable when the service protocol is not enabled.
373 verify(mockResidentRunner.debugToggleDebugCheckElevationsEnabled()).called(2);
374 });
375 });
376}
377
378class MockDevice extends Mock implements Device {
379 MockDevice() {
380 when(isSupported()).thenReturn(true);
381 }
382}
383
384class MockResidentRunner extends Mock implements ResidentRunner {}
385
386class MockFlutterDevice extends Mock implements FlutterDevice {}
387
388class TestRunner extends ResidentRunner {
389 TestRunner(List<FlutterDevice> devices)
390 : super(devices);
391
392 bool hasHelpBeenPrinted = false;
393 String receivedCommand;
394
395 @override
396 Future<void> cleanupAfterSignal() async { }
397
398 @override
399 Future<void> cleanupAtFinish() async { }
400
401 @override
Jonah Williamsadf45d12019-07-09 13:10:26 -0700402 void printHelp({ bool details }) {
403 hasHelpBeenPrinted = true;
404 }
405
406 @override
407 Future<int> run({
408 Completer<DebugConnectionInfo> connectionInfoCompleter,
409 Completer<void> appStartedCompleter,
410 String route,
Jonah Williamsadf45d12019-07-09 13:10:26 -0700411 }) async => null;
412
413 @override
414 Future<int> attach({
415 Completer<DebugConnectionInfo> connectionInfoCompleter,
416 Completer<void> appStartedCompleter,
417 }) async => null;
418}