From ad7727a21cdd8fe8a0890aef637a9138bc8f14c2 Mon Sep 17 00:00:00 2001 From: Yegor Date: Thu, 21 Oct 2021 11:13:18 -0700 Subject: [PATCH] [web] enable CanvasKit tests using a local bundle fetched from CIPD (#92134) --- .ci.yaml | 168 ++++++++++++++++++ TESTOWNERS | 1 + dev/bots/test.dart | 96 ++++++++-- .../lib/skia_client.dart | 9 +- .../lib/src/build_system/targets/web.dart | 48 ++++- .../lib/src/commands/assemble.dart | 2 +- .../flutter_tools/lib/src/flutter_cache.dart | 8 + .../lib/src/test/flutter_web_goldens.dart | 4 + .../lib/src/test/flutter_web_platform.dart | 62 ++++++- .../flutter_tools/lib/src/test/runner.dart | 1 + .../flutter_tools/lib/src/web/compile.dart | 2 +- .../build_system/targets/web_test.dart | 6 +- .../test/general.shard/cache_test.dart | 33 +++- .../web/golden_comparator_test.dart | 13 +- packages/flutter_tools/test/src/common.dart | 2 +- 15 files changed, 425 insertions(+), 30 deletions(-) diff --git a/.ci.yaml b/.ci.yaml index a5a86eb56d2..0d5ea5ef831 100755 --- a/.ci.yaml +++ b/.ci.yaml @@ -1184,6 +1184,174 @@ targets: - bin/ - .ci.yaml + - name: Linux web_canvaskit_tests_0 + bringup: true + recipe: flutter/flutter_drone + timeout: 60 + properties: + dependencies: >- + [ + {"dependency": "android_sdk"}, + {"dependency": "chrome_and_driver"}, + {"dependency": "goldctl"} + ] + shard: web_canvaskit_tests + subshard: "0" + tags: > + ["framework","hostonly","shard"] + scheduler: luci + runIf: + - dev/ + - packages/ + - bin/ + + - name: Linux web_canvaskit_tests_1 + bringup: true + recipe: flutter/flutter_drone + timeout: 60 + properties: + dependencies: >- + [ + {"dependency": "android_sdk"}, + {"dependency": "chrome_and_driver"}, + {"dependency": "goldctl"} + ] + shard: web_canvaskit_tests + subshard: "1" + tags: > + ["framework","hostonly","shard"] + scheduler: luci + runIf: + - dev/ + - packages/ + - bin/ + + - name: Linux web_canvaskit_tests_2 + bringup: true + recipe: flutter/flutter_drone + timeout: 60 + properties: + dependencies: >- + [ + {"dependency": "android_sdk"}, + {"dependency": "chrome_and_driver"}, + {"dependency": "goldctl"} + ] + shard: web_canvaskit_tests + subshard: "2" + tags: > + ["framework","hostonly","shard"] + scheduler: luci + runIf: + - dev/ + - packages/ + - bin/ + + - name: Linux web_canvaskit_tests_3 + bringup: true + recipe: flutter/flutter_drone + timeout: 60 + properties: + dependencies: >- + [ + {"dependency": "android_sdk"}, + {"dependency": "chrome_and_driver"}, + {"dependency": "goldctl"} + ] + shard: web_canvaskit_tests + subshard: "3" + tags: > + ["framework","hostonly","shard"] + scheduler: luci + runIf: + - dev/ + - packages/ + - bin/ + + - name: Linux web_canvaskit_tests_4 + bringup: true + recipe: flutter/flutter_drone + timeout: 60 + properties: + dependencies: >- + [ + {"dependency": "android_sdk"}, + {"dependency": "chrome_and_driver"}, + {"dependency": "goldctl"} + ] + shard: web_canvaskit_tests + subshard: "4" + tags: > + ["framework","hostonly","shard"] + scheduler: luci + runIf: + - dev/ + - packages/ + - bin/ + + - name: Linux web_canvaskit_tests_5 + bringup: true + recipe: flutter/flutter_drone + timeout: 60 + properties: + dependencies: >- + [ + {"dependency": "android_sdk"}, + {"dependency": "chrome_and_driver"}, + {"dependency": "goldctl"} + ] + shard: web_canvaskit_tests + subshard: "5" + tags: > + ["framework","hostonly","shard"] + scheduler: luci + runIf: + - dev/ + - packages/ + - bin/ + + - name: Linux web_canvaskit_tests_6 + bringup: true + recipe: flutter/flutter_drone + timeout: 60 + properties: + dependencies: >- + [ + {"dependency": "android_sdk"}, + {"dependency": "chrome_and_driver"}, + {"dependency": "goldctl"} + ] + shard: web_canvaskit_tests + subshard: "6" + tags: > + ["framework","hostonly","shard"] + scheduler: luci + runIf: + - dev/ + - packages/ + - bin/ + + - name: Linux web_canvaskit_tests_7_last + bringup: true + recipe: flutter/flutter_drone + timeout: 60 + properties: + dependencies: >- + [ + {"dependency": "android_sdk"}, + {"dependency": "chrome_and_driver"}, + {"dependency": "goldctl"} + ] + shard: web_canvaskit_tests + subshard: "7_last" + tags: > + ["framework","hostonly","shard"] + scheduler: luci + runIf: + - dev/ + - packages/ + - bin/ + - name: Linux web_tool_tests recipe: flutter/flutter_drone timeout: 60 diff --git a/TESTOWNERS b/TESTOWNERS index 487b96806fb..559100d9dc5 100644 --- a/TESTOWNERS +++ b/TESTOWNERS @@ -225,6 +225,7 @@ # web_integration_tests @yjbanov @flutter/web # web_long_running_tests @yjbanov @flutter/web # web_tests @yjbanov @flutter/web +# web_canvaskit_tests @yjbanov @flutter/web # web_tool_tests @zanderso @flutter/tool # fuchsia_precache @zanderso @flutter/tool # skp_generator @Hixie diff --git a/dev/bots/test.dart b/dev/bots/test.dart index 61a431a7964..1a65d04b8ad 100644 --- a/dev/bots/test.dart +++ b/dev/bots/test.dart @@ -74,13 +74,66 @@ const String kSubshardKey = 'SUBSHARD'; int get webShardCount => Platform.environment.containsKey('WEB_SHARD_COUNT') ? int.parse(Platform.environment['WEB_SHARD_COUNT']!) : 8; -/// Tests that we don't run on Web for compilation reasons. + +/// Tests that we don't run on Web. +/// +/// In general avoid adding new tests here. If a test cannot run on the web +/// because it fails at runtime, such as when a piece of functionality is not +/// implemented or not implementable on the web, prefer using `skip` in the +/// test code. Only add tests here that cannot be skipped using `skip`. For +/// example: +/// +/// * Test code cannot be compiled because it uses Dart VM-specific +/// functionality. In this case `skip` doesn't help because the code cannot +/// reach the point where it can even run the skipping logic. +/// * Migrations. It is OK to put tests here that need to be temporarily +/// disabled in certain modes because of some migration or initial bringup. +/// +/// The key in the map is the renderer type that the list applies to. The value +/// is the list of tests known to fail for that renderer. // // TODO(yjbanov): we're getting rid of this as part of https://github.com/flutter/flutter/projects/60 -const List kWebTestFileKnownFailures = [ - 'test/services/message_codecs_vm_test.dart', - 'test/examples/sector_layout_test.dart', -]; +const Map> kWebTestFileKnownFailures = >{ + 'html': [ + // These tests are not compilable on the web due to dependencies on + // VM-specific functionality. + 'test/services/message_codecs_vm_test.dart', + 'test/examples/sector_layout_test.dart', + ], + 'canvaskit': [ + // These tests are not compilable on the web due to dependencies on + // VM-specific functionality. + 'test/services/message_codecs_vm_test.dart', + 'test/examples/sector_layout_test.dart', + + // These tests are broken and need to be fixed. + // TODO(yjbanov): https://github.com/flutter/flutter/issues/71604 + 'test/painting/decoration_test.dart', + 'test/material/text_selection_theme_test.dart', + 'test/material/date_picker_test.dart', + 'test/rendering/layers_test.dart', + 'test/painting/text_style_test.dart', + 'test/widgets/image_test.dart', + 'test/cupertino/colors_test.dart', + 'test/cupertino/slider_test.dart', + 'test/material/text_field_test.dart', + 'test/rendering/proxy_box_test.dart', + 'test/widgets/app_overrides_test.dart', + 'test/material/calendar_date_picker_test.dart', + 'test/material/ink_paint_test.dart', + 'test/rendering/editable_test.dart', + 'test/cupertino/dialog_test.dart', + 'test/widgets/shape_decoration_test.dart', + 'test/material/time_picker_theme_test.dart', + 'test/cupertino/picker_test.dart', + 'test/material/chip_theme_test.dart', + 'test/cupertino/nav_bar_test.dart', + 'test/widgets/performance_overlay_test.dart', + 'test/widgets/html_element_view_test.dart', + 'test/cupertino/scaffold_test.dart', + 'test/rendering/platform_view_test.dart', + ], +}; const String kSmokeTestShardName = 'smoke_tests'; const List _kAllBuildModes = ['debug', 'profile', 'release']; @@ -143,8 +196,10 @@ Future main(List args) async { // web_tool_tests is also used by HHH: https://dart.googlesource.com/recipes/+/refs/heads/master/recipes/dart/flutter_engine.py 'web_tool_tests': _runWebToolTests, 'tool_integration_tests': _runIntegrationToolTests, - // All the unit/widget tests run using `flutter test --platform=chrome` - 'web_tests': _runWebUnitTests, + // All the unit/widget tests run using `flutter test --platform=chrome --web-renderer=html` + 'web_tests': _runWebHtmlUnitTests, + // All the unit/widget tests run using `flutter test --platform=chrome --web-renderer=canvaskit` + 'web_canvaskit_tests': _runWebCanvasKitUnitTests, // All web integration tests 'web_long_running_tests': _runWebLongRunningTests, 'flutter_plugins': _runFlutterPluginsTests, @@ -802,7 +857,15 @@ Future _runFrameworkCoverage() async { } } -Future _runWebUnitTests() async { +Future _runWebHtmlUnitTests() { + return _runWebUnitTests('html'); +} + +Future _runWebCanvasKitUnitTests() { + return _runWebUnitTests('canvaskit'); +} + +Future _runWebUnitTests(String webRenderer) async { final Map subshards = {}; final Directory flutterPackageDirectory = Directory(path.join(flutterRoot, 'packages', 'flutter')); @@ -817,7 +880,7 @@ Future _runWebUnitTests() async { ) .whereType() .map((File file) => path.relative(file.path, from: flutterPackageDirectory.path)) - .where((String filePath) => !kWebTestFileKnownFailures.contains(path.split(filePath).join('/'))) + .where((String filePath) => !kWebTestFileKnownFailures[webRenderer]!.contains(path.split(filePath).join('/'))) .toList() // Finally we shuffle the list because we want the average cost per file to be uniformly // distributed. If the list is not sorted then different shards and batches may have @@ -832,6 +895,7 @@ Future _runWebUnitTests() async { // This for loop computes all but the last shard. for (int index = 0; index < webShardCount - 1; index += 1) { subshards['$index'] = () => _runFlutterWebTest( + webRenderer, flutterPackageDirectory.path, allTests.sublist( index * testsPerShard, @@ -846,6 +910,7 @@ Future _runWebUnitTests() async { // between `.cirrus.yml` and `test.dart`. subshards['${webShardCount - 1}_last'] = () async { await _runFlutterWebTest( + webRenderer, flutterPackageDirectory.path, allTests.sublist( (webShardCount - 1) * testsPerShard, @@ -853,12 +918,14 @@ Future _runWebUnitTests() async { ), ); await _runFlutterWebTest( + webRenderer, path.join(flutterRoot, 'packages', 'flutter_web_plugins'), ['test'], ); await _runFlutterWebTest( - path.join(flutterRoot, 'packages', 'flutter_driver'), - [path.join('test', 'src', 'web_tests', 'web_extension_test.dart')], + webRenderer, + path.join(flutterRoot, 'packages', 'flutter_driver'), + [path.join('test', 'src', 'web_tests', 'web_extension_test.dart')], ); }; @@ -1421,7 +1488,7 @@ Future _runWebDebugTest(String target, { } } -Future _runFlutterWebTest(String workingDirectory, List tests) async { +Future _runFlutterWebTest(String webRenderer, String workingDirectory, List tests) async { await runCommand( flutter, [ @@ -1430,9 +1497,8 @@ Future _runFlutterWebTest(String workingDirectory, List tests) asy '--concurrency=1', // do not parallelize on Cirrus, to reduce flakiness '-v', '--platform=chrome', - // TODO(ferhatb): Run web tests with both rendering backends. - '--web-renderer=html', // use html backend for web tests. - '--sound-null-safety', // web tests do not autodetect yet. + '--web-renderer=$webRenderer', + '--sound-null-safety', ...flutterTestArgs, ...tests, ], diff --git a/packages/flutter_goldens_client/lib/skia_client.dart b/packages/flutter_goldens_client/lib/skia_client.dart index fbe71a0c597..396d2e2f4af 100644 --- a/packages/flutter_goldens_client/lib/skia_client.dart +++ b/packages/flutter_goldens_client/lib/skia_client.dart @@ -19,6 +19,7 @@ import 'package:process/process.dart'; const String _kFlutterRootKey = 'FLUTTER_ROOT'; const String _kGoldctlKey = 'GOLDCTL'; const String _kTestBrowserKey = 'FLUTTER_TEST_BROWSER'; +const String _kWebRendererKey = 'FLUTTER_WEB_RENDERER'; /// A client for uploading image tests and making baseline requests to the /// Flutter Gold Dashboard. @@ -368,6 +369,9 @@ class SkiaGoldClient { if (platform.environment[_kTestBrowserKey] != null) { keys['Browser'] = platform.environment[_kTestBrowserKey]; keys['Platform'] = '${keys['Platform']}-browser'; + if (platform.environment[_kWebRendererKey] == 'canvaskit') { + keys['WebRenderer'] = 'canvaskit'; + } } return json.encode(keys); } @@ -413,7 +417,10 @@ class SkiaGoldClient { /// the image keys. String getTraceID(String testName) { final Map keys = { - if (platform.environment[_kTestBrowserKey] != null) 'Browser' : platform.environment[_kTestBrowserKey], + if (platform.environment[_kTestBrowserKey] != null) + 'Browser' : platform.environment[_kTestBrowserKey], + if (platform.environment[_kTestBrowserKey] != null && platform.environment[_kWebRendererKey] == 'canvaskit') + 'WebRenderer' : 'canvaskit', 'CI' : 'luci', 'Platform' : platform.operatingSystem, 'name' : testName, diff --git a/packages/flutter_tools/lib/src/build_system/targets/web.dart b/packages/flutter_tools/lib/src/build_system/targets/web.dart index 05608135da7..6a74cf3df17 100644 --- a/packages/flutter_tools/lib/src/build_system/targets/web.dart +++ b/packages/flutter_tools/lib/src/build_system/targets/web.dart @@ -404,17 +404,57 @@ class WebReleaseBundle extends Target { } } +/// Static assets provided by the Flutter SDK that do not change, such as +/// CanvasKit. +/// +/// These assets can be cached forever and are only invalidated when the +/// Flutter SDK is upgraded to a new version. +class WebBuiltInAssets extends Target { + const WebBuiltInAssets(this.fileSystem); + + final FileSystem fileSystem; + + @override + String get name => 'web_static_assets'; + + @override + List get dependencies => const []; + + @override + List get depfiles => const []; + + @override + List get inputs => const []; + + @override + List get outputs => const []; + + @override + Future build(Environment environment) async { + final Directory flutterWebSdk = globals.artifacts.getHostArtifact(HostArtifact.flutterWebSdk) as Directory; + final Directory canvasKitDirectory = flutterWebSdk.childDirectory('canvaskit'); + for (final File file in canvasKitDirectory.listSync(recursive: true).whereType()) { + final String relativePath = fileSystem.path.relative(file.path, from: canvasKitDirectory.path); + final String targetPath = fileSystem.path.join(environment.outputDir.path, 'canvaskit', relativePath); + file.copySync(targetPath); + } + } +} + /// Generate a service worker for a web target. class WebServiceWorker extends Target { - const WebServiceWorker(); + const WebServiceWorker(this.fileSystem); + + final FileSystem fileSystem; @override String get name => 'web_service_worker'; @override - List get dependencies => const [ - Dart2JSTarget(), - WebReleaseBundle(), + List get dependencies => [ + const Dart2JSTarget(), + const WebReleaseBundle(), + WebBuiltInAssets(fileSystem), ]; @override diff --git a/packages/flutter_tools/lib/src/commands/assemble.dart b/packages/flutter_tools/lib/src/commands/assemble.dart index 96f121b8c04..9c06a44933e 100644 --- a/packages/flutter_tools/lib/src/commands/assemble.dart +++ b/packages/flutter_tools/lib/src/commands/assemble.dart @@ -49,7 +49,7 @@ List _kDefaultTargets = [ const ReleaseBundleLinuxAssets(TargetPlatform.linux_x64), const ReleaseBundleLinuxAssets(TargetPlatform.linux_arm64), // Web targets - const WebServiceWorker(), + WebServiceWorker(globals.fs), const ReleaseAndroidApplication(), // This is a one-off rule for bundle and aot compat. const CopyFlutterBundle(), diff --git a/packages/flutter_tools/lib/src/flutter_cache.dart b/packages/flutter_tools/lib/src/flutter_cache.dart index 7f21f8313c5..9e29d5ad75c 100644 --- a/packages/flutter_tools/lib/src/flutter_cache.dart +++ b/packages/flutter_tools/lib/src/flutter_cache.dart @@ -196,6 +196,14 @@ class FlutterWebSdk extends CachedArtifact { entity.copySync(newPath); } } + + final String canvasKitVersion = cache.getVersionFor('canvaskit')!; + final String canvasKitUrl = '$_cipdBaseUrl/flutter/web/canvaskit_bundle/+/$canvasKitVersion'; + return artifactUpdater.downloadZipArchive( + 'Downloading CanvasKit...', + Uri.parse(canvasKitUrl), + location, + ); } } diff --git a/packages/flutter_tools/lib/src/test/flutter_web_goldens.dart b/packages/flutter_tools/lib/src/test/flutter_web_goldens.dart index 82d7a0e2207..f8950f11591 100644 --- a/packages/flutter_tools/lib/src/test/flutter_web_goldens.dart +++ b/packages/flutter_tools/lib/src/test/flutter_web_goldens.dart @@ -15,6 +15,7 @@ import '../base/file_system.dart'; import '../base/io.dart'; import '../base/logger.dart'; import '../convert.dart'; +import '../web/compile.dart'; import 'test_compiler.dart'; import 'test_config.dart'; @@ -31,6 +32,7 @@ class TestGoldenComparator { @required Logger logger, @required FileSystem fileSystem, @required ProcessManager processManager, + @required this.webRenderer, }) : tempDir = fileSystem.systemTempDirectory.createTempSync('flutter_web_platform.'), _logger = logger, _fileSystem = fileSystem, @@ -42,6 +44,7 @@ class TestGoldenComparator { final Logger _logger; final FileSystem _fileSystem; final ProcessManager _processManager; + final WebRendererMode webRenderer; TestCompiler _compiler; TestGoldenComparatorProcess _previousComparator; @@ -88,6 +91,7 @@ class TestGoldenComparator { final Map environment = { // Chrome is the only supported browser currently. 'FLUTTER_TEST_BROWSER': 'chrome', + 'FLUTTER_WEB_RENDERER': webRenderer == WebRendererMode.html ? 'html' : 'canvaskit', }; return _processManager.start(command, environment: environment); } diff --git a/packages/flutter_tools/lib/src/test/flutter_web_platform.dart b/packages/flutter_tools/lib/src/test/flutter_web_platform.dart index cd0a534cb23..53c4b7a3640 100644 --- a/packages/flutter_tools/lib/src/test/flutter_web_platform.dart +++ b/packages/flutter_tools/lib/src/test/flutter_web_platform.dart @@ -53,11 +53,13 @@ class FlutterWebPlatform extends PlatformPlugin { @required Logger logger, @required Artifacts artifacts, @required ProcessManager processManager, + @required Cache cache, }) : _fileSystem = fileSystem, _flutterToolPackageConfig = flutterToolPackageConfig, _chromiumLauncher = chromiumLauncher, _logger = logger, - _artifacts = artifacts { + _artifacts = artifacts, + _cache = cache { final shelf.Cascade cascade = shelf.Cascade() .add(_webSocketHandler.handler) .add(createStaticHandler( @@ -65,6 +67,7 @@ class FlutterWebPlatform extends PlatformPlugin { serveFilesOutsidePath: true, )) .add(_handleStaticArtifact) + .add(_localCanvasKitHandler) .add(_goldenFileHandler) .add(_wrapperHandler) .add(_handleTestRequest) @@ -80,6 +83,7 @@ class FlutterWebPlatform extends PlatformPlugin { fileSystem: _fileSystem, logger: _logger, processManager: processManager, + webRenderer: _rendererMode, ); } @@ -95,6 +99,7 @@ class FlutterWebPlatform extends PlatformPlugin { final OneOffHandler _webSocketHandler = OneOffHandler(); final AsyncMemoizer _closeMemo = AsyncMemoizer(); final String _root; + final Cache _cache; /// Allows only one test suite (typically one test file) to be loaded and run /// at any given point in time. Loading more than one file at a time is known @@ -117,6 +122,7 @@ class FlutterWebPlatform extends PlatformPlugin { @required ChromiumLauncher chromiumLauncher, @required Artifacts artifacts, @required ProcessManager processManager, + @required Cache cache, }) async { final shelf_io.IOServer server = shelf_io.IOServer(await HttpMultiServer.loopback(0)); final PackageConfig packageConfig = await loadPackageConfigWithLogging( @@ -145,6 +151,7 @@ class FlutterWebPlatform extends PlatformPlugin { logger: logger, nullAssertions: nullAssertions, processManager: processManager, + cache: cache, ); } @@ -218,6 +225,23 @@ class FlutterWebPlatform extends PlatformPlugin { 'host.dart.js', )); + File _canvasKitFile(String relativePath) { + // TODO(yjbanov): https://github.com/flutter/flutter/issues/52588 + // + // Update this when we start building CanvasKit from sources. In the + // meantime, get the Web SDK directory from cache rather than through + // Artifacts. The latter is sensitive to `--local-engine`, which changes + // the directory to point to ENGINE/src/out. However, CanvasKit is not yet + // built as part of the engine, but fetched from CIPD, and so it won't be + // found in ENGINE/src/out. + final Directory webSdkDirectory = _cache.getWebSdkDirectory(); + final File canvasKitFile = _fileSystem.file(_fileSystem.path.join( + webSdkDirectory.path, + relativePath, + )); + return canvasKitFile; + } + Future _handleTestRequest(shelf.Request request) async { if (request.url.path.endsWith('.dart.browser_test.dart.js')) { final String leadingPath = request.url.path.split('.browser_test.dart.js')[0]; @@ -365,6 +389,37 @@ class FlutterWebPlatform extends PlatformPlugin { } } + /// Serves a local build of CanvasKit, replacing the CDN build, which can + /// cause test flakiness due to reliance on network. + shelf.Response _localCanvasKitHandler(shelf.Request request) { + final String path = _fileSystem.path.fromUri(request.url); + if (!path.startsWith('canvaskit/')) { + return shelf.Response.notFound('Not a CanvasKit file request'); + } + + final String extension = _fileSystem.path.extension(path); + String contentType; + switch (extension) { + case '.js': + contentType = 'text/javascript'; + break; + case '.wasm': + contentType = 'application/wasm'; + break; + default: + final String error = 'Failed to determine Content-Type for "${request.url.path}".'; + _logger.printError(error); + return shelf.Response.internalServerError(body: error); + } + + return shelf.Response.ok( + _canvasKitFile(path).openRead(), + headers: { + HttpHeaders.contentTypeHeader: contentType, + }, + ); + } + // A handler that serves wrapper files used to bootstrap tests. shelf.Response _wrapperHandler(shelf.Request request) { final String path = _fileSystem.path.fromUri(request.url); @@ -377,6 +432,11 @@ class FlutterWebPlatform extends PlatformPlugin { ${htmlEscape.convert(test)} Test + $link diff --git a/packages/flutter_tools/lib/src/test/runner.dart b/packages/flutter_tools/lib/src/test/runner.dart index b622469beec..532d41ff979 100644 --- a/packages/flutter_tools/lib/src/test/runner.dart +++ b/packages/flutter_tools/lib/src/test/runner.dart @@ -176,6 +176,7 @@ class _FlutterTestRunnerImpl implements FlutterTestRunner { browserFinder: findChromeExecutable, logger: globals.logger, ), + cache: globals.cache, ); }, ); diff --git a/packages/flutter_tools/lib/src/web/compile.dart b/packages/flutter_tools/lib/src/web/compile.dart index 18b01d75605..aad0161b506 100644 --- a/packages/flutter_tools/lib/src/web/compile.dart +++ b/packages/flutter_tools/lib/src/web/compile.dart @@ -37,7 +37,7 @@ Future buildWeb( final Status status = globals.logger.startProgress('Compiling $target for the Web...'); final Stopwatch sw = Stopwatch()..start(); try { - final BuildResult result = await globals.buildSystem.build(const WebServiceWorker(), Environment( + final BuildResult result = await globals.buildSystem.build(WebServiceWorker(globals.fs), Environment( projectDir: globals.fs.currentDirectory, outputDir: outputDirectory, buildDir: flutterProject.directory diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart index e536d4824fa..2f9c5da8bcf 100644 --- a/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart +++ b/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart @@ -637,7 +637,7 @@ void main() { environment.outputDir.childDirectory('a').childFile('a.txt') ..createSync(recursive: true) ..writeAsStringSync('A'); - await const WebServiceWorker().build(environment); + await WebServiceWorker(globals.fs).build(environment); expect(environment.outputDir.childFile('flutter_service_worker.js'), exists); // Contains file hash. @@ -656,7 +656,7 @@ void main() { environment.outputDir .childFile('index.html') .createSync(recursive: true); - await const WebServiceWorker().build(environment); + await WebServiceWorker(globals.fs).build(environment); expect(environment.outputDir.childFile('flutter_service_worker.js'), exists); // Contains file hash for both `/` and index.html. @@ -674,7 +674,7 @@ void main() { environment.outputDir .childFile('main.dart.js.map') .createSync(recursive: true); - await const WebServiceWorker().build(environment); + await WebServiceWorker(globals.fs).build(environment); // No caching of source maps. expect(environment.outputDir.childFile('flutter_service_worker.js').readAsStringSync(), diff --git a/packages/flutter_tools/test/general.shard/cache_test.dart b/packages/flutter_tools/test/general.shard/cache_test.dart index 859966169c3..24b330298cb 100644 --- a/packages/flutter_tools/test/general.shard/cache_test.dart +++ b/packages/flutter_tools/test/general.shard/cache_test.dart @@ -728,20 +728,49 @@ void main() { expect(logger.errorText, contains('Failed to delete some stamp files')); }); - testWithoutContext('FlutterWebSdk deletes previous directory contents', () { + testWithoutContext('FlutterWebSdk fetches web artifacts and deletes previous directory contents', () async { final MemoryFileSystem fileSystem = MemoryFileSystem.test(); + final File canvasKitVersionFile = fileSystem.currentDirectory + .childDirectory('cache') + .childDirectory('bin') + .childDirectory('internal') + .childFile('canvaskit.version'); + canvasKitVersionFile.createSync(recursive: true); + canvasKitVersionFile.writeAsStringSync('abcdefg'); + final Cache cache = Cache.test(processManager: FakeProcessManager.any(), fileSystem: fileSystem); final Directory webCacheDirectory = cache.getWebSdkDirectory(); final FakeArtifactUpdater artifactUpdater = FakeArtifactUpdater(); final FlutterWebSdk webSdk = FlutterWebSdk(cache, platform: FakePlatform()); + final List messages = []; + final List downloads = []; + final List locations = []; artifactUpdater.onDownloadZipArchive = (String message, Uri uri, Directory location) { + messages.add(message); + downloads.add(uri.toString()); + locations.add(location.path); location.createSync(recursive: true); location.childFile('foo').createSync(); }; webCacheDirectory.childFile('bar').createSync(recursive: true); - webSdk.updateInner(artifactUpdater, fileSystem, FakeOperatingSystemUtils()); + await webSdk.updateInner(artifactUpdater, fileSystem, FakeOperatingSystemUtils()); + + expect(messages, [ + 'Downloading Web SDK...', + 'Downloading CanvasKit...', + ]); + + expect(downloads, [ + 'https://storage.googleapis.com/flutter_infra_release/flutter/null/flutter-web-sdk-linux-x64.zip', + 'https://chrome-infra-packages.appspot.com/dl/flutter/web/canvaskit_bundle/+/abcdefg', + ]); + + expect(locations, [ + 'cache/bin/cache/flutter_web_sdk', + 'cache/bin/cache/flutter_web_sdk', + ]); expect(webCacheDirectory.childFile('foo'), exists); expect(webCacheDirectory.childFile('bar'), isNot(exists)); diff --git a/packages/flutter_tools/test/general.shard/web/golden_comparator_test.dart b/packages/flutter_tools/test/general.shard/web/golden_comparator_test.dart index e459d5b88ad..e1e5069c873 100644 --- a/packages/flutter_tools/test/general.shard/web/golden_comparator_test.dart +++ b/packages/flutter_tools/test/general.shard/web/golden_comparator_test.dart @@ -13,6 +13,7 @@ import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/test/flutter_web_goldens.dart'; import 'package:flutter_tools/src/test/test_compiler.dart'; +import 'package:flutter_tools/src/web/compile.dart'; import 'package:test/fake.dart'; import '../../src/common.dart'; @@ -45,7 +46,12 @@ void main() { '--non-interactive', '--packages=.dart_tool/package_config.json', 'compiler_output' - ], stdout: '${jsonEncode(expectedResponse)}\n', + ], + stdout: '${jsonEncode(expectedResponse)}\n', + environment: const { + 'FLUTTER_TEST_BROWSER': 'chrome', + 'FLUTTER_WEB_RENDERER': 'html', + }, )); final TestGoldenComparator comparator = TestGoldenComparator( @@ -54,6 +60,7 @@ void main() { processManager: processManager, fileSystem: MemoryFileSystem.test(), logger: BufferLogger.test(), + webRenderer: WebRendererMode.html, ); final String result = await comparator.compareGoldens(testUri, imageBytes, goldenKey, false); @@ -82,6 +89,7 @@ void main() { processManager: processManager, fileSystem: MemoryFileSystem.test(), logger: BufferLogger.test(), + webRenderer: WebRendererMode.canvaskit, ); final String result = await comparator.compareGoldens(testUri, imageBytes, goldenKey, false); @@ -114,6 +122,7 @@ void main() { processManager: processManager, fileSystem: MemoryFileSystem.test(), logger: BufferLogger.test(), + webRenderer: WebRendererMode.html, ); final String result1 = await comparator.compareGoldens(testUri, imageBytes, goldenKey, false); @@ -158,6 +167,7 @@ void main() { processManager: processManager, fileSystem: MemoryFileSystem.test(), logger: BufferLogger.test(), + webRenderer: WebRendererMode.canvaskit, ); final String result1 = await comparator.compareGoldens(testUri, imageBytes, goldenKey, false); @@ -192,6 +202,7 @@ void main() { processManager: processManager, fileSystem: fileSystem, logger: BufferLogger.test(), + webRenderer: WebRendererMode.html, ); final String result = await comparator.compareGoldens(testUri, imageBytes, goldenKey, false); diff --git a/packages/flutter_tools/test/src/common.dart b/packages/flutter_tools/test/src/common.dart index feb0ad49542..6f0e31152c2 100644 --- a/packages/flutter_tools/test/src/common.dart +++ b/packages/flutter_tools/test/src/common.dart @@ -190,7 +190,7 @@ void testWithoutContext(String description, FutureOr Function() body, { List? tags, Map? onPlatform, int? retry, - }) { +}) { return test( description, () async { return runZoned(body, zoneValues: {