mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
[flutter_tools] handle missing method on exit, debugDumpX (#80890)
This commit is contained in:
parent
35bffd20d4
commit
e67f14e511
@ -364,10 +364,16 @@ class FlutterDevice {
|
||||
}
|
||||
for (final FlutterView view in views) {
|
||||
if (view != null && view.uiIsolate != null) {
|
||||
// If successful, there will be no response from flutterExit.
|
||||
// If successful, there will be no response from flutterExit. If the exit
|
||||
// method is not registered, this will complete with `false`.
|
||||
unawaited(vmService.flutterExit(
|
||||
isolateId: view.uiIsolate.id,
|
||||
));
|
||||
).then((bool exited) async {
|
||||
// If exiting the app failed, fall back to stopApp
|
||||
if (!exited) {
|
||||
await device.stopApp(package, userIdentifier: userIdentifier);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
return vmService.service.onDone
|
||||
@ -378,10 +384,6 @@ class FlutterDevice {
|
||||
);
|
||||
})
|
||||
.timeout(timeoutDelay, onTimeout: () {
|
||||
// TODO(jonahwilliams): this only seems to fail on CI in the
|
||||
// flutter_attach_android_test. This log should help verify this
|
||||
// is where the tool is getting stuck.
|
||||
globals.logger.printTrace('error: vm service shutdown failed');
|
||||
return device.stopApp(package, userIdentifier: userIdentifier);
|
||||
});
|
||||
}
|
||||
|
@ -545,7 +545,7 @@ class FlutterVmService {
|
||||
'ext.flutter.debugDumpApp',
|
||||
isolateId: isolateId,
|
||||
);
|
||||
return response['data']?.toString();
|
||||
return response != null ? response['data']?.toString() : '';
|
||||
}
|
||||
|
||||
Future<String> flutterDebugDumpRenderTree({
|
||||
@ -556,7 +556,7 @@ class FlutterVmService {
|
||||
isolateId: isolateId,
|
||||
args: <String, Object>{}
|
||||
);
|
||||
return response['data']?.toString();
|
||||
return response != null ? response['data']?.toString() : '';
|
||||
}
|
||||
|
||||
Future<String> flutterDebugDumpLayerTree({
|
||||
@ -566,7 +566,7 @@ class FlutterVmService {
|
||||
'ext.flutter.debugDumpLayerTree',
|
||||
isolateId: isolateId,
|
||||
);
|
||||
return response['data']?.toString();
|
||||
return response != null ? response['data']?.toString() : '';
|
||||
}
|
||||
|
||||
Future<String> flutterDebugDumpSemanticsTreeInTraversalOrder({
|
||||
@ -576,7 +576,7 @@ class FlutterVmService {
|
||||
'ext.flutter.debugDumpSemanticsTreeInTraversalOrder',
|
||||
isolateId: isolateId,
|
||||
);
|
||||
return response['data']?.toString();
|
||||
return response != null ? response['data']?.toString() : '';
|
||||
}
|
||||
|
||||
Future<String> flutterDebugDumpSemanticsTreeInInverseHitTestOrder({
|
||||
@ -586,7 +586,7 @@ class FlutterVmService {
|
||||
'ext.flutter.debugDumpSemanticsTreeInInverseHitTestOrder',
|
||||
isolateId: isolateId,
|
||||
);
|
||||
return response['data']?.toString();
|
||||
return response != null ? response['data']?.toString() : '';
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> _flutterToggle(String name, {
|
||||
@ -701,15 +701,26 @@ class FlutterVmService {
|
||||
///
|
||||
/// This method is only supported by certain embedders. This is
|
||||
/// described by [Device.supportsFlutterExit].
|
||||
Future<void> flutterExit({
|
||||
Future<bool> flutterExit({
|
||||
@required String isolateId,
|
||||
}) {
|
||||
return invokeFlutterExtensionRpcRaw(
|
||||
'ext.flutter.exit',
|
||||
isolateId: isolateId,
|
||||
).catchError((dynamic error, StackTrace stackTrace) {
|
||||
// Do nothing on sentinel or exception, the isolate already exited.
|
||||
}, test: (dynamic error) => error is vm_service.SentinelException || error is vm_service.RPCError);
|
||||
}) async {
|
||||
try {
|
||||
final Map<String, Object> result = await invokeFlutterExtensionRpcRaw(
|
||||
'ext.flutter.exit',
|
||||
isolateId: isolateId,
|
||||
);
|
||||
// A response of `null` indicates that `invokeFlutterExtensionRpcRaw` caught an RPCError
|
||||
// with a missing method code. This can happen when attempting to quit a flutter app
|
||||
// that never registered the methods in the bindings.
|
||||
if (result == null) {
|
||||
return false;
|
||||
}
|
||||
} on vm_service.SentinelException {
|
||||
// Do nothing on sentinel, the isolate already exited.
|
||||
} on vm_service.RPCError {
|
||||
// Do nothing on RPCError, the isolate already exited.
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Return the current platform override for the flutter view running with
|
||||
|
@ -1674,6 +1674,42 @@ void main() {
|
||||
expect(fakeVmServiceHost.hasRemainingExpectations, false);
|
||||
}));
|
||||
|
||||
testUsingContext('FlutterDevice will exit an isolate that did not register the exit extension method', () => testbed.run(() async {
|
||||
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
||||
FakeVmServiceRequest(
|
||||
method: '_flutter.listViews',
|
||||
jsonResponse: <String, Object>{
|
||||
'views': <Object>[
|
||||
fakeFlutterView.toJson(),
|
||||
],
|
||||
},
|
||||
),
|
||||
FakeVmServiceRequest(
|
||||
method: 'getIsolate',
|
||||
args: <String, Object>{
|
||||
'isolateId': fakeUnpausedIsolate.id,
|
||||
},
|
||||
jsonResponse: fakeUnpausedIsolate.toJson(),
|
||||
),
|
||||
FakeVmServiceRequest(
|
||||
method: 'ext.flutter.exit',
|
||||
args: <String, Object>{
|
||||
'isolateId': fakeUnpausedIsolate.id,
|
||||
},
|
||||
errorCode: RPCErrorCodes.kMethodNotFound,
|
||||
),
|
||||
]);
|
||||
final TestFlutterDevice flutterDevice = TestFlutterDevice(
|
||||
mockDevice,
|
||||
);
|
||||
flutterDevice.vmService = fakeVmServiceHost.vmService;
|
||||
|
||||
await flutterDevice.exitApps(timeoutDelay: Duration.zero);
|
||||
|
||||
expect(mockDevice.appStopped, true);
|
||||
expect(fakeVmServiceHost.hasRemainingExpectations, false);
|
||||
}));
|
||||
|
||||
testUsingContext('FlutterDevice can exit from a release mode isolate with no VmService', () => testbed.run(() async {
|
||||
final TestFlutterDevice flutterDevice = TestFlutterDevice(
|
||||
mockDevice,
|
||||
|
@ -331,6 +331,101 @@ void main() {
|
||||
expect(fakeVmServiceHost.hasRemainingExpectations, false);
|
||||
});
|
||||
|
||||
testWithoutContext('flutterDebugDumpSemanticsTreeInTraversalOrder handles missing method', () async {
|
||||
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
|
||||
requests: <VmServiceExpectation>[
|
||||
const FakeVmServiceRequest(
|
||||
method: 'ext.flutter.debugDumpSemanticsTreeInTraversalOrder',
|
||||
args: <String, Object>{
|
||||
'isolateId': '1'
|
||||
},
|
||||
errorCode: RPCErrorCodes.kMethodNotFound,
|
||||
),
|
||||
]
|
||||
);
|
||||
|
||||
expect(await fakeVmServiceHost.vmService.flutterDebugDumpSemanticsTreeInTraversalOrder(
|
||||
isolateId: '1',
|
||||
), '');
|
||||
expect(fakeVmServiceHost.hasRemainingExpectations, false);
|
||||
});
|
||||
|
||||
testWithoutContext('flutterDebugDumpSemanticsTreeInInverseHitTestOrder handles missing method', () async {
|
||||
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
|
||||
requests: <VmServiceExpectation>[
|
||||
const FakeVmServiceRequest(
|
||||
method: 'ext.flutter.debugDumpSemanticsTreeInInverseHitTestOrder',
|
||||
args: <String, Object>{
|
||||
'isolateId': '1'
|
||||
},
|
||||
errorCode: RPCErrorCodes.kMethodNotFound,
|
||||
),
|
||||
]
|
||||
);
|
||||
|
||||
expect(await fakeVmServiceHost.vmService.flutterDebugDumpSemanticsTreeInInverseHitTestOrder(
|
||||
isolateId: '1',
|
||||
), '');
|
||||
expect(fakeVmServiceHost.hasRemainingExpectations, false);
|
||||
});
|
||||
|
||||
testWithoutContext('flutterDebugDumpLayerTree handles missing method', () async {
|
||||
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
|
||||
requests: <VmServiceExpectation>[
|
||||
const FakeVmServiceRequest(
|
||||
method: 'ext.flutter.debugDumpLayerTree',
|
||||
args: <String, Object>{
|
||||
'isolateId': '1'
|
||||
},
|
||||
errorCode: RPCErrorCodes.kMethodNotFound,
|
||||
),
|
||||
]
|
||||
);
|
||||
|
||||
expect(await fakeVmServiceHost.vmService.flutterDebugDumpLayerTree(
|
||||
isolateId: '1',
|
||||
), '');
|
||||
expect(fakeVmServiceHost.hasRemainingExpectations, false);
|
||||
});
|
||||
|
||||
testWithoutContext('flutterDebugDumpRenderTree handles missing method', () async {
|
||||
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
|
||||
requests: <VmServiceExpectation>[
|
||||
const FakeVmServiceRequest(
|
||||
method: 'ext.flutter.debugDumpRenderTree',
|
||||
args: <String, Object>{
|
||||
'isolateId': '1'
|
||||
},
|
||||
errorCode: RPCErrorCodes.kMethodNotFound,
|
||||
),
|
||||
]
|
||||
);
|
||||
|
||||
expect(await fakeVmServiceHost.vmService.flutterDebugDumpRenderTree(
|
||||
isolateId: '1',
|
||||
), '');
|
||||
expect(fakeVmServiceHost.hasRemainingExpectations, false);
|
||||
});
|
||||
|
||||
testWithoutContext('flutterDebugDumpApp handles missing method', () async {
|
||||
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
|
||||
requests: <VmServiceExpectation>[
|
||||
const FakeVmServiceRequest(
|
||||
method: 'ext.flutter.debugDumpApp',
|
||||
args: <String, Object>{
|
||||
'isolateId': '1'
|
||||
},
|
||||
errorCode: RPCErrorCodes.kMethodNotFound,
|
||||
),
|
||||
]
|
||||
);
|
||||
|
||||
expect(await fakeVmServiceHost.vmService.flutterDebugDumpApp(
|
||||
isolateId: '1',
|
||||
), '');
|
||||
expect(fakeVmServiceHost.hasRemainingExpectations, false);
|
||||
});
|
||||
|
||||
testWithoutContext('Framework service extension invocations return null if service disappears ', () async {
|
||||
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
|
||||
requests: <VmServiceExpectation>[
|
||||
|
Loading…
Reference in New Issue
Block a user