Yegor | d4830fc | 2017-11-08 10:43:47 -0800 | [diff] [blame] | 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 | |
Ian Hickson | 6bfd9bb | 2018-11-08 20:54:06 -0800 | [diff] [blame] | 5 | import 'dart:async'; |
Jonah Williams | 301eaa8 | 2019-04-15 08:59:28 -0700 | [diff] [blame] | 6 | import 'dart:io'; |
Ian Hickson | 6bfd9bb | 2018-11-08 20:54:06 -0800 | [diff] [blame] | 7 | |
| 8 | import 'package:flutter_tools/src/base/io.dart'; |
| 9 | import 'package:flutter_tools/src/base/logger.dart'; |
Yegor | d4830fc | 2017-11-08 10:43:47 -0800 | [diff] [blame] | 10 | import 'package:flutter_tools/src/vmservice.dart'; |
Ian Hickson | 6bfd9bb | 2018-11-08 20:54:06 -0800 | [diff] [blame] | 11 | import 'package:json_rpc_2/json_rpc_2.dart' as rpc; |
| 12 | import 'package:quiver/testing/async.dart'; |
Yegor | d4830fc | 2017-11-08 10:43:47 -0800 | [diff] [blame] | 13 | |
Ian Hickson | d919e69 | 2019-07-13 11:51:44 -0700 | [diff] [blame] | 14 | import '../src/common.dart'; |
| 15 | import '../src/context.dart'; |
| 16 | import '../src/mocks.dart'; |
Ian Hickson | 6bfd9bb | 2018-11-08 20:54:06 -0800 | [diff] [blame] | 17 | |
| 18 | class MockPeer implements rpc.Peer { |
Dan Field | 1db5d66 | 2019-04-25 08:27:00 -0700 | [diff] [blame] | 19 | |
| 20 | @override |
| 21 | rpc.ErrorCallback get onUnhandledError => null; |
| 22 | |
Ian Hickson | 6bfd9bb | 2018-11-08 20:54:06 -0800 | [diff] [blame] | 23 | @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 Volkert | 12bbaba | 2018-04-19 23:36:15 -0700 | [diff] [blame] | 159 | |
Yegor | d4830fc | 2017-11-08 10:43:47 -0800 | [diff] [blame] | 160 | void main() { |
Ian Hickson | 31a9626 | 2019-01-19 00:31:05 -0800 | [diff] [blame] | 161 | MockStdio mockStdio; |
Yegor | d4830fc | 2017-11-08 10:43:47 -0800 | [diff] [blame] | 162 | group('VMService', () { |
Ian Hickson | 31a9626 | 2019-01-19 00:31:05 -0800 | [diff] [blame] | 163 | setUp(() { |
| 164 | mockStdio = MockStdio(); |
| 165 | }); |
| 166 | |
Todd Volkert | 12bbaba | 2018-04-19 23:36:15 -0700 | [diff] [blame] | 167 | testUsingContext('fails connection eagerly in the connect() method', () async { |
Ian Hickson | 31a9626 | 2019-01-19 00:31:05 -0800 | [diff] [blame] | 168 | 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 Williams | 301eaa8 | 2019-04-15 08:59:28 -0700 | [diff] [blame] | 186 | WebSocketConnector: () => (String url, {CompressionOptions compression}) async => throw const SocketException('test'), |
Yegor | d4830fc | 2017-11-08 10:43:47 -0800 | [diff] [blame] | 187 | }); |
Ian Hickson | 6bfd9bb | 2018-11-08 20:54:06 -0800 | [diff] [blame] | 188 | |
| 189 | testUsingContext('refreshViews', () { |
| 190 | FakeAsync().run((FakeAsync time) { |
| 191 | bool done = false; |
| 192 | final MockPeer mockPeer = MockPeer(); |
| 193 | expect(mockPeer.returnedFromSendRequest, 0); |
Ian Hickson | 31a9626 | 2019-01-19 00:31:05 -0800 | [diff] [blame] | 194 | final VMService vmService = VMService(mockPeer, null, null, null, null, null); |
Ian Hickson | 6bfd9bb | 2018-11-08 20:54:06 -0800 | [diff] [blame] | 195 | 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 | }); |
Yegor | d4830fc | 2017-11-08 10:43:47 -0800 | [diff] [blame] | 256 | }); |
| 257 | } |