John McCutchan | 0de6916 | 2016-07-20 12:59:30 -0700 | [diff] [blame] | 1 | // 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 Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 5 | import 'dart:async'; |
| 6 | import 'dart:convert'; |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 7 | import 'dart:io'; // ignore: dart_io_import |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 8 | |
Todd Volkert | ff61962 | 2017-03-27 12:55:19 -0700 | [diff] [blame] | 9 | import 'package:file/file.dart'; |
| 10 | import 'package:file/memory.dart'; |
Jonah Williams | a30ffb6 | 2019-05-22 12:20:02 -0700 | [diff] [blame] | 11 | import 'package:flutter_tools/src/base/file_system.dart'; |
Jason Simmons | 311cde9 | 2019-05-29 19:04:35 -0700 | [diff] [blame] | 12 | import 'package:flutter_tools/src/base/io.dart'; |
Jonah Williams | 5ec039d | 2019-08-15 09:08:19 -0700 | [diff] [blame] | 13 | import 'package:flutter_tools/src/compile.dart'; |
John McCutchan | 0de6916 | 2016-07-20 12:59:30 -0700 | [diff] [blame] | 14 | import 'package:flutter_tools/src/devfs.dart'; |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 15 | import 'package:flutter_tools/src/vmservice.dart'; |
Zachary Anderson | 4d49066 | 2017-06-22 09:48:31 -0700 | [diff] [blame] | 16 | import 'package:json_rpc_2/json_rpc_2.dart' as rpc; |
Jonah Williams | 5ec039d | 2019-08-15 09:08:19 -0700 | [diff] [blame] | 17 | import 'package:mockito/mockito.dart'; |
John McCutchan | 0de6916 | 2016-07-20 12:59:30 -0700 | [diff] [blame] | 18 | |
Ian Hickson | d919e69 | 2019-07-13 11:51:44 -0700 | [diff] [blame] | 19 | import '../src/common.dart'; |
| 20 | import '../src/context.dart'; |
| 21 | import '../src/mocks.dart'; |
John McCutchan | 0de6916 | 2016-07-20 12:59:30 -0700 | [diff] [blame] | 22 | |
| 23 | void main() { |
Todd Volkert | ff61962 | 2017-03-27 12:55:19 -0700 | [diff] [blame] | 24 | FileSystem fs; |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 25 | String filePath; |
| 26 | Directory tempDir; |
| 27 | String basePath; |
| 28 | DevFS devFS; |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 29 | |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 30 | setUpAll(() { |
| 31 | fs = MemoryFileSystem(); |
| 32 | filePath = fs.path.join('lib', 'foo.txt'); |
Todd Volkert | ff61962 | 2017-03-27 12:55:19 -0700 | [diff] [blame] | 33 | }); |
| 34 | |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 35 | group('DevFSContent', () { |
| 36 | test('bytes', () { |
Alexandre Ardhuin | d927c93 | 2018-09-12 08:29:29 +0200 | [diff] [blame] | 37 | final DevFSByteContent content = DevFSByteContent(<int>[4, 5, 6]); |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 38 | 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 Ardhuin | d927c93 | 2018-09-12 08:29:29 +0200 | [diff] [blame] | 47 | final DevFSStringContent content = DevFSStringContent('some string'); |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 48 | expect(content.string, 'some string'); |
Jason Simmons | 466d154 | 2018-03-12 11:06:32 -0700 | [diff] [blame] | 49 | expect(content.bytes, orderedEquals(utf8.encode('some string'))); |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 50 | expect(content.isModified, isTrue); |
| 51 | expect(content.isModified, isFalse); |
| 52 | content.string = 'another string'; |
| 53 | expect(content.string, 'another string'); |
Jason Simmons | 466d154 | 2018-03-12 11:06:32 -0700 | [diff] [blame] | 54 | expect(content.bytes, orderedEquals(utf8.encode('another string'))); |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 55 | expect(content.isModified, isTrue); |
| 56 | expect(content.isModified, isFalse); |
Jason Simmons | 466d154 | 2018-03-12 11:06:32 -0700 | [diff] [blame] | 57 | content.bytes = utf8.encode('foo bar'); |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 58 | expect(content.string, 'foo bar'); |
Jason Simmons | 466d154 | 2018-03-12 11:06:32 -0700 | [diff] [blame] | 59 | expect(content.bytes, orderedEquals(utf8.encode('foo bar'))); |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 60 | expect(content.isModified, isTrue); |
| 61 | expect(content.isModified, isFalse); |
| 62 | }); |
Greg Spencer | 485ed2f | 2018-10-09 14:33:47 -0700 | [diff] [blame] | 63 | 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 Aprelev | 0b940a4 | 2018-11-14 13:26:24 -0800 | [diff] [blame] | 70 | file.writeAsBytesSync(<int>[1, 2, 3], flush: true); |
Greg Spencer | 485ed2f | 2018-10-09 14:33:47 -0700 | [diff] [blame] | 71 | |
Devon Carew | e30f9a9 | 2018-10-29 22:39:40 -0700 | [diff] [blame] | 72 | final DateTime fiveSecondsAgo = DateTime.now().subtract(const Duration(seconds:5)); |
Greg Spencer | 485ed2f | 2018-10-09 14:33:47 -0700 | [diff] [blame] | 73 | expect(content.isModifiedAfter(fiveSecondsAgo), isTrue); |
| 74 | expect(content.isModifiedAfter(fiveSecondsAgo), isTrue); |
| 75 | expect(content.isModifiedAfter(null), isTrue); |
| 76 | |
Alexander Aprelev | 0b940a4 | 2018-11-14 13:26:24 -0800 | [diff] [blame] | 77 | file.writeAsBytesSync(<int>[2, 3, 4], flush: true); |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 78 | expect(content.fileDependencies, <String>[filePath]); |
Greg Spencer | 485ed2f | 2018-10-09 14:33:47 -0700 | [diff] [blame] | 79 | expect(content.isModified, isTrue); |
| 80 | expect(content.isModified, isFalse); |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 81 | expect(await content.contentsAsBytes(), <int>[2, 3, 4]); |
Greg Spencer | 485ed2f | 2018-10-09 14:33:47 -0700 | [diff] [blame] | 82 | 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 Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 92 | }, skip: Platform.isWindows); // TODO(jonahwilliams): fix or disable this functionality. |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 93 | }); |
| 94 | |
Alexander Aprelev | 839fdbd | 2019-09-29 21:32:06 -0700 | [diff] [blame] | 95 | 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 Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 164 | group('devfs remote', () { |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 165 | MockVMService vmService; |
| 166 | final MockResidentCompiler residentCompiler = MockResidentCompiler(); |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 167 | |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 168 | setUpAll(() async { |
| 169 | tempDir = _newTempDir(fs); |
| 170 | basePath = tempDir.path; |
| 171 | vmService = MockVMService(); |
| 172 | await vmService.setUp(); |
| 173 | }); |
Jonah Williams | 5ec039d | 2019-08-15 09:08:19 -0700 | [diff] [blame] | 174 | |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 175 | tearDownAll(() async { |
| 176 | await vmService.tearDown(); |
| 177 | _cleanupTempDirs(); |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 178 | }); |
| 179 | |
| 180 | testUsingContext('create dev file system', () async { |
| 181 | // simulate workspace |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 182 | final File file = fs.file(fs.path.join(basePath, filePath)); |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 183 | await file.parent.create(recursive: true); |
| 184 | file.writeAsBytesSync(<int>[1, 2, 3]); |
| 185 | |
| 186 | // simulate package |
Todd Volkert | ff61962 | 2017-03-27 12:55:19 -0700 | [diff] [blame] | 187 | await _createPackage(fs, 'somepkg', 'somefile.txt'); |
Jonah Williams | 445505d | 2019-05-30 16:13:46 -0700 | [diff] [blame] | 188 | |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 189 | devFS = DevFS(vmService, 'test', tempDir); |
| 190 | await devFS.create(); |
| 191 | vmService.expectMessages(<String>['create test']); |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 192 | expect(devFS.assetPathsToEvict, isEmpty); |
| 193 | |
Alexander Aprelev | 52bd2cc | 2018-12-27 09:53:24 -0800 | [diff] [blame] | 194 | final UpdateFSReport report = await devFS.update( |
Chris Bracken | 251e82d | 2018-08-31 13:31:56 -0700 | [diff] [blame] | 195 | mainPath: 'lib/foo.txt', |
| 196 | generator: residentCompiler, |
| 197 | pathToReload: 'lib/foo.txt.dill', |
Jacob Richman | f5f70f0 | 2018-10-23 10:09:18 -0700 | [diff] [blame] | 198 | trackWidgetCreation: false, |
Alexander Aprelev | 12c4e05 | 2019-03-20 21:58:15 -0700 | [diff] [blame] | 199 | invalidatedFiles: <Uri>[], |
Chris Bracken | 251e82d | 2018-08-31 13:31:56 -0700 | [diff] [blame] | 200 | ); |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 201 | vmService.expectMessages(<String>[ |
| 202 | 'writeFile test lib/foo.txt.dill', |
| 203 | ]); |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 204 | expect(devFS.assetPathsToEvict, isEmpty); |
Alexander Aprelev | 52bd2cc | 2018-12-27 09:53:24 -0800 | [diff] [blame] | 205 | expect(report.syncedBytes, 22); |
| 206 | expect(report.success, true); |
Todd Volkert | ff61962 | 2017-03-27 12:55:19 -0700 | [diff] [blame] | 207 | }, overrides: <Type, Generator>{ |
| 208 | FileSystem: () => fs, |
Dan Rubel | 8742fb0 | 2017-03-13 13:47:29 -0400 | [diff] [blame] | 209 | }); |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 210 | |
| 211 | testUsingContext('delete dev file system', () async { |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 212 | expect(vmService.messages, isEmpty, reason: 'prior test timeout'); |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 213 | await devFS.destroy(); |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 214 | vmService.expectMessages(<String>['destroy test']); |
Zachary Anderson | 4d49066 | 2017-06-22 09:48:31 -0700 | [diff] [blame] | 215 | expect(devFS.assetPathsToEvict, isEmpty); |
| 216 | }, overrides: <Type, Generator>{ |
| 217 | FileSystem: () => fs, |
| 218 | }); |
| 219 | |
| 220 | testUsingContext('cleanup preexisting file system', () async { |
| 221 | // simulate workspace |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 222 | final File file = fs.file(fs.path.join(basePath, filePath)); |
Zachary Anderson | 4d49066 | 2017-06-22 09:48:31 -0700 | [diff] [blame] | 223 | 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 Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 229 | devFS = DevFS(vmService, 'test', tempDir); |
Zachary Anderson | 4d49066 | 2017-06-22 09:48:31 -0700 | [diff] [blame] | 230 | await devFS.create(); |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 231 | vmService.expectMessages(<String>['create test']); |
Zachary Anderson | 4d49066 | 2017-06-22 09:48:31 -0700 | [diff] [blame] | 232 | expect(devFS.assetPathsToEvict, isEmpty); |
| 233 | |
| 234 | // Try to create again. |
| 235 | await devFS.create(); |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 236 | vmService.expectMessages(<String>['create test', 'destroy test', 'create test']); |
Zachary Anderson | 4d49066 | 2017-06-22 09:48:31 -0700 | [diff] [blame] | 237 | expect(devFS.assetPathsToEvict, isEmpty); |
| 238 | |
| 239 | // Really destroy. |
| 240 | await devFS.destroy(); |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 241 | vmService.expectMessages(<String>['destroy test']); |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 242 | expect(devFS.assetPathsToEvict, isEmpty); |
Todd Volkert | ff61962 | 2017-03-27 12:55:19 -0700 | [diff] [blame] | 243 | }, overrides: <Type, Generator>{ |
| 244 | FileSystem: () => fs, |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 245 | }); |
Jonah Williams | 5ec039d | 2019-08-15 09:08:19 -0700 | [diff] [blame] | 246 | |
| 247 | testUsingContext('reports unsuccessful compile when errors are returned', () async { |
| 248 | devFS = DevFS(vmService, 'test', tempDir); |
| 249 | await devFS.create(); |
Jonah Williams | 39f85f9 | 2019-10-02 12:46:33 -0700 | [diff] [blame] | 250 | final DateTime previousCompile = devFS.lastCompiled; |
Jonah Williams | 5ec039d | 2019-08-15 09:08:19 -0700 | [diff] [blame] | 251 | |
| 252 | final RealMockResidentCompiler residentCompiler = RealMockResidentCompiler(); |
| 253 | when(residentCompiler.recompile( |
| 254 | any, |
| 255 | any, |
| 256 | outputPath: anyNamed('outputPath'), |
Jonah Williams | 39f85f9 | 2019-10-02 12:46:33 -0700 | [diff] [blame] | 257 | packagesFilePath: anyNamed('packagesFilePath'), |
Jonah Williams | 5ec039d | 2019-08-15 09:08:19 -0700 | [diff] [blame] | 258 | )).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 Williams | 39f85f9 | 2019-10-02 12:46:33 -0700 | [diff] [blame] | 271 | 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 Williams | 5ec039d | 2019-08-15 09:08:19 -0700 | [diff] [blame] | 305 | }, overrides: <Type, Generator>{ |
| 306 | FileSystem: () => fs, |
| 307 | }); |
Michael Goderbauer | 17057bb | 2017-03-01 10:11:56 -0800 | [diff] [blame] | 308 | }); |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 309 | } |
| 310 | |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 311 | class MockVMService extends BasicMock implements VMService { |
| 312 | MockVMService() { |
| 313 | _vm = MockVM(this); |
| 314 | } |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 315 | |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 316 | Uri _httpAddress; |
| 317 | HttpServer _server; |
| 318 | MockVM _vm; |
Alexandre Ardhuin | 2ea1d81 | 2018-10-04 07:28:07 +0200 | [diff] [blame] | 319 | |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 320 | @override |
| 321 | Uri get httpAddress => _httpAddress; |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 322 | |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 323 | @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 | |
| 355 | class 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 Anderson | 22ca3f9 | 2019-06-12 11:18:53 -0700 | [diff] [blame] | 387 | bool truncateLogs = true, |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 388 | }) 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 Williams | 5ec039d | 2019-08-15 09:08:19 -0700 | [diff] [blame] | 397 | class RealMockResidentCompiler extends Mock implements ResidentCompiler {} |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 398 | |
| 399 | final List<Directory> _tempDirs = <Directory>[]; |
Michael Goderbauer | 17057bb | 2017-03-01 10:11:56 -0800 | [diff] [blame] | 400 | final Map <String, Uri> _packages = <String, Uri>{}; |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 401 | |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 402 | Directory _newTempDir(FileSystem fs) { |
| 403 | final Directory tempDir = fs.systemTempDirectory.createTempSync('flutter_devfs${_tempDirs.length}_test.'); |
| 404 | _tempDirs.add(tempDir); |
| 405 | return tempDir; |
| 406 | } |
| 407 | |
| 408 | void _cleanupTempDirs() { |
Zachary Anderson | e2340c6 | 2019-09-13 14:51:35 -0700 | [diff] [blame] | 409 | while (_tempDirs.isNotEmpty) { |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 410 | tryToDelete(_tempDirs.removeLast()); |
Zachary Anderson | e2340c6 | 2019-09-13 14:51:35 -0700 | [diff] [blame] | 411 | } |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 412 | } |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 413 | |
Jonah Williams | 39f85f9 | 2019-10-02 12:46:33 -0700 | [diff] [blame] | 414 | Future<File> _createPackage(FileSystem fs, String pkgName, String pkgFileName, { bool doubleSlash = false }) async { |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 415 | final Directory pkgTempDir = _newTempDir(fs); |
| 416 | String pkgFilePath = fs.path.join(pkgTempDir.path, pkgName, 'lib', pkgFileName); |
Michael Goderbauer | e0c5148 | 2017-03-03 18:21:01 -0800 | [diff] [blame] | 417 | if (doubleSlash) { |
| 418 | // Force two separators into the path. |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 419 | final String doubleSlash = fs.path.separator + fs.path.separator; |
| 420 | pkgFilePath = pkgTempDir.path + doubleSlash + fs.path.join(pkgName, 'lib', pkgFileName); |
Michael Goderbauer | e0c5148 | 2017-03-03 18:21:01 -0800 | [diff] [blame] | 421 | } |
| 422 | final File pkgFile = fs.file(pkgFilePath); |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 423 | await pkgFile.parent.create(recursive: true); |
| 424 | pkgFile.writeAsBytesSync(<int>[11, 12, 13]); |
Michael Goderbauer | 17057bb | 2017-03-01 10:11:56 -0800 | [diff] [blame] | 425 | _packages[pkgName] = fs.path.toUri(pkgFile.parent.path); |
Alexandre Ardhuin | d927c93 | 2018-09-12 08:29:29 +0200 | [diff] [blame] | 426 | final StringBuffer sb = StringBuffer(); |
Michael Goderbauer | 17057bb | 2017-03-01 10:11:56 -0800 | [diff] [blame] | 427 | _packages.forEach((String pkgName, Uri pkgUri) { |
| 428 | sb.writeln('$pkgName:$pkgUri'); |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 429 | }); |
Jonah Williams | 39f85f9 | 2019-10-02 12:46:33 -0700 | [diff] [blame] | 430 | return fs.file(fs.path.join(_tempDirs[0].path, '.packages')) |
| 431 | ..writeAsStringSync(sb.toString()); |
John McCutchan | 0de6916 | 2016-07-20 12:59:30 -0700 | [diff] [blame] | 432 | } |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 433 | |
Alexander Aprelev | 839fdbd | 2019-09-29 21:32:06 -0700 | [diff] [blame] | 434 | class RealMockVM extends Mock implements VM { |
| 435 | |
| 436 | } |
| 437 | |
| 438 | class RealMockVMService extends Mock implements VMService { |
| 439 | |
| 440 | } |
| 441 | |
| 442 | class 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 | |
| 452 | class MockOddlyFailingHttpClient extends Mock implements HttpClient {} |
| 453 | class MockHttpClientRequest extends Mock implements HttpClientRequest {} |
| 454 | class MockHttpHeaders extends Mock implements HttpHeaders {} |
Alexandre Ardhuin | f0553ba | 2019-09-30 18:48:23 +0200 | [diff] [blame] | 455 | class MockHttpClientResponse extends Mock implements HttpClientResponse {} |