|
2 | 2 | // Use of this source code is governed by a BSD-style license that can be |
3 | 3 | // found in the LICENSE file. |
4 | 4 |
|
| 5 | +import 'dart:async'; |
5 | 6 | import 'dart:convert'; |
6 | 7 |
|
7 | 8 | import 'package:file/file.dart'; |
@@ -258,7 +259,7 @@ void main() { |
258 | 259 | expect(devFS.lastCompiled, isNot(previousCompile)); |
259 | 260 | }); |
260 | 261 |
|
261 | | - testWithoutContext('DevFS uses provided DevFSWriter instead of default HTTP writer', () async { |
| 262 | + testWithoutContext('DevFS uses provided DevFSWriter instead of default HTTP writer', () async { |
262 | 263 | final FileSystem fileSystem = MemoryFileSystem.test(); |
263 | 264 | final FakeDevFSWriter writer = FakeDevFSWriter(); |
264 | 265 | final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost( |
@@ -332,6 +333,84 @@ void main() { |
332 | 333 | Uri.parse('goodbye'): DevFSFileContent(file), |
333 | 334 | }, Uri.parse('/foo/bar/devfs/')), throwsA(isA<DevFSException>())); |
334 | 335 | }); |
| 336 | + |
| 337 | + testWithoutContext('test handles request closure hangs', () async { |
| 338 | + final FileSystem fileSystem = MemoryFileSystem.test(); |
| 339 | + final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost( |
| 340 | + requests: <VmServiceExpectation>[createDevFSRequest], |
| 341 | + ); |
| 342 | + final HttpClient httpClient = MockHttpClient(); |
| 343 | + final MockHttpClientRequest httpRequest = MockHttpClientRequest(); |
| 344 | + when(httpRequest.headers).thenReturn(MockHttpHeaders()); |
| 345 | + when(httpClient.putUrl(any)).thenAnswer((Invocation invocation) { |
| 346 | + return Future<HttpClientRequest>.value(httpRequest); |
| 347 | + }); |
| 348 | + int closeCount = 0; |
| 349 | + final Completer<MockHttpClientResponse> hanger = Completer<MockHttpClientResponse>(); |
| 350 | + final Completer<MockHttpClientResponse> succeeder = Completer<MockHttpClientResponse>(); |
| 351 | + final List<Completer<MockHttpClientResponse>> closeCompleters = |
| 352 | + <Completer<MockHttpClientResponse>>[hanger, succeeder]; |
| 353 | + succeeder.complete(MockHttpClientResponse()); |
| 354 | + |
| 355 | + when(httpRequest.close()).thenAnswer((Invocation invocation) { |
| 356 | + final Completer<MockHttpClientResponse> completer = closeCompleters[closeCount]; |
| 357 | + closeCount += 1; |
| 358 | + return completer.future; |
| 359 | + }); |
| 360 | + when(httpRequest.abort()).thenAnswer((_) { |
| 361 | + hanger.completeError(const HttpException('aborted')); |
| 362 | + }); |
| 363 | + when(httpRequest.done).thenAnswer((_) { |
| 364 | + if (closeCount == 1) { |
| 365 | + return hanger.future; |
| 366 | + } else if (closeCount == 2) { |
| 367 | + return succeeder.future; |
| 368 | + } else { |
| 369 | + // This branch shouldn't happen. |
| 370 | + fail('This branch should not happen'); |
| 371 | + } |
| 372 | + }); |
| 373 | + |
| 374 | + final BufferLogger logger = BufferLogger.test(); |
| 375 | + final DevFS devFS = DevFS( |
| 376 | + fakeVmServiceHost.vmService, |
| 377 | + 'test', |
| 378 | + fileSystem.currentDirectory, |
| 379 | + fileSystem: fileSystem, |
| 380 | + logger: logger, |
| 381 | + osUtils: FakeOperatingSystemUtils(), |
| 382 | + httpClient: httpClient, |
| 383 | + ); |
| 384 | + |
| 385 | + await devFS.create(); |
| 386 | + final DateTime previousCompile = devFS.lastCompiled; |
| 387 | + |
| 388 | + final MockResidentCompiler residentCompiler = MockResidentCompiler(); |
| 389 | + when(residentCompiler.recompile( |
| 390 | + any, |
| 391 | + any, |
| 392 | + outputPath: anyNamed('outputPath'), |
| 393 | + packageConfig: anyNamed('packageConfig'), |
| 394 | + )).thenAnswer((Invocation invocation) async { |
| 395 | + fileSystem.file('example').createSync(); |
| 396 | + return const CompilerOutput('lib/foo.txt.dill', 0, <Uri>[]); |
| 397 | + }); |
| 398 | + |
| 399 | + final UpdateFSReport report = await devFS.update( |
| 400 | + mainUri: Uri.parse('lib/main.dart'), |
| 401 | + generator: residentCompiler, |
| 402 | + dillOutputPath: 'lib/foo.dill', |
| 403 | + pathToReload: 'lib/foo.txt.dill', |
| 404 | + trackWidgetCreation: false, |
| 405 | + invalidatedFiles: <Uri>[], |
| 406 | + packageConfig: PackageConfig.empty, |
| 407 | + ); |
| 408 | + |
| 409 | + expect(report.success, true); |
| 410 | + expect(devFS.lastCompiled, isNot(previousCompile)); |
| 411 | + expect(closeCount, 2); |
| 412 | + expect(logger.errorText, ''); |
| 413 | + }); |
335 | 414 | } |
336 | 415 |
|
337 | 416 | class MockHttpClientRequest extends Mock implements HttpClientRequest {} |
|
0 commit comments