diff --git a/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart b/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart index a4ef4e4df7..55361794ff 100644 --- a/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart +++ b/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart @@ -435,7 +435,8 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive). final String targetPlatform = getNameForTargetPlatform(TargetPlatform.web_javascript); final String sdkName = await device!.device!.sdkNameAndVersion; - late UpdateFSReport report; + // Will be null if there is no report. + final UpdateFSReport? report; if (debuggingOptions.buildInfo.isDebug && !debuggingOptions.webUseWasm) { await runSourceGenerators(); // Don't reset the resident compiler for web, since the extra recompile is @@ -469,6 +470,7 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive). return OperationResult(1, 'Failed to recompile application.'); } } else { + report = null; try { final WebBuilder webBuilder = WebBuilder( logger: _logger, @@ -490,8 +492,9 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive). } } - late Duration reloadDuration; - late Duration reassembleDuration; + // Both will be null when not assigned. + Duration? reloadDuration; + Duration? reassembleDuration; try { if (!deviceIsDebuggable) { _logger.printStatus('Recompile complete. Page requires refresh.'); @@ -578,12 +581,12 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive). fullRestart: true, reason: reason, overallTimeInMs: elapsed.inMilliseconds, - syncedBytes: report.syncedBytes, - invalidatedSourcesCount: report.invalidatedSourcesCount, - transferTimeInMs: report.transferDuration.inMilliseconds, - compileTimeInMs: report.compileDuration.inMilliseconds, - findInvalidatedTimeInMs: report.findInvalidatedDuration.inMilliseconds, - scannedSourcesCount: report.scannedSourcesCount, + syncedBytes: report?.syncedBytes, + invalidatedSourcesCount: report?.invalidatedSourcesCount, + transferTimeInMs: report?.transferDuration.inMilliseconds, + compileTimeInMs: report?.compileDuration.inMilliseconds, + findInvalidatedTimeInMs: report?.findInvalidatedDuration.inMilliseconds, + scannedSourcesCount: report?.scannedSourcesCount, ).send(); _analytics.send( Event.hotRunnerInfo( @@ -594,12 +597,12 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive). fullRestart: true, reason: reason, overallTimeInMs: elapsed.inMilliseconds, - syncedBytes: report.syncedBytes, - invalidatedSourcesCount: report.invalidatedSourcesCount, - transferTimeInMs: report.transferDuration.inMilliseconds, - compileTimeInMs: report.compileDuration.inMilliseconds, - findInvalidatedTimeInMs: report.findInvalidatedDuration.inMilliseconds, - scannedSourcesCount: report.scannedSourcesCount, + syncedBytes: report?.syncedBytes, + invalidatedSourcesCount: report?.invalidatedSourcesCount, + transferTimeInMs: report?.transferDuration.inMilliseconds, + compileTimeInMs: report?.compileDuration.inMilliseconds, + findInvalidatedTimeInMs: report?.findInvalidatedDuration.inMilliseconds, + scannedSourcesCount: report?.scannedSourcesCount, ), ); } else { @@ -618,14 +621,14 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive). fullRestart: false, reason: reason, overallTimeInMs: elapsed.inMilliseconds, - syncedBytes: report.syncedBytes, - invalidatedSourcesCount: report.invalidatedSourcesCount, - transferTimeInMs: report.transferDuration.inMilliseconds, - compileTimeInMs: report.compileDuration.inMilliseconds, - findInvalidatedTimeInMs: report.findInvalidatedDuration.inMilliseconds, - scannedSourcesCount: report.scannedSourcesCount, - reassembleTimeInMs: reassembleDuration.inMilliseconds, - reloadVMTimeInMs: reloadDuration.inMilliseconds, + syncedBytes: report?.syncedBytes, + invalidatedSourcesCount: report?.invalidatedSourcesCount, + transferTimeInMs: report?.transferDuration.inMilliseconds, + compileTimeInMs: report?.compileDuration.inMilliseconds, + findInvalidatedTimeInMs: report?.findInvalidatedDuration.inMilliseconds, + scannedSourcesCount: report?.scannedSourcesCount, + reassembleTimeInMs: reassembleDuration?.inMilliseconds, + reloadVMTimeInMs: reloadDuration?.inMilliseconds, ).send(); _analytics.send( Event.hotRunnerInfo( @@ -636,14 +639,14 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive). fullRestart: false, reason: reason, overallTimeInMs: elapsed.inMilliseconds, - syncedBytes: report.syncedBytes, - invalidatedSourcesCount: report.invalidatedSourcesCount, - transferTimeInMs: report.transferDuration.inMilliseconds, - compileTimeInMs: report.compileDuration.inMilliseconds, - findInvalidatedTimeInMs: report.findInvalidatedDuration.inMilliseconds, - scannedSourcesCount: report.scannedSourcesCount, - reassembleTimeInMs: reassembleDuration.inMilliseconds, - reloadVMTimeInMs: reloadDuration.inMilliseconds, + syncedBytes: report?.syncedBytes, + invalidatedSourcesCount: report?.invalidatedSourcesCount, + transferTimeInMs: report?.transferDuration.inMilliseconds, + compileTimeInMs: report?.compileDuration.inMilliseconds, + findInvalidatedTimeInMs: report?.findInvalidatedDuration.inMilliseconds, + scannedSourcesCount: report?.scannedSourcesCount, + reassembleTimeInMs: reassembleDuration?.inMilliseconds, + reloadVMTimeInMs: reloadDuration?.inMilliseconds, ), ); } 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 93fc2c05ae..03c15c46fa 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 @@ -17,6 +17,7 @@ import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/terminal.dart'; import 'package:flutter_tools/src/base/time.dart'; import 'package:flutter_tools/src/build_info.dart'; +import 'package:flutter_tools/src/build_system/build_system.dart'; import 'package:flutter_tools/src/build_system/tools/shader_compiler.dart'; import 'package:flutter_tools/src/compile.dart'; import 'package:flutter_tools/src/dart/pub.dart'; @@ -46,6 +47,7 @@ import '../src/fake_pub_deps.dart'; import '../src/fake_vm_services.dart'; import '../src/fakes.dart' as test_fakes; import '../src/package_config.dart'; +import '../src/test_build_system.dart'; const List kAttachLogExpectations = [ FakeVmServiceRequest(method: 'streamListen', args: {'streamId': 'Stdout'}), @@ -914,6 +916,88 @@ name: my_app }, ); + // Regression test for https://github.com/flutter/flutter/issues/167887. + testUsingContext( + 'WASM builds report analysis without crashing', + () async { + final BufferLogger logger = BufferLogger.test(); + final ResidentRunner residentWebRunner = setUpResidentRunner( + flutterDevice, + logger: logger, + systemClock: SystemClock.fixed(DateTime(2001)), + debuggingOptions: DebuggingOptions.enabled( + const BuildInfo( + BuildMode.debug, + null, + trackWidgetCreation: true, + treeShakeIcons: false, + packageConfigPath: '.dart_tool/package_config.json', + // Hot reload only supported with these flags for now. + extraFrontEndOptions: kDdcLibraryBundleFlags, + ), + webUseWasm: true, + ), + ); + fakeVmServiceHost = FakeVmServiceHost( + requests: [ + ...kAttachExpectations, + const FakeVmServiceRequest( + method: kReloadSourcesServiceName, + args: {'isolateId': ''}, + jsonResponse: {'type': 'ReloadReport', 'success': true}, + ), + const FakeVmServiceRequest( + method: 'ext.flutter.reassemble', + jsonResponse: {'type': 'ReloadReport', 'success': true}, + ), + const FakeVmServiceRequest( + method: 'streamListen', + args: {'streamId': 'Isolate'}, + ), + ], + ); + setupMocks(); + final TestChromiumLauncher chromiumLauncher = TestChromiumLauncher(); + final FakeProcess process = FakeProcess(); + final Chromium chrome = Chromium( + 1, + chromeConnection, + chromiumLauncher: chromiumLauncher, + process: process, + logger: logger, + ); + chromiumLauncher.setInstance(chrome); + + flutterDevice.device = GoogleChromeDevice( + fileSystem: fileSystem, + chromiumLauncher: chromiumLauncher, + logger: BufferLogger.test(), + platform: FakePlatform(), + processManager: FakeProcessManager.any(), + ); + webDevFS.report = UpdateFSReport(success: true); + + final Completer connectionInfoCompleter = + Completer(); + unawaited(residentWebRunner.run(connectionInfoCompleter: connectionInfoCompleter)); + final DebugConnectionInfo debugConnectionInfo = await connectionInfoCompleter.future; + + expect(debugConnectionInfo, isNotNull); + + final OperationResult result = await residentWebRunner.restart(); + expect(logger.statusText, contains('Reloaded application in')); + expect(result.code, 0); + }, + overrides: { + Analytics: () => fakeAnalytics, + BuildSystem: () => TestBuildSystem.all(BuildResult(success: true)), + FileSystem: () => fileSystem, + ProcessManager: () => processManager, + FeatureFlags: enableExplicitPackageDependencies, + Pub: FakePubWithPrimedDeps.new, + }, + ); + // Hot restart is available with and without the DDC library bundle format. // Test one extra config where `fullRestart` is false without the DDC library // bundle format - we should do a hot restart in this case because hot reload @@ -1995,6 +2079,11 @@ class FakeChromeTab extends Fake implements ChromeTab { class FakeWipConnection extends Fake implements WipConnection { @override final WipDebugger debugger = FakeWipDebugger(); + + @override + Future sendCommand(String method, [Map? params]) async { + return WipResponse({'id': 0, 'result': {}}); + } } /// A test implementation of the [ChromiumLauncher] that launches a fixed instance.