blob: 0a49b427632d06521ad7215962514334b1f3f233 [file] [log] [blame]
John McCutchan0de69162016-07-20 12:59:30 -07001// Copyright 2016 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
Dan Rubel0295def2017-01-22 10:37:10 -05005import 'dart:async';
6import 'dart:convert';
Jonah Williams27876e02019-05-31 13:17:12 -07007import 'dart:io'; // ignore: dart_io_import
Dan Rubel0295def2017-01-22 10:37:10 -05008
Todd Volkertff619622017-03-27 12:55:19 -07009import 'package:file/file.dart';
10import 'package:file/memory.dart';
Jonah Williamsa30ffb62019-05-22 12:20:02 -070011import 'package:flutter_tools/src/base/file_system.dart';
Jason Simmons311cde92019-05-29 19:04:35 -070012import 'package:flutter_tools/src/base/io.dart';
Jonah Williams5ec039d2019-08-15 09:08:19 -070013import 'package:flutter_tools/src/compile.dart';
John McCutchan0de69162016-07-20 12:59:30 -070014import 'package:flutter_tools/src/devfs.dart';
Dan Rubel0295def2017-01-22 10:37:10 -050015import 'package:flutter_tools/src/vmservice.dart';
Zachary Anderson4d490662017-06-22 09:48:31 -070016import 'package:json_rpc_2/json_rpc_2.dart' as rpc;
Jonah Williams5ec039d2019-08-15 09:08:19 -070017import 'package:mockito/mockito.dart';
John McCutchan0de69162016-07-20 12:59:30 -070018
Ian Hicksond919e692019-07-13 11:51:44 -070019import '../src/common.dart';
20import '../src/context.dart';
21import '../src/mocks.dart';
John McCutchan0de69162016-07-20 12:59:30 -070022
23void main() {
Todd Volkertff619622017-03-27 12:55:19 -070024 FileSystem fs;
Jonah Williams27876e02019-05-31 13:17:12 -070025 String filePath;
26 Directory tempDir;
27 String basePath;
28 DevFS devFS;
Dan Rubel0295def2017-01-22 10:37:10 -050029
Jonah Williams27876e02019-05-31 13:17:12 -070030 setUpAll(() {
31 fs = MemoryFileSystem();
32 filePath = fs.path.join('lib', 'foo.txt');
Todd Volkertff619622017-03-27 12:55:19 -070033 });
34
Dan Rubel0295def2017-01-22 10:37:10 -050035 group('DevFSContent', () {
36 test('bytes', () {
Alexandre Ardhuind927c932018-09-12 08:29:29 +020037 final DevFSByteContent content = DevFSByteContent(<int>[4, 5, 6]);
Dan Rubel0295def2017-01-22 10:37:10 -050038 expect(content.bytes, orderedEquals(<int>[4, 5, 6]));
39 expect(content.isModified, isTrue);
40 expect(content.isModified, isFalse);
41 content.bytes = <int>[7, 8, 9, 2];
42 expect(content.bytes, orderedEquals(<int>[7, 8, 9, 2]));
43 expect(content.isModified, isTrue);
44 expect(content.isModified, isFalse);
45 });
46 test('string', () {
Alexandre Ardhuind927c932018-09-12 08:29:29 +020047 final DevFSStringContent content = DevFSStringContent('some string');
Dan Rubel0295def2017-01-22 10:37:10 -050048 expect(content.string, 'some string');
Jason Simmons466d1542018-03-12 11:06:32 -070049 expect(content.bytes, orderedEquals(utf8.encode('some string')));
Dan Rubel0295def2017-01-22 10:37:10 -050050 expect(content.isModified, isTrue);
51 expect(content.isModified, isFalse);
52 content.string = 'another string';
53 expect(content.string, 'another string');
Jason Simmons466d1542018-03-12 11:06:32 -070054 expect(content.bytes, orderedEquals(utf8.encode('another string')));
Dan Rubel0295def2017-01-22 10:37:10 -050055 expect(content.isModified, isTrue);
56 expect(content.isModified, isFalse);
Jason Simmons466d1542018-03-12 11:06:32 -070057 content.bytes = utf8.encode('foo bar');
Dan Rubel0295def2017-01-22 10:37:10 -050058 expect(content.string, 'foo bar');
Jason Simmons466d1542018-03-12 11:06:32 -070059 expect(content.bytes, orderedEquals(utf8.encode('foo bar')));
Dan Rubel0295def2017-01-22 10:37:10 -050060 expect(content.isModified, isTrue);
61 expect(content.isModified, isFalse);
62 });
Greg Spencer485ed2f2018-10-09 14:33:47 -070063 testUsingContext('file', () async {
64 final File file = fs.file(filePath);
65 final DevFSFileContent content = DevFSFileContent(file);
66 expect(content.isModified, isFalse);
67 expect(content.isModified, isFalse);
68
69 file.parent.createSync(recursive: true);
Alexander Aprelev0b940a42018-11-14 13:26:24 -080070 file.writeAsBytesSync(<int>[1, 2, 3], flush: true);
Greg Spencer485ed2f2018-10-09 14:33:47 -070071
Devon Carewe30f9a92018-10-29 22:39:40 -070072 final DateTime fiveSecondsAgo = DateTime.now().subtract(const Duration(seconds:5));
Greg Spencer485ed2f2018-10-09 14:33:47 -070073 expect(content.isModifiedAfter(fiveSecondsAgo), isTrue);
74 expect(content.isModifiedAfter(fiveSecondsAgo), isTrue);
75 expect(content.isModifiedAfter(null), isTrue);
76
Alexander Aprelev0b940a42018-11-14 13:26:24 -080077 file.writeAsBytesSync(<int>[2, 3, 4], flush: true);
Jonah Williams27876e02019-05-31 13:17:12 -070078 expect(content.fileDependencies, <String>[filePath]);
Greg Spencer485ed2f2018-10-09 14:33:47 -070079 expect(content.isModified, isTrue);
80 expect(content.isModified, isFalse);
Jonah Williams27876e02019-05-31 13:17:12 -070081 expect(await content.contentsAsBytes(), <int>[2, 3, 4]);
Greg Spencer485ed2f2018-10-09 14:33:47 -070082 updateFileModificationTime(file.path, fiveSecondsAgo, 0);
83 expect(content.isModified, isFalse);
84 expect(content.isModified, isFalse);
85
86 file.deleteSync();
87 expect(content.isModified, isTrue);
88 expect(content.isModified, isFalse);
89 expect(content.isModified, isFalse);
90 }, overrides: <Type, Generator>{
91 FileSystem: () => fs,
Jonah Williams27876e02019-05-31 13:17:12 -070092 }, skip: Platform.isWindows); // TODO(jonahwilliams): fix or disable this functionality.
Dan Rubel0295def2017-01-22 10:37:10 -050093 });
94
Alexander Aprelev839fdbd2019-09-29 21:32:06 -070095 group('mocked http client', () {
96 HttpOverrides savedHttpOverrides;
97 HttpClient httpClient;
98
99 setUpAll(() {
100 tempDir = _newTempDir(fs);
101 basePath = tempDir.path;
102 savedHttpOverrides = HttpOverrides.current;
103 httpClient = MockOddlyFailingHttpClient();
104 HttpOverrides.global = MyHttpOverrides(httpClient);
105 });
106
107 tearDownAll(() async {
108 HttpOverrides.global = savedHttpOverrides;
109 });
110
111 testUsingContext('retry uploads when failure', () async {
112 final File file = fs.file(fs.path.join(basePath, filePath));
113 await file.parent.create(recursive: true);
114 file.writeAsBytesSync(<int>[1, 2, 3]);
115 // simulate package
116 await _createPackage(fs, 'somepkg', 'somefile.txt');
117
118 final RealMockVMService vmService = RealMockVMService();
119 final RealMockVM vm = RealMockVM();
120 final Map<String, dynamic> response = <String, dynamic>{ 'uri': 'file://abc' };
121 when(vm.createDevFS(any)).thenAnswer((Invocation invocation) {
122 return Future<Map<String, dynamic>>.value(response);
123 });
124 when(vmService.vm).thenReturn(vm);
125
126 reset(httpClient);
127
128 final MockHttpClientRequest httpRequest = MockHttpClientRequest();
129 when(httpRequest.headers).thenReturn(MockHttpHeaders());
130 when(httpClient.putUrl(any)).thenAnswer((Invocation invocation) {
131 return Future<HttpClientRequest>.value(httpRequest);
132 });
133 final MockHttpClientResponse httpClientResponse = MockHttpClientResponse();
134 int nRequest = 0;
135 const int kFailedAttempts = 5;
136 when(httpRequest.close()).thenAnswer((Invocation invocation) {
137 if (nRequest++ < kFailedAttempts) {
138 throw 'Connection resert by peer';
139 }
140 return Future<HttpClientResponse>.value(httpClientResponse);
141 });
142
143 devFS = DevFS(vmService, 'test', tempDir);
144 await devFS.create();
145
146 final MockResidentCompiler residentCompiler = MockResidentCompiler();
147 final UpdateFSReport report = await devFS.update(
148 mainPath: 'lib/foo.txt',
149 generator: residentCompiler,
150 pathToReload: 'lib/foo.txt.dill',
151 trackWidgetCreation: false,
152 invalidatedFiles: <Uri>[],
153 );
154
155 expect(report.syncedBytes, 22);
156 expect(report.success, isTrue);
157 verify(httpClient.putUrl(any)).called(kFailedAttempts + 1);
158 verify(httpRequest.close()).called(kFailedAttempts + 1);
159 }, overrides: <Type, Generator>{
160 FileSystem: () => fs,
161 });
162 });
163
Dan Rubel0295def2017-01-22 10:37:10 -0500164 group('devfs remote', () {
Jonah Williams27876e02019-05-31 13:17:12 -0700165 MockVMService vmService;
166 final MockResidentCompiler residentCompiler = MockResidentCompiler();
Dan Rubel0295def2017-01-22 10:37:10 -0500167
Jonah Williams27876e02019-05-31 13:17:12 -0700168 setUpAll(() async {
169 tempDir = _newTempDir(fs);
170 basePath = tempDir.path;
171 vmService = MockVMService();
172 await vmService.setUp();
173 });
Jonah Williams5ec039d2019-08-15 09:08:19 -0700174
Jonah Williams27876e02019-05-31 13:17:12 -0700175 tearDownAll(() async {
176 await vmService.tearDown();
177 _cleanupTempDirs();
Dan Rubel0295def2017-01-22 10:37:10 -0500178 });
179
180 testUsingContext('create dev file system', () async {
181 // simulate workspace
Jonah Williams27876e02019-05-31 13:17:12 -0700182 final File file = fs.file(fs.path.join(basePath, filePath));
Dan Rubel0295def2017-01-22 10:37:10 -0500183 await file.parent.create(recursive: true);
184 file.writeAsBytesSync(<int>[1, 2, 3]);
185
186 // simulate package
Todd Volkertff619622017-03-27 12:55:19 -0700187 await _createPackage(fs, 'somepkg', 'somefile.txt');
Jonah Williams445505d2019-05-30 16:13:46 -0700188
Jonah Williams27876e02019-05-31 13:17:12 -0700189 devFS = DevFS(vmService, 'test', tempDir);
190 await devFS.create();
191 vmService.expectMessages(<String>['create test']);
Dan Rubel0295def2017-01-22 10:37:10 -0500192 expect(devFS.assetPathsToEvict, isEmpty);
193
Alexander Aprelev52bd2cc2018-12-27 09:53:24 -0800194 final UpdateFSReport report = await devFS.update(
Chris Bracken251e82d2018-08-31 13:31:56 -0700195 mainPath: 'lib/foo.txt',
196 generator: residentCompiler,
197 pathToReload: 'lib/foo.txt.dill',
Jacob Richmanf5f70f02018-10-23 10:09:18 -0700198 trackWidgetCreation: false,
Alexander Aprelev12c4e052019-03-20 21:58:15 -0700199 invalidatedFiles: <Uri>[],
Chris Bracken251e82d2018-08-31 13:31:56 -0700200 );
Jonah Williams27876e02019-05-31 13:17:12 -0700201 vmService.expectMessages(<String>[
202 'writeFile test lib/foo.txt.dill',
203 ]);
Dan Rubel0295def2017-01-22 10:37:10 -0500204 expect(devFS.assetPathsToEvict, isEmpty);
Alexander Aprelev52bd2cc2018-12-27 09:53:24 -0800205 expect(report.syncedBytes, 22);
206 expect(report.success, true);
Todd Volkertff619622017-03-27 12:55:19 -0700207 }, overrides: <Type, Generator>{
208 FileSystem: () => fs,
Dan Rubel8742fb02017-03-13 13:47:29 -0400209 });
Dan Rubel0295def2017-01-22 10:37:10 -0500210
211 testUsingContext('delete dev file system', () async {
Jonah Williams27876e02019-05-31 13:17:12 -0700212 expect(vmService.messages, isEmpty, reason: 'prior test timeout');
Dan Rubel0295def2017-01-22 10:37:10 -0500213 await devFS.destroy();
Jonah Williams27876e02019-05-31 13:17:12 -0700214 vmService.expectMessages(<String>['destroy test']);
Zachary Anderson4d490662017-06-22 09:48:31 -0700215 expect(devFS.assetPathsToEvict, isEmpty);
216 }, overrides: <Type, Generator>{
217 FileSystem: () => fs,
218 });
219
220 testUsingContext('cleanup preexisting file system', () async {
221 // simulate workspace
Jonah Williams27876e02019-05-31 13:17:12 -0700222 final File file = fs.file(fs.path.join(basePath, filePath));
Zachary Anderson4d490662017-06-22 09:48:31 -0700223 await file.parent.create(recursive: true);
224 file.writeAsBytesSync(<int>[1, 2, 3]);
225
226 // simulate package
227 await _createPackage(fs, 'somepkg', 'somefile.txt');
228
Jonah Williams27876e02019-05-31 13:17:12 -0700229 devFS = DevFS(vmService, 'test', tempDir);
Zachary Anderson4d490662017-06-22 09:48:31 -0700230 await devFS.create();
Jonah Williams27876e02019-05-31 13:17:12 -0700231 vmService.expectMessages(<String>['create test']);
Zachary Anderson4d490662017-06-22 09:48:31 -0700232 expect(devFS.assetPathsToEvict, isEmpty);
233
234 // Try to create again.
235 await devFS.create();
Jonah Williams27876e02019-05-31 13:17:12 -0700236 vmService.expectMessages(<String>['create test', 'destroy test', 'create test']);
Zachary Anderson4d490662017-06-22 09:48:31 -0700237 expect(devFS.assetPathsToEvict, isEmpty);
238
239 // Really destroy.
240 await devFS.destroy();
Jonah Williams27876e02019-05-31 13:17:12 -0700241 vmService.expectMessages(<String>['destroy test']);
Dan Rubel0295def2017-01-22 10:37:10 -0500242 expect(devFS.assetPathsToEvict, isEmpty);
Todd Volkertff619622017-03-27 12:55:19 -0700243 }, overrides: <Type, Generator>{
244 FileSystem: () => fs,
Dan Rubel0295def2017-01-22 10:37:10 -0500245 });
Jonah Williams5ec039d2019-08-15 09:08:19 -0700246
247 testUsingContext('reports unsuccessful compile when errors are returned', () async {
248 devFS = DevFS(vmService, 'test', tempDir);
249 await devFS.create();
Jonah Williams39f85f92019-10-02 12:46:33 -0700250 final DateTime previousCompile = devFS.lastCompiled;
Jonah Williams5ec039d2019-08-15 09:08:19 -0700251
252 final RealMockResidentCompiler residentCompiler = RealMockResidentCompiler();
253 when(residentCompiler.recompile(
254 any,
255 any,
256 outputPath: anyNamed('outputPath'),
Jonah Williams39f85f92019-10-02 12:46:33 -0700257 packagesFilePath: anyNamed('packagesFilePath'),
Jonah Williams5ec039d2019-08-15 09:08:19 -0700258 )).thenAnswer((Invocation invocation) {
259 return Future<CompilerOutput>.value(const CompilerOutput('example', 2, <Uri>[]));
260 });
261
262 final UpdateFSReport report = await devFS.update(
263 mainPath: 'lib/foo.txt',
264 generator: residentCompiler,
265 pathToReload: 'lib/foo.txt.dill',
266 trackWidgetCreation: false,
267 invalidatedFiles: <Uri>[],
268 );
269
270 expect(report.success, false);
Jonah Williams39f85f92019-10-02 12:46:33 -0700271 expect(devFS.lastCompiled, previousCompile);
272 }, overrides: <Type, Generator>{
273 FileSystem: () => fs,
274 });
275
276 testUsingContext('correctly updates last compiled time when compilation does not fail', () async {
277 devFS = DevFS(vmService, 'test', tempDir);
278 // simulate package
279 final File sourceFile = await _createPackage(fs, 'somepkg', 'main.dart');
280
281 await devFS.create();
282 final DateTime previousCompile = devFS.lastCompiled;
283
284 final RealMockResidentCompiler residentCompiler = RealMockResidentCompiler();
285 when(residentCompiler.recompile(
286 any,
287 any,
288 outputPath: anyNamed('outputPath'),
289 packagesFilePath: anyNamed('packagesFilePath'),
290 )).thenAnswer((Invocation invocation) {
291 fs.file('example').createSync();
292 return Future<CompilerOutput>.value(CompilerOutput('example', 0, <Uri>[sourceFile.uri]));
293 });
294
295 final UpdateFSReport report = await devFS.update(
296 mainPath: 'lib/main.dart',
297 generator: residentCompiler,
298 pathToReload: 'lib/foo.txt.dill',
299 trackWidgetCreation: false,
300 invalidatedFiles: <Uri>[],
301 );
302
303 expect(report.success, true);
304 expect(devFS.lastCompiled, isNot(previousCompile));
Jonah Williams5ec039d2019-08-15 09:08:19 -0700305 }, overrides: <Type, Generator>{
306 FileSystem: () => fs,
307 });
Michael Goderbauer17057bb2017-03-01 10:11:56 -0800308 });
Dan Rubel0295def2017-01-22 10:37:10 -0500309}
310
Jonah Williams27876e02019-05-31 13:17:12 -0700311class MockVMService extends BasicMock implements VMService {
312 MockVMService() {
313 _vm = MockVM(this);
314 }
Dan Rubel0295def2017-01-22 10:37:10 -0500315
Jonah Williams27876e02019-05-31 13:17:12 -0700316 Uri _httpAddress;
317 HttpServer _server;
318 MockVM _vm;
Alexandre Ardhuin2ea1d812018-10-04 07:28:07 +0200319
Jonah Williams27876e02019-05-31 13:17:12 -0700320 @override
321 Uri get httpAddress => _httpAddress;
Dan Rubel0295def2017-01-22 10:37:10 -0500322
Jonah Williams27876e02019-05-31 13:17:12 -0700323 @override
324 VM get vm => _vm;
325
326 Future<void> setUp() async {
327 try {
328 _server = await HttpServer.bind(InternetAddress.loopbackIPv6, 0);
329 _httpAddress = Uri.parse('http://[::1]:${_server.port}');
330 } on SocketException {
331 // Fall back to IPv4 if the host doesn't support binding to IPv6 localhost
332 _server = await HttpServer.bind(InternetAddress.loopbackIPv4, 0);
333 _httpAddress = Uri.parse('http://127.0.0.1:${_server.port}');
334 }
335 _server.listen((HttpRequest request) {
336 final String fsName = request.headers.value('dev_fs_name');
337 final String devicePath = utf8.decode(base64.decode(request.headers.value('dev_fs_uri_b64')));
338 messages.add('writeFile $fsName $devicePath');
339 request.drain<List<int>>().then<void>((List<int> value) {
340 request.response
341 ..write('Got it')
342 ..close();
343 });
344 });
345 }
346
347 Future<void> tearDown() async {
348 await _server?.close();
349 }
350
351 @override
352 dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
353}
354
355class MockVM implements VM {
356 MockVM(this._service);
357
358 final MockVMService _service;
359 final Uri _baseUri = Uri.parse('file:///tmp/devfs/test');
360 bool _devFSExists = false;
361
362 static const int kFileSystemAlreadyExists = 1001;
363
364 @override
365 Future<Map<String, dynamic>> createDevFS(String fsName) async {
366 _service.messages.add('create $fsName');
367 if (_devFSExists) {
368 throw rpc.RpcException(kFileSystemAlreadyExists, 'File system already exists');
369 }
370 _devFSExists = true;
371 return <String, dynamic>{'uri': '$_baseUri'};
372 }
373
374 @override
375 Future<Map<String, dynamic>> deleteDevFS(String fsName) async {
376 _service.messages.add('destroy $fsName');
377 _devFSExists = false;
378 return <String, dynamic>{'type': 'Success'};
379 }
380
381 @override
382 Future<Map<String, dynamic>> invokeRpcRaw(
383 String method, {
384 Map<String, dynamic> params = const <String, dynamic>{},
385 Duration timeout,
386 bool timeoutFatal = true,
Zachary Anderson22ca3f92019-06-12 11:18:53 -0700387 bool truncateLogs = true,
Jonah Williams27876e02019-05-31 13:17:12 -0700388 }) async {
389 _service.messages.add('$method $params');
390 return <String, dynamic>{'success': true};
391 }
392
393 @override
394 dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
395}
396
Jonah Williams5ec039d2019-08-15 09:08:19 -0700397class RealMockResidentCompiler extends Mock implements ResidentCompiler {}
Jonah Williams27876e02019-05-31 13:17:12 -0700398
399final List<Directory> _tempDirs = <Directory>[];
Michael Goderbauer17057bb2017-03-01 10:11:56 -0800400final Map <String, Uri> _packages = <String, Uri>{};
Dan Rubel0295def2017-01-22 10:37:10 -0500401
Jonah Williams27876e02019-05-31 13:17:12 -0700402Directory _newTempDir(FileSystem fs) {
403 final Directory tempDir = fs.systemTempDirectory.createTempSync('flutter_devfs${_tempDirs.length}_test.');
404 _tempDirs.add(tempDir);
405 return tempDir;
406}
407
408void _cleanupTempDirs() {
Zachary Andersone2340c62019-09-13 14:51:35 -0700409 while (_tempDirs.isNotEmpty) {
Jonah Williams27876e02019-05-31 13:17:12 -0700410 tryToDelete(_tempDirs.removeLast());
Zachary Andersone2340c62019-09-13 14:51:35 -0700411 }
Jonah Williams27876e02019-05-31 13:17:12 -0700412}
Dan Rubel0295def2017-01-22 10:37:10 -0500413
Jonah Williams39f85f92019-10-02 12:46:33 -0700414Future<File> _createPackage(FileSystem fs, String pkgName, String pkgFileName, { bool doubleSlash = false }) async {
Jonah Williams27876e02019-05-31 13:17:12 -0700415 final Directory pkgTempDir = _newTempDir(fs);
416 String pkgFilePath = fs.path.join(pkgTempDir.path, pkgName, 'lib', pkgFileName);
Michael Goderbauere0c51482017-03-03 18:21:01 -0800417 if (doubleSlash) {
418 // Force two separators into the path.
Jonah Williams27876e02019-05-31 13:17:12 -0700419 final String doubleSlash = fs.path.separator + fs.path.separator;
420 pkgFilePath = pkgTempDir.path + doubleSlash + fs.path.join(pkgName, 'lib', pkgFileName);
Michael Goderbauere0c51482017-03-03 18:21:01 -0800421 }
422 final File pkgFile = fs.file(pkgFilePath);
Dan Rubel0295def2017-01-22 10:37:10 -0500423 await pkgFile.parent.create(recursive: true);
424 pkgFile.writeAsBytesSync(<int>[11, 12, 13]);
Michael Goderbauer17057bb2017-03-01 10:11:56 -0800425 _packages[pkgName] = fs.path.toUri(pkgFile.parent.path);
Alexandre Ardhuind927c932018-09-12 08:29:29 +0200426 final StringBuffer sb = StringBuffer();
Michael Goderbauer17057bb2017-03-01 10:11:56 -0800427 _packages.forEach((String pkgName, Uri pkgUri) {
428 sb.writeln('$pkgName:$pkgUri');
Dan Rubel0295def2017-01-22 10:37:10 -0500429 });
Jonah Williams39f85f92019-10-02 12:46:33 -0700430 return fs.file(fs.path.join(_tempDirs[0].path, '.packages'))
431 ..writeAsStringSync(sb.toString());
John McCutchan0de69162016-07-20 12:59:30 -0700432}
Jonah Williams27876e02019-05-31 13:17:12 -0700433
Alexander Aprelev839fdbd2019-09-29 21:32:06 -0700434class RealMockVM extends Mock implements VM {
435
436}
437
438class RealMockVMService extends Mock implements VMService {
439
440}
441
442class MyHttpOverrides extends HttpOverrides {
443 MyHttpOverrides(this._httpClient);
444 @override
445 HttpClient createHttpClient(SecurityContext context) {
446 return _httpClient;
447 }
448
449 final HttpClient _httpClient;
450}
451
452class MockOddlyFailingHttpClient extends Mock implements HttpClient {}
453class MockHttpClientRequest extends Mock implements HttpClientRequest {}
454class MockHttpHeaders extends Mock implements HttpHeaders {}
Alexandre Ardhuinf0553ba2019-09-30 18:48:23 +0200455class MockHttpClientResponse extends Mock implements HttpClientResponse {}