blob: ccf816b6988842f84d3969a9711b9e1931109b94 [file] [log] [blame]
Yegord4830fc2017-11-08 10:43:47 -08001// 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
Ian Hickson6bfd9bb2018-11-08 20:54:06 -08005import 'dart:async';
Jonah Williams301eaa82019-04-15 08:59:28 -07006import 'dart:io';
Ian Hickson6bfd9bb2018-11-08 20:54:06 -08007
8import 'package:flutter_tools/src/base/io.dart';
9import 'package:flutter_tools/src/base/logger.dart';
Yegord4830fc2017-11-08 10:43:47 -080010import 'package:flutter_tools/src/vmservice.dart';
Ian Hickson6bfd9bb2018-11-08 20:54:06 -080011import 'package:json_rpc_2/json_rpc_2.dart' as rpc;
12import 'package:quiver/testing/async.dart';
Yegord4830fc2017-11-08 10:43:47 -080013
Ian Hicksond919e692019-07-13 11:51:44 -070014import '../src/common.dart';
15import '../src/context.dart';
16import '../src/mocks.dart';
Ian Hickson6bfd9bb2018-11-08 20:54:06 -080017
18class MockPeer implements rpc.Peer {
Dan Field1db5d662019-04-25 08:27:00 -070019
20 @override
21 rpc.ErrorCallback get onUnhandledError => null;
22
Ian Hickson6bfd9bb2018-11-08 20:54:06 -080023 @override
24 Future<dynamic> get done async {
25 throw 'unexpected call to done';
26 }
27
28 @override
29 bool get isClosed {
30 throw 'unexpected call to isClosed';
31 }
32
33 @override
34 Future<dynamic> close() async {
35 throw 'unexpected call to close()';
36 }
37
38 @override
39 Future<dynamic> listen() async {
40 // this does get called
41 }
42
43 @override
44 void registerFallback(dynamic callback(rpc.Parameters parameters)) {
45 throw 'unexpected call to registerFallback';
46 }
47
48 @override
49 void registerMethod(String name, Function callback) {
50 // this does get called
51 }
52
53 @override
54 void sendNotification(String method, [ dynamic parameters ]) {
55 throw 'unexpected call to sendNotification';
56 }
57
58 bool isolatesEnabled = false;
59
60 Future<void> _getVMLatch;
61 Completer<void> _currentGetVMLatchCompleter;
62
63 void tripGetVMLatch() {
64 final Completer<void> lastCompleter = _currentGetVMLatchCompleter;
65 _currentGetVMLatchCompleter = Completer<void>();
66 _getVMLatch = _currentGetVMLatchCompleter.future;
67 lastCompleter?.complete();
68 }
69
70 int returnedFromSendRequest = 0;
71
72 @override
73 Future<dynamic> sendRequest(String method, [ dynamic parameters ]) async {
74 if (method == 'getVM')
75 await _getVMLatch;
76 await Future<void>.delayed(Duration.zero);
77 returnedFromSendRequest += 1;
78 if (method == 'getVM') {
79 return <String, dynamic>{
80 'type': 'VM',
81 'name': 'vm',
82 'architectureBits': 64,
83 'targetCPU': 'x64',
84 'hostCPU': ' Intel(R) Xeon(R) CPU E5-1650 v2 @ 3.50GHz',
85 'version': '2.1.0-dev.7.1.flutter-45f9462398 (Fri Oct 19 19:27:56 2018 +0000) on "linux_x64"',
86 '_profilerMode': 'Dart',
87 '_nativeZoneMemoryUsage': 0,
88 'pid': 103707,
89 'startTime': 1540426121876,
90 '_embedder': 'Flutter',
91 '_maxRSS': 312614912,
92 '_currentRSS': 33091584,
93 'isolates': isolatesEnabled ? <dynamic>[
94 <String, dynamic>{
95 'type': '@Isolate',
96 'fixedId': true,
97 'id': 'isolates/242098474',
98 'name': 'main.dart:main()',
99 'number': 242098474,
100 },
101 ] : <dynamic>[],
102 };
103 }
104 if (method == 'getIsolate') {
105 return <String, dynamic>{
106 'type': 'Isolate',
107 'fixedId': true,
108 'id': 'isolates/242098474',
109 'name': 'main.dart:main()',
110 'number': 242098474,
111 '_originNumber': 242098474,
112 'startTime': 1540488745340,
113 '_heaps': <String, dynamic>{
114 'new': <String, dynamic>{
115 'used': 0,
116 'capacity': 0,
117 'external': 0,
118 'collections': 0,
119 'time': 0.0,
120 'avgCollectionPeriodMillis': 0.0,
121 },
122 'old': <String, dynamic>{
123 'used': 0,
124 'capacity': 0,
125 'external': 0,
126 'collections': 0,
127 'time': 0.0,
128 'avgCollectionPeriodMillis': 0.0,
129 },
130 },
131 };
132 }
133 if (method == '_flutter.listViews') {
134 return <String, dynamic>{
135 'type': 'FlutterViewList',
136 'views': isolatesEnabled ? <dynamic>[
137 <String, dynamic>{
138 'type': 'FlutterView',
139 'id': '_flutterView/0x4a4c1f8',
140 'isolate': <String, dynamic>{
141 'type': '@Isolate',
142 'fixedId': true,
143 'id': 'isolates/242098474',
144 'name': 'main.dart:main()',
145 'number': 242098474,
146 },
147 },
148 ] : <dynamic>[],
149 };
150 }
151 return null;
152 }
153
154 @override
155 dynamic withBatch(dynamic callback()) {
156 throw 'unexpected call to withBatch';
157 }
158}
Todd Volkert12bbaba2018-04-19 23:36:15 -0700159
Yegord4830fc2017-11-08 10:43:47 -0800160void main() {
Ian Hickson31a96262019-01-19 00:31:05 -0800161 MockStdio mockStdio;
Yegord4830fc2017-11-08 10:43:47 -0800162 group('VMService', () {
Ian Hickson31a96262019-01-19 00:31:05 -0800163 setUp(() {
164 mockStdio = MockStdio();
165 });
166
Todd Volkert12bbaba2018-04-19 23:36:15 -0700167 testUsingContext('fails connection eagerly in the connect() method', () async {
Ian Hickson31a96262019-01-19 00:31:05 -0800168 FakeAsync().run((FakeAsync time) {
169 bool failed = false;
170 final Future<VMService> future = VMService.connect(Uri.parse('http://host.invalid:9999/'));
171 future.whenComplete(() {
172 failed = true;
173 });
174 time.elapse(const Duration(seconds: 5));
175 expect(failed, isFalse);
176 expect(mockStdio.writtenToStdout.join(''), '');
177 expect(mockStdio.writtenToStderr.join(''), '');
178 time.elapse(const Duration(seconds: 5));
179 expect(failed, isFalse);
180 expect(mockStdio.writtenToStdout.join(''), 'This is taking longer than expected...\n');
181 expect(mockStdio.writtenToStderr.join(''), '');
182 });
183 }, overrides: <Type, Generator>{
184 Logger: () => StdoutLogger(),
185 Stdio: () => mockStdio,
Jonah Williams301eaa82019-04-15 08:59:28 -0700186 WebSocketConnector: () => (String url, {CompressionOptions compression}) async => throw const SocketException('test'),
Yegord4830fc2017-11-08 10:43:47 -0800187 });
Ian Hickson6bfd9bb2018-11-08 20:54:06 -0800188
189 testUsingContext('refreshViews', () {
190 FakeAsync().run((FakeAsync time) {
191 bool done = false;
192 final MockPeer mockPeer = MockPeer();
193 expect(mockPeer.returnedFromSendRequest, 0);
Ian Hickson31a96262019-01-19 00:31:05 -0800194 final VMService vmService = VMService(mockPeer, null, null, null, null, null);
Ian Hickson6bfd9bb2018-11-08 20:54:06 -0800195 vmService.getVM().then((void value) { done = true; });
196 expect(done, isFalse);
197 expect(mockPeer.returnedFromSendRequest, 0);
198 time.elapse(Duration.zero);
199 expect(done, isTrue);
200 expect(mockPeer.returnedFromSendRequest, 1);
201
202 done = false;
203 mockPeer.tripGetVMLatch(); // this blocks the upcoming getVM call
204 final Future<void> ready = vmService.refreshViews(waitForViews: true);
205 ready.then((void value) { done = true; });
206 expect(mockPeer.returnedFromSendRequest, 1);
207 time.elapse(Duration.zero); // this unblocks the listViews call which returns nothing
208 expect(mockPeer.returnedFromSendRequest, 2);
209 time.elapse(const Duration(milliseconds: 50)); // the last listViews had no views, so it waits 50ms, then calls getVM
210 expect(done, isFalse);
211 expect(mockPeer.returnedFromSendRequest, 2);
212 mockPeer.tripGetVMLatch(); // this unblocks the getVM call
213 expect(mockPeer.returnedFromSendRequest, 2);
214 time.elapse(Duration.zero); // here getVM returns with no isolates and listViews returns no views
215 expect(mockPeer.returnedFromSendRequest, 4);
216 time.elapse(const Duration(milliseconds: 50)); // so refreshViews waits another 50ms
217 expect(done, isFalse);
218 expect(mockPeer.returnedFromSendRequest, 4);
219 mockPeer.tripGetVMLatch(); // this unblocks the getVM call
220 expect(mockPeer.returnedFromSendRequest, 4);
221 time.elapse(Duration.zero); // here getVM returns with no isolates and listViews returns no views
222 expect(mockPeer.returnedFromSendRequest, 6);
223 time.elapse(const Duration(milliseconds: 50)); // so refreshViews waits another 50ms
224 expect(done, isFalse);
225 expect(mockPeer.returnedFromSendRequest, 6);
226 mockPeer.tripGetVMLatch(); // this unblocks the getVM call
227 expect(mockPeer.returnedFromSendRequest, 6);
228 time.elapse(Duration.zero); // here getVM returns with no isolates and listViews returns no views
229 expect(mockPeer.returnedFromSendRequest, 8);
230 time.elapse(const Duration(milliseconds: 50)); // so refreshViews waits another 50ms
231 expect(done, isFalse);
232 expect(mockPeer.returnedFromSendRequest, 8);
233 mockPeer.tripGetVMLatch(); // this unblocks the getVM call
234 expect(mockPeer.returnedFromSendRequest, 8);
235 time.elapse(Duration.zero); // here getVM returns with no isolates and listViews returns no views
236 expect(mockPeer.returnedFromSendRequest, 10);
237 const String message = 'Flutter is taking longer than expected to report its views. Still trying...\n';
238 expect(mockStdio.writtenToStdout.join(''), message);
239 expect(mockStdio.writtenToStderr.join(''), '');
240 time.elapse(const Duration(milliseconds: 50)); // so refreshViews waits another 50ms
241 expect(done, isFalse);
242 expect(mockPeer.returnedFromSendRequest, 10);
243 mockPeer.isolatesEnabled = true;
244 mockPeer.tripGetVMLatch(); // this unblocks the getVM call
245 expect(mockPeer.returnedFromSendRequest, 10);
246 time.elapse(Duration.zero); // now it returns an isolate and the listViews call returns views
247 expect(mockPeer.returnedFromSendRequest, 13);
248 expect(done, isTrue);
249 expect(mockStdio.writtenToStdout.join(''), message);
250 expect(mockStdio.writtenToStderr.join(''), '');
251 });
252 }, overrides: <Type, Generator>{
253 Logger: () => StdoutLogger(),
254 Stdio: () => mockStdio,
255 });
Yegord4830fc2017-11-08 10:43:47 -0800256 });
257}