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