Unverified Commit 0cd2ece5 authored by Zachary Anderson's avatar Zachary Anderson Committed by GitHub

[flutter_tools] Hanlde OSError in places where we've seen it thrown (#52491)

parent 183da8f8
...@@ -78,6 +78,7 @@ export 'dart:io' ...@@ -78,6 +78,7 @@ export 'dart:io'
IOSink, IOSink,
// Link NO! Use `file_system.dart` // Link NO! Use `file_system.dart`
// NetworkInterface NO! Use `io.dart` // NetworkInterface NO! Use `io.dart`
OSError,
pid, pid,
// Platform NO! use `platform.dart` // Platform NO! use `platform.dart`
Process, Process,
......
...@@ -319,7 +319,12 @@ class _DevFSHttpWriter { ...@@ -319,7 +319,12 @@ class _DevFSHttpWriter {
onError: (dynamic error) { globals.printTrace('error: $error'); }, onError: (dynamic error) { globals.printTrace('error: $error'); },
cancelOnError: true); cancelOnError: true);
break; break;
} on Exception catch (error, trace) { } catch (error, trace) { // ignore: avoid_catches_without_on_clauses
// We treat OSError as an Exception.
// See: https://github.com/dart-lang/sdk/issues/40934
if (error is! Exception && error is! OSError) {
rethrow;
}
if (!_completer.isCompleted) { if (!_completer.isCompleted) {
globals.printTrace('Error writing "$deviceUri" to DevFS: $error'); globals.printTrace('Error writing "$deviceUri" to DevFS: $error');
if (retry > 0) { if (retry > 0) {
......
...@@ -58,10 +58,10 @@ class MDnsObservatoryDiscovery { ...@@ -58,10 +58,10 @@ class MDnsObservatoryDiscovery {
try { try {
await client.start(); await client.start();
final List<PtrResourceRecord> pointerRecords = await client final List<PtrResourceRecord> pointerRecords = await client
.lookup<PtrResourceRecord>( .lookup<PtrResourceRecord>(
ResourceRecordQuery.serverPointer(dartObservatoryName), ResourceRecordQuery.serverPointer(dartObservatoryName),
) )
.toList(); .toList();
if (pointerRecords.isEmpty) { if (pointerRecords.isEmpty) {
globals.printTrace('No pointer records found.'); globals.printTrace('No pointer records found.');
return null; return null;
...@@ -69,8 +69,8 @@ class MDnsObservatoryDiscovery { ...@@ -69,8 +69,8 @@ class MDnsObservatoryDiscovery {
// We have no guarantee that we won't get multiple hits from the same // We have no guarantee that we won't get multiple hits from the same
// service on this. // service on this.
final Set<String> uniqueDomainNames = pointerRecords final Set<String> uniqueDomainNames = pointerRecords
.map<String>((PtrResourceRecord record) => record.domainName) .map<String>((PtrResourceRecord record) => record.domainName)
.toSet(); .toSet();
String domainName; String domainName;
if (applicationId != null) { if (applicationId != null) {
...@@ -98,10 +98,10 @@ class MDnsObservatoryDiscovery { ...@@ -98,10 +98,10 @@ class MDnsObservatoryDiscovery {
globals.printTrace('Checking for available port on $domainName'); globals.printTrace('Checking for available port on $domainName');
// Here, if we get more than one, it should just be a duplicate. // Here, if we get more than one, it should just be a duplicate.
final List<SrvResourceRecord> srv = await client final List<SrvResourceRecord> srv = await client
.lookup<SrvResourceRecord>( .lookup<SrvResourceRecord>(
ResourceRecordQuery.service(domainName), ResourceRecordQuery.service(domainName),
) )
.toList(); .toList();
if (srv.isEmpty) { if (srv.isEmpty) {
return null; return null;
} }
...@@ -133,6 +133,11 @@ class MDnsObservatoryDiscovery { ...@@ -133,6 +133,11 @@ class MDnsObservatoryDiscovery {
authCode += '/'; authCode += '/';
} }
return MDnsObservatoryDiscoveryResult(srv.first.port, authCode); return MDnsObservatoryDiscoveryResult(srv.first.port, authCode);
} on OSError catch (e) {
// OSError is neither an Error nor and Exception, so we wrap it in a
// SocketException and rethrow.
// See: https://github.com/dart-lang/sdk/issues/40934
throw SocketException('mdns query failed', osError: e);
} finally { } finally {
client.stop(); client.stop();
} }
......
...@@ -109,59 +109,66 @@ void main() { ...@@ -109,59 +109,66 @@ void main() {
HttpOverrides.global = savedHttpOverrides; HttpOverrides.global = savedHttpOverrides;
}); });
testUsingContext('retry uploads when failure', () async { final List<dynamic> exceptions = <dynamic>[
final File file = fs.file(fs.path.join(basePath, filePath)); Exception('Connection resert by peer'),
await file.parent.create(recursive: true); const OSError('Connection reset by peer'),
file.writeAsBytesSync(<int>[1, 2, 3]); ];
// simulate package
await _createPackage(fs, 'somepkg', 'somefile.txt'); for (final dynamic exception in exceptions) {
testUsingContext('retry uploads when failure: $exception', () async {
final RealMockVMService vmService = RealMockVMService(); final File file = fs.file(fs.path.join(basePath, filePath));
final RealMockVM vm = RealMockVM(); await file.parent.create(recursive: true);
final Map<String, dynamic> response = <String, dynamic>{ 'uri': 'file://abc' }; file.writeAsBytesSync(<int>[1, 2, 3]);
when(vm.createDevFS(any)).thenAnswer((Invocation invocation) { // simulate package
return Future<Map<String, dynamic>>.value(response); await _createPackage(fs, 'somepkg', 'somefile.txt');
});
when(vmService.vm).thenReturn(vm); final RealMockVMService vmService = RealMockVMService();
final RealMockVM vm = RealMockVM();
reset(httpClient); final Map<String, dynamic> response = <String, dynamic>{ 'uri': 'file://abc' };
when(vm.createDevFS(any)).thenAnswer((Invocation invocation) {
final MockHttpClientRequest httpRequest = MockHttpClientRequest(); return Future<Map<String, dynamic>>.value(response);
when(httpRequest.headers).thenReturn(MockHttpHeaders()); });
when(httpClient.putUrl(any)).thenAnswer((Invocation invocation) { when(vmService.vm).thenReturn(vm);
return Future<HttpClientRequest>.value(httpRequest);
reset(httpClient);
final MockHttpClientRequest httpRequest = MockHttpClientRequest();
when(httpRequest.headers).thenReturn(MockHttpHeaders());
when(httpClient.putUrl(any)).thenAnswer((Invocation invocation) {
return Future<HttpClientRequest>.value(httpRequest);
});
final MockHttpClientResponse httpClientResponse = MockHttpClientResponse();
int nRequest = 0;
const int kFailedAttempts = 5;
when(httpRequest.close()).thenAnswer((Invocation invocation) {
if (nRequest++ < kFailedAttempts) {
throw exception;
}
return Future<HttpClientResponse>.value(httpClientResponse);
});
final DevFS devFS = DevFS(vmService, 'test', tempDir);
await devFS.create();
final MockResidentCompiler residentCompiler = MockResidentCompiler();
final UpdateFSReport report = await devFS.update(
mainPath: 'lib/foo.txt',
generator: residentCompiler,
pathToReload: 'lib/foo.txt.dill',
trackWidgetCreation: false,
invalidatedFiles: <Uri>[],
);
expect(report.syncedBytes, 22);
expect(report.success, isTrue);
verify(httpClient.putUrl(any)).called(kFailedAttempts + 1);
verify(httpRequest.close()).called(kFailedAttempts + 1);
}, overrides: <Type, Generator>{
FileSystem: () => fs,
HttpClientFactory: () => () => httpClient,
ProcessManager: () => FakeProcessManager.any(),
}); });
final MockHttpClientResponse httpClientResponse = MockHttpClientResponse(); }
int nRequest = 0;
const int kFailedAttempts = 5;
when(httpRequest.close()).thenAnswer((Invocation invocation) {
if (nRequest++ < kFailedAttempts) {
throw Exception('Connection resert by peer');
}
return Future<HttpClientResponse>.value(httpClientResponse);
});
final DevFS devFS = DevFS(vmService, 'test', tempDir);
await devFS.create();
final MockResidentCompiler residentCompiler = MockResidentCompiler();
final UpdateFSReport report = await devFS.update(
mainPath: 'lib/foo.txt',
generator: residentCompiler,
pathToReload: 'lib/foo.txt.dill',
trackWidgetCreation: false,
invalidatedFiles: <Uri>[],
);
expect(report.syncedBytes, 22);
expect(report.success, isTrue);
verify(httpClient.putUrl(any)).called(kFailedAttempts + 1);
verify(httpRequest.close()).called(kFailedAttempts + 1);
}, overrides: <Type, Generator>{
FileSystem: () => fs,
HttpClientFactory: () => () => httpClient,
ProcessManager: () => FakeProcessManager.any(),
});
}); });
group('devfs remote', () { group('devfs remote', () {
......
...@@ -192,6 +192,21 @@ void main() { ...@@ -192,6 +192,21 @@ void main() {
final int port = (await portDiscovery.query(applicationId: 'bar'))?.port; final int port = (await portDiscovery.query(applicationId: 'bar'))?.port;
expect(port, isNull); expect(port, isNull);
}); });
testUsingContext('Throws SocketException when client throws OSError on start', () async {
final MDnsClient client = MockMDnsClient();
when(client.start()).thenAnswer((_) {
throw const OSError('Operation not suppoted on socket', 102);
});
final MDnsObservatoryDiscovery portDiscovery = MDnsObservatoryDiscovery(
mdnsClient: client,
);
expect(
() async => await portDiscovery.query(),
throwsA(isA<SocketException>()),
);
});
}); });
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment