diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart index a0f6713eb20..9ad245753cf 100644 --- a/packages/flutter_tools/lib/src/resident_runner.dart +++ b/packages/flutter_tools/lib/src/resident_runner.dart @@ -714,23 +714,30 @@ abstract class ResidentHandlers { continue; } final List views = await device!.vmService!.getFlutterViews(); - for (final FlutterView view in views) { - final Map? rasterData = - await device.vmService!.renderFrameWithRasterStats( - viewId: view.id, - uiIsolateId: view.uiIsolate!.id, - ); - if (rasterData != null) { - final File tempFile = globals.fsUtils.getUniqueFile( - globals.fs.currentDirectory, - 'flutter_jank_metrics', - 'json', - ); - tempFile.writeAsStringSync(jsonEncode(rasterData), flush: true); - logger.printStatus('Wrote jank metrics to ${tempFile.absolute.path}'); - } else { - logger.printWarning('Unable to get jank metrics.'); + try { + for (final FlutterView view in views) { + final Map? rasterData = + await device.vmService!.renderFrameWithRasterStats( + viewId: view.id, + uiIsolateId: view.uiIsolate!.id, + ); + if (rasterData != null) { + final File tempFile = globals.fsUtils.getUniqueFile( + globals.fs.currentDirectory, + 'flutter_jank_metrics', + 'json', + ); + tempFile.writeAsStringSync(jsonEncode(rasterData), flush: true); + logger.printStatus('Wrote jank metrics to ${tempFile.absolute.path}'); + } else { + logger.printWarning('Unable to get jank metrics.'); + } } + } on vm_service.RPCError catch (err) { + if (err.code != RPCErrorCodes.kServerError || !err.message.contains('Raster status not supported on Impeller backend')) { + rethrow; + } + logger.printWarning('Unable to get jank metrics for Impeller renderer'); } } return true; diff --git a/packages/flutter_tools/test/general.shard/devfs_test.dart b/packages/flutter_tools/test/general.shard/devfs_test.dart index 117226c8186..561a587c11d 100644 --- a/packages/flutter_tools/test/general.shard/devfs_test.dart +++ b/packages/flutter_tools/test/general.shard/devfs_test.dart @@ -48,13 +48,13 @@ const FakeVmServiceRequest failingCreateDevFSRequest = FakeVmServiceRequest( args: { 'fsName': 'test', }, - errorCode: RPCErrorCodes.kServiceDisappeared, + error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared), ); const FakeVmServiceRequest failingDeleteDevFSRequest = FakeVmServiceRequest( method: '_deleteDevFS', args: {'fsName': 'test'}, - errorCode: RPCErrorCodes.kServiceDisappeared, + error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared), ); void main() { diff --git a/packages/flutter_tools/test/general.shard/resident_devtools_handler_test.dart b/packages/flutter_tools/test/general.shard/resident_devtools_handler_test.dart index 0aab7fd6100..8e07a195760 100644 --- a/packages/flutter_tools/test/general.shard/resident_devtools_handler_test.dart +++ b/packages/flutter_tools/test/general.shard/resident_devtools_handler_test.dart @@ -264,14 +264,14 @@ void main() { ), const FakeVmServiceRequest( method: kListViewsMethod, - errorCode: RPCErrorCodes.kServiceDisappeared, + error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared), ), const FakeVmServiceRequest( method: 'streamCancel', args: { 'streamId': 'Isolate', }, - errorCode: RPCErrorCodes.kServiceDisappeared, + error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared), ), ], httpAddress: Uri.parse('http://localhost:1234')); @@ -340,14 +340,14 @@ void main() { ), const FakeVmServiceRequest( method: kListViewsMethod, - errorCode: RPCErrorCodes.kServiceDisappeared, + error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared), ), const FakeVmServiceRequest( method: 'streamCancel', args: { 'streamId': 'Isolate', }, - errorCode: RPCErrorCodes.kServiceDisappeared, + error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared), ), ], httpAddress: Uri.parse('http://localhost:5678')); diff --git a/packages/flutter_tools/test/general.shard/resident_runner_helpers.dart b/packages/flutter_tools/test/general.shard/resident_runner_helpers.dart index d2671cfdb19..f6eaa090566 100644 --- a/packages/flutter_tools/test/general.shard/resident_runner_helpers.dart +++ b/packages/flutter_tools/test/general.shard/resident_runner_helpers.dart @@ -114,6 +114,18 @@ final FakeVmServiceRequest listViews = FakeVmServiceRequest( }, ); +const FakeVmServiceRequest renderFrameRasterStats = FakeVmServiceRequest( + method: kRenderFrameWithRasterStatsMethod, + args: { + 'viewId': 'a', + 'isolateId': '1', + }, + error: FakeRPCError( + code: RPCErrorCodes.kServerError, + error: 'Raster status not supported on Impeller backend', + ), +); + const FakeVmServiceRequest setAssetBundlePath = FakeVmServiceRequest( method: '_flutter.setAssetBundlePath', args: { diff --git a/packages/flutter_tools/test/general.shard/resident_runner_test.dart b/packages/flutter_tools/test/general.shard/resident_runner_test.dart index d51952e353f..6e86ca5f1cc 100644 --- a/packages/flutter_tools/test/general.shard/resident_runner_test.dart +++ b/packages/flutter_tools/test/general.shard/resident_runner_test.dart @@ -333,6 +333,32 @@ void main() { Usage: () => TestUsage(), })); + testUsingContext('ResidentRunner can handle an RPC exception from debugFrameJankMetrics', () => testbed.run(() async { + fakeVmServiceHost = FakeVmServiceHost(requests: [ + listViews, + listViews, + listViews, + renderFrameRasterStats, + ]); + final Completer futureConnectionInfo = Completer.sync(); + final Completer futureAppStart = Completer.sync(); + unawaited(residentRunner.attach( + appStartedCompleter: futureAppStart, + connectionInfoCompleter: futureConnectionInfo, + enableDevTools: true, + )); + await futureAppStart.future; + + final bool result = await residentRunner.debugFrameJankMetrics(); + expect(result, true); + expect((globals.flutterUsage as TestUsage).events, isEmpty); + expect(fakeAnalytics.sentEvents, isEmpty); + expect(fakeVmServiceHost?.hasRemainingExpectations, false); + expect((globals.logger as BufferLogger).warningText, contains('Unable to get jank metrics for Impeller renderer')); + }, overrides: { + Usage: () => TestUsage(), + })); + testUsingContext('ResidentRunner fails its operation if the device initialization is not complete', () => testbed.run(() async { fakeVmServiceHost = FakeVmServiceHost(requests: [ listViews, diff --git a/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart b/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart index 43b9f0931bc..169bab4bf74 100644 --- a/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart +++ b/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart @@ -973,7 +973,7 @@ void main() { const FakeVmServiceRequest( method: kHotRestartServiceName, // Failed response, - errorCode: RPCErrorCodes.kInternalError, + error: FakeRPCError(code: RPCErrorCodes.kInternalError), ), ]); setupMocks(); diff --git a/packages/flutter_tools/test/general.shard/terminal_handler_test.dart b/packages/flutter_tools/test/general.shard/terminal_handler_test.dart index a3a06840f5a..d5cf915032a 100644 --- a/packages/flutter_tools/test/general.shard/terminal_handler_test.dart +++ b/packages/flutter_tools/test/general.shard/terminal_handler_test.dart @@ -1125,7 +1125,7 @@ void main() { const FakeVmServiceRequest( method: 'ext.dwds.screenshot', // Failed response, - errorCode: RPCErrorCodes.kInternalError, + error: FakeRPCError(code: RPCErrorCodes.kInternalError), ), FakeVmServiceRequest( method: 'ext.flutter.debugAllowBanner', @@ -1165,7 +1165,7 @@ void main() { 'enabled': 'true', }, // Failed response, - errorCode: RPCErrorCodes.kInternalError, + error: const FakeRPCError(code: RPCErrorCodes.kInternalError), ), ], logger: logger, diff --git a/packages/flutter_tools/test/general.shard/tracing_test.dart b/packages/flutter_tools/test/general.shard/tracing_test.dart index f7638801b8e..e7719b26e63 100644 --- a/packages/flutter_tools/test/general.shard/tracing_test.dart +++ b/packages/flutter_tools/test/general.shard/tracing_test.dart @@ -140,7 +140,7 @@ void main() { ...vmServiceSetup, const FakeVmServiceRequest( method: 'getVMTimeline', - errorCode: RPCErrorCodes.kServiceDisappeared, + error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared), ), const FakeVmServiceRequest( method: 'setVMTimelineFlags', diff --git a/packages/flutter_tools/test/general.shard/vmservice_test.dart b/packages/flutter_tools/test/general.shard/vmservice_test.dart index 4b91dac99f3..d93f3e3e6d2 100644 --- a/packages/flutter_tools/test/general.shard/vmservice_test.dart +++ b/packages/flutter_tools/test/general.shard/vmservice_test.dart @@ -301,7 +301,7 @@ void main() { args: { 'isolateId': '1', }, - errorCode: RPCErrorCodes.kMethodNotFound, + error: FakeRPCError(code: RPCErrorCodes.kMethodNotFound), ), ] ); @@ -320,7 +320,7 @@ void main() { args: { 'isolateId': '1', }, - errorCode: RPCErrorCodes.kMethodNotFound, + error: FakeRPCError(code: RPCErrorCodes.kMethodNotFound), ), ] ); @@ -339,7 +339,7 @@ void main() { args: { 'isolateId': '1', }, - errorCode: RPCErrorCodes.kMethodNotFound, + error: FakeRPCError(code: RPCErrorCodes.kMethodNotFound), ), ] ); @@ -358,7 +358,7 @@ void main() { args: { 'isolateId': '1', }, - errorCode: RPCErrorCodes.kMethodNotFound, + error: FakeRPCError(code: RPCErrorCodes.kMethodNotFound), ), ] ); @@ -377,7 +377,7 @@ void main() { args: { 'isolateId': '1', }, - errorCode: RPCErrorCodes.kMethodNotFound, + error: FakeRPCError(code: RPCErrorCodes.kMethodNotFound), ), ] ); @@ -396,7 +396,7 @@ void main() { args: { 'isolateId': '1', }, - errorCode: RPCErrorCodes.kMethodNotFound, + error: FakeRPCError(code: RPCErrorCodes.kMethodNotFound), ), ] ); @@ -436,26 +436,26 @@ void main() { args: { 'viewId': '1234', }, - errorCode: RPCErrorCodes.kServiceDisappeared, + error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared), ), const FakeVmServiceRequest( method: kListViewsMethod, - errorCode: RPCErrorCodes.kServiceDisappeared, + error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared), ), const FakeVmServiceRequest( method: kScreenshotSkpMethod, - errorCode: RPCErrorCodes.kServiceDisappeared, + error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared), ), const FakeVmServiceRequest( method: 'setVMTimelineFlags', args: { 'recordedStreams': ['test'], }, - errorCode: RPCErrorCodes.kServiceDisappeared, + error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared), ), const FakeVmServiceRequest( method: 'getVMTimeline', - errorCode: RPCErrorCodes.kServiceDisappeared, + error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared), ), const FakeVmServiceRequest( method: kRenderFrameWithRasterStatsMethod, @@ -463,7 +463,7 @@ void main() { 'viewId': '1', 'isolateId': '12', }, - errorCode: RPCErrorCodes.kServiceDisappeared, + error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared), ), ] ); @@ -495,9 +495,13 @@ void main() { testWithoutContext('getIsolateOrNull returns null if service disappears ', () async { final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost( requests: [ - const FakeVmServiceRequest(method: 'getIsolate', args: { - 'isolateId': 'isolate/123', - }, errorCode: RPCErrorCodes.kServiceDisappeared), + const FakeVmServiceRequest( + method: 'getIsolate', + args: { + 'isolateId': 'isolate/123', + }, + error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared), + ), ] ); @@ -702,7 +706,7 @@ void main() { args: { 'isolateId': '1', }, - errorCode: RPCErrorCodes.kServiceDisappeared, + error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared), ), // Assume a different isolate returns. FakeVmServiceStreamResponse( @@ -734,7 +738,7 @@ void main() { 'streamId': 'Isolate', }, // Stream already subscribed - https://github.com/dart-lang/sdk/blob/main/runtime/vm/service/service.md#streamlisten - errorCode: 103, + error: FakeRPCError(code: 103), ), listViewsRequest, FakeVmServiceRequest( @@ -802,14 +806,14 @@ void main() { ), const FakeVmServiceRequest( method: kListViewsMethod, - errorCode: RPCErrorCodes.kServiceDisappeared, + error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared), ), const FakeVmServiceRequest( method: 'streamCancel', args: { 'streamId': 'Isolate', }, - errorCode: RPCErrorCodes.kServiceDisappeared, + error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared), ), ]); diff --git a/packages/flutter_tools/test/src/fake_vm_services.dart b/packages/flutter_tools/test/src/fake_vm_services.dart index e08f2072491..75be62f87e3 100644 --- a/packages/flutter_tools/test/src/fake_vm_services.dart +++ b/packages/flutter_tools/test/src/fake_vm_services.dart @@ -39,7 +39,7 @@ class FakeVmServiceHost { expect(_requests, isEmpty); return; } - if (fakeRequest.errorCode == null) { + if (fakeRequest.error == null) { _input.add(json.encode({ 'jsonrpc': '2.0', 'id': request['id'], @@ -50,8 +50,8 @@ class FakeVmServiceHost { 'jsonrpc': '2.0', 'id': request['id'], 'error': { - 'code': fakeRequest.errorCode, - 'message': 'error', + 'code': fakeRequest.error!.code, + 'message': fakeRequest.error!.error, }, })); } @@ -90,12 +90,22 @@ abstract class VmServiceExpectation { bool get isRequest; } +class FakeRPCError { + const FakeRPCError({ + required this.code, + this.error = 'error', + }); + + final int code; + final String error; +} + class FakeVmServiceRequest implements VmServiceExpectation { const FakeVmServiceRequest({ required this.method, this.args = const {}, this.jsonResponse, - this.errorCode, + this.error, this.close = false, }); @@ -106,7 +116,7 @@ class FakeVmServiceRequest implements VmServiceExpectation { /// If non-null, the error code for a [vm_service.RPCError] in place of a /// standard response. - final int? errorCode; + final FakeRPCError? error; final Map? args; final Map? jsonResponse;