From af42e7d7300a8a5d6c4b771a27d467a02352e2c0 Mon Sep 17 00:00:00 2001 From: Lau Ching Jun Date: Mon, 28 Jun 2021 13:16:04 -0700 Subject: [PATCH] Only set assets directory after assets are uploaded onto devfs. (#85418) --- packages/flutter_tools/lib/src/devfs.dart | 3 + .../lib/src/isolated/devfs_web.dart | 4 + packages/flutter_tools/lib/src/run_hot.dart | 30 +++-- .../general.shard/resident_runner_test.dart | 103 +++++++++++++----- 4 files changed, 104 insertions(+), 36 deletions(-) diff --git a/packages/flutter_tools/lib/src/devfs.dart b/packages/flutter_tools/lib/src/devfs.dart index cb8d06fd82..adcb252dd8 100644 --- a/packages/flutter_tools/lib/src/devfs.dart +++ b/packages/flutter_tools/lib/src/devfs.dart @@ -480,6 +480,9 @@ class DevFS { final Directory rootDirectory; final Set assetPathsToEvict = {}; + // A flag to indicate whether we have called `setAssetDirectory` on the target device. + bool hasSetAssetDirectory = false; + List sources = []; DateTime lastCompiled; DateTime _previousCompiled; diff --git a/packages/flutter_tools/lib/src/isolated/devfs_web.dart b/packages/flutter_tools/lib/src/isolated/devfs_web.dart index 24176f24c8..f4f845f407 100644 --- a/packages/flutter_tools/lib/src/isolated/devfs_web.dart +++ b/packages/flutter_tools/lib/src/isolated/devfs_web.dart @@ -660,6 +660,10 @@ class WebDevFS implements DevFS { Dwds get dwds => webAssetServer.dwds; + // A flag to indicate whether we have called `setAssetDirectory` on the target device. + @override + bool hasSetAssetDirectory = false; + Future _cachedExtensionFuture; StreamSubscription _connectedApps; diff --git a/packages/flutter_tools/lib/src/run_hot.dart b/packages/flutter_tools/lib/src/run_hot.dart index 5b1f956eb9..f2033f11b1 100644 --- a/packages/flutter_tools/lib/src/run_hot.dart +++ b/packages/flutter_tools/lib/src/run_hot.dart @@ -279,14 +279,6 @@ class HotRunner extends ResidentRunner { device.generator.accept(); } final List views = await device.vmService.getFlutterViews(); - final Uri deviceAssetsDirectoryUri = device.devFS.baseUri.resolveUri(globals.fs.path.toUri(getAssetBuildDirectory())); - await Future.wait(views.map>( - (FlutterView view) => device.vmService.setAssetDirectory( - assetsDirectory: deviceAssetsDirectoryUri, - uiIsolateId: view.uiIsolate.id, - viewId: view.id, - ) - )); for (final FlutterView view in views) { globals.printTrace('Connected to $view.'); } @@ -896,7 +888,7 @@ class HotRunner extends ResidentRunner { } reloadVMTimer.stop(); - await _evictDirtyAssets(); + await evictDirtyAssets(); final Stopwatch reassembleTimer = _stopwatchFactory.createStopwatch('reloadSources:reassemble')..start(); @@ -997,13 +989,31 @@ class HotRunner extends ResidentRunner { printDebuggerList(); } - Future _evictDirtyAssets() async { + @visibleForTesting + Future evictDirtyAssets() async { final List>> futures = >>[]; for (final FlutterDevice device in flutterDevices) { if (device.devFS.assetPathsToEvict.isEmpty) { continue; } final List views = await device.vmService.getFlutterViews(); + + // If this is the first time we update the assets, make sure to call the setAssetDirectory + if (!device.devFS.hasSetAssetDirectory) { + final Uri deviceAssetsDirectoryUri = device.devFS.baseUri.resolveUri(globals.fs.path.toUri(getAssetBuildDirectory())); + await Future.wait(views.map>( + (FlutterView view) => device.vmService.setAssetDirectory( + assetsDirectory: deviceAssetsDirectoryUri, + uiIsolateId: view.uiIsolate.id, + viewId: view.id, + ) + )); + for (final FlutterView view in views) { + globals.printTrace('Set asset directory in $view.'); + } + device.devFS.hasSetAssetDirectory = true; + } + if (views.first.uiIsolate == null) { globals.printError('Application isolate not found for $device'); continue; 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 12e9738549..99b044eece 100644 --- a/packages/flutter_tools/test/general.shard/resident_runner_test.dart +++ b/packages/flutter_tools/test/general.shard/resident_runner_test.dart @@ -139,6 +139,14 @@ const FakeVmServiceRequest setAssetBundlePath = FakeVmServiceRequest( } ); +const FakeVmServiceRequest evict = FakeVmServiceRequest( + method: 'ext.flutter.evict', + args: { + 'value': 'asset', + 'isolateId': '1', + } +); + final Uri testUri = Uri.parse('foo://bar'); void main() { @@ -179,7 +187,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ listViews, listViews, - setAssetBundlePath, ]); final Completer futureConnectionInfo = Completer.sync(); final Completer futureAppStart = Completer.sync(); @@ -203,7 +210,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ listViews, listViews, - setAssetBundlePath, ]); final FakeResidentCompiler residentCompiler = FakeResidentCompiler() ..nextOutput = const CompilerOutput('foo', 0 ,[]); @@ -295,7 +301,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ listViews, listViews, - setAssetBundlePath, ]); final FakeResidentCompiler residentCompiler = FakeResidentCompiler() ..nextOutput = const CompilerOutput('foo', 0 ,[]); @@ -320,7 +325,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ listViews, listViews, - setAssetBundlePath, listViews, FakeVmServiceRequest( method: 'getIsolate', @@ -389,7 +393,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ listViews, listViews, - setAssetBundlePath, listViews, ]); final Completer futureConnectionInfo = Completer.sync(); @@ -422,7 +425,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ listViews, listViews, - setAssetBundlePath, ]); final Completer futureConnectionInfo = Completer.sync(); final Completer futureAppStart = Completer.sync(); @@ -444,7 +446,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ listViews, listViews, - setAssetBundlePath, listViews, ]); final Completer futureConnectionInfo = Completer.sync(); @@ -479,7 +480,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ listViews, listViews, - setAssetBundlePath, listViews, ]); residentRunner = HotRunner( @@ -526,7 +526,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ listViews, listViews, - setAssetBundlePath, listViews, FakeVmServiceRequest( method: 'getIsolate', @@ -571,7 +570,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ listViews, listViews, - setAssetBundlePath, listViews, FakeVmServiceRequest( method: 'getVM', @@ -632,7 +630,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ listViews, listViews, - setAssetBundlePath, listViews, FakeVmServiceRequest( method: 'getVM', @@ -697,7 +694,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ listViews, listViews, - setAssetBundlePath, listViews, FakeVmServiceRequest( method: 'getVM', @@ -765,7 +761,6 @@ void main() { jsonResponse: fakeVM.toJson(), ), listViews, - setAssetBundlePath, listViews, FakeVmServiceRequest( method: 'getVM', @@ -853,7 +848,6 @@ void main() { listViews, listViews, listViews, - setAssetBundlePath, FakeVmServiceRequest( method: 'getIsolate', args: { @@ -914,7 +908,6 @@ void main() { listViews, listViews, listViews, - setAssetBundlePath, FakeVmServiceRequest( method: 'getIsolate', args: { @@ -981,7 +974,6 @@ void main() { listViews, listViews, listViews, - setAssetBundlePath, FakeVmServiceRequest( method: 'getIsolate', args: { @@ -1103,7 +1095,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ listViews, listViews, - setAssetBundlePath, ]); final Completer futureConnectionInfo = Completer.sync(); final Completer futureAppStart = Completer.sync(); @@ -1421,7 +1412,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ listViews, listViews, - setAssetBundlePath, ]); residentRunner = ColdRunner( [ @@ -1465,7 +1455,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ listViews, listViews, - setAssetBundlePath, ], wsAddress: testUri); globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); residentRunner = HotRunner( @@ -1488,7 +1477,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ listViews, listViews, - setAssetBundlePath, ], wsAddress: testUri); globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); residentRunner = HotRunner( @@ -1511,7 +1499,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ listViews, listViews, - setAssetBundlePath, ], wsAddress: testUri); globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); residentRunner = HotRunner( @@ -1542,7 +1529,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ listViews, listViews, - setAssetBundlePath, ], wsAddress: testUri); globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); residentRunner = HotRunner( @@ -1573,7 +1559,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ listViews, listViews, - setAssetBundlePath, ], wsAddress: testUri); globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); residentRunner = HotRunner( @@ -1597,7 +1582,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ listViews, listViews, - setAssetBundlePath, ], wsAddress: testUri); globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); residentRunner = HotRunner( @@ -1625,7 +1609,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ listViews, listViews, - setAssetBundlePath, ], wsAddress: testUri); globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); residentRunner = HotRunner( @@ -1646,7 +1629,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ listViews, listViews, - setAssetBundlePath, ]); globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); residentRunner = HotRunner( @@ -1925,6 +1907,72 @@ void main() { expect((residentRunner.residentDevtoolsHandler as NoOpDevtoolsHandler).wasShutdown, true); })); + + testUsingContext('HotRunner sets asset directory when first evict assets', () => testbed.run(() async { + fakeVmServiceHost = FakeVmServiceHost(requests: [ + listViews, + setAssetBundlePath, + evict, + ]); + residentRunner = HotRunner( + [ + flutterDevice, + ], + stayResident: false, + debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug), + target: 'main.dart', + devtoolsHandler: createNoOpHandler, + ); + + (flutterDevice.devFS as FakeDevFS).assetPathsToEvict = {'asset'}; + + expect(flutterDevice.devFS.hasSetAssetDirectory, false); + await (residentRunner as HotRunner).evictDirtyAssets(); + expect(flutterDevice.devFS.hasSetAssetDirectory, true); + expect(fakeVmServiceHost.hasRemainingExpectations, false); + })); + + testUsingContext('HotRunner does not sets asset directory when no assets to evict', () => testbed.run(() async { + fakeVmServiceHost = FakeVmServiceHost(requests: [ + ]); + residentRunner = HotRunner( + [ + flutterDevice, + ], + stayResident: false, + debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug), + target: 'main.dart', + devtoolsHandler: createNoOpHandler, + ); + + expect(flutterDevice.devFS.hasSetAssetDirectory, false); + await (residentRunner as HotRunner).evictDirtyAssets(); + expect(flutterDevice.devFS.hasSetAssetDirectory, false); + expect(fakeVmServiceHost.hasRemainingExpectations, false); + })); + + testUsingContext('HotRunner does not set asset directory if it has been set before', () => testbed.run(() async { + fakeVmServiceHost = FakeVmServiceHost(requests: [ + listViews, + evict, + ]); + residentRunner = HotRunner( + [ + flutterDevice, + ], + stayResident: false, + debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug), + target: 'main.dart', + devtoolsHandler: createNoOpHandler, + ); + + (flutterDevice.devFS as FakeDevFS).assetPathsToEvict = {'asset'}; + flutterDevice.devFS.hasSetAssetDirectory = true; + + await (residentRunner as HotRunner).evictDirtyAssets(); + expect(flutterDevice.devFS.hasSetAssetDirectory, true); + expect(fakeVmServiceHost.hasRemainingExpectations, false); + })); } class FakeDartDevelopmentServiceException implements dds.DartDevelopmentServiceException { @@ -2231,6 +2279,9 @@ class FakeDevFS extends Fake implements DevFS { UpdateFSReport nextUpdateReport = UpdateFSReport(success: true); + @override + bool hasSetAssetDirectory = false; + @override Future create() async { return Uri();