when setting up the log reader for a device during flutter run, discard any RPCError thrown due to the device being disconnected (#155049)

Fixes https://github.com/flutter/flutter/issues/154903

This PR contains some refactoring. To make the actual change easier to figure out, I've tried to separate parts of the change into multiple commits for easier reviewing 🙂.

**I plan on cherry-picking this change to stable.**
This commit is contained in:
Andrew Kolos 2024-09-12 08:42:15 -07:00 committed by GitHub
parent 50a190caec
commit d8f8613a5b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 72 additions and 8 deletions

View File

@ -416,8 +416,19 @@ class FlutterDevice {
_loggingSubscription = null;
}
Future<void> initLogReader() async {
final vm_service.VM vm = await vmService!.service.getVM();
/// Attempts to set up reading logs from the Flutter app on the device.
///
/// This can fail if the device if no longer connected.
Future<void> tryInitLogReader() async {
final vm_service.VM? vm = await vmService!.getVmGuarded();
if (vm == null) {
globals.printError(
'Unable to initiate log reader for device'
'${device?.name}, because the Flutter VM service connection '
'is closed.',
);
return;
}
final DeviceLogReader logReader = await device!.getLogReader(app: package);
logReader.appPid = vm.pid;
}

View File

@ -104,7 +104,7 @@ class ColdRunner extends ResidentRunner {
if (device!.vmService == null) {
continue;
}
await device.initLogReader();
await device.tryInitLogReader();
globals.printTrace('Connected to ${device.device!.name}');
}
@ -154,7 +154,7 @@ class ColdRunner extends ResidentRunner {
}
for (final FlutterDevice? device in flutterDevices) {
await device!.initLogReader();
await device!.tryInitLogReader();
}
for (final FlutterDevice? device in flutterDevices) {
final List<FlutterView> views = await device!.vmService!.getFlutterViews();

View File

@ -261,7 +261,7 @@ class HotRunner extends ResidentRunner {
}
for (final FlutterDevice? device in flutterDevices) {
await device!.initLogReader();
await device!.tryInitLogReader();
device
.developmentShaderCompiler
.configureCompiler(device.targetPlatform);

View File

@ -491,6 +491,22 @@ class FlutterVmService {
final Uri? wsAddress;
final Uri? httpAddress;
/// Calls [service.getVM]. However, in the case that an [vm_service.RPCError]
/// is thrown due to the service being disconnected, the error is discarded
/// and null is returned.
Future<vm_service.VM?> getVmGuarded() async {
try {
return await service.getVM();
} on vm_service.RPCError catch (err) {
if (err.code == RPCErrorCodes.kServiceDisappeared ||
err.message.contains('Service connection disposed')) {
globals.printTrace('VmService.getVm call failed: $err');
return null;
}
rethrow;
}
}
Future<vm_service.Response?> callMethodWrapper(
String method, {
String? isolateId,

View File

@ -165,7 +165,7 @@ class FakeFlutterDevice extends Fake implements FlutterDevice {
}
@override
Future<void> initLogReader() async { }
Future<void> tryInitLogReader() async { }
}
class FakeDevice extends Fake implements Device {

View File

@ -228,7 +228,7 @@ class FakeFlutterDevice extends Fake implements FlutterDevice {
Future<void> stopEchoingDeviceLog() async { }
@override
Future<void> initLogReader() async { }
Future<void> tryInitLogReader() async { }
@override
Future<Uri> setupDevFS(String fsName, Directory rootDirectory) async {

View File

@ -1934,6 +1934,43 @@ flutter:
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('FlutterDevice does not throw when unable to initiate log reader due to VM service disconnection', () async {
fakeVmServiceHost = FakeVmServiceHost(
requests: <VmServiceExpectation>[
const FakeVmServiceRequest(
method: 'getVM',
error: FakeRPCError(
code: RPCErrorCodes.kServerError,
error: 'Service connection disposed',
),
),
],
);
final TestFlutterDevice flutterDevice = TestFlutterDevice(device);
flutterDevice.vmService = fakeVmServiceHost!.vmService;
await flutterDevice.tryInitLogReader();
final BufferLogger logger = globals.logger as BufferLogger;
expect(
logger.traceText,
contains(
'VmService.getVm call failed: getVM: (-32000) '
'Service connection disposed\n',
),
);
expect(
logger.errorText,
contains(
'Unable to initiate log reader for deviceFakeDevice, because '
'the Flutter VM service connection is closed.\n',
),
);
}, overrides: <Type, Generator>{
Logger: () => BufferLogger.test(),
Artifacts: () => Artifacts.test(),
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('Uses existing DDS URI from exception field', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
final FakeDevice device = FakeDevice()

View File

@ -1755,7 +1755,7 @@ class FakeFlutterDevice extends Fake implements FlutterDevice {
Future<void> stopEchoingDeviceLog() async {}
@override
Future<void> initLogReader() async {}
Future<void> tryInitLogReader() async {}
@override
Future<Uri?> setupDevFS(String fsName, Directory rootDirectory) async {