From 385728e7fdedfa3323f39ea662bc9607efcfc5d8 Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Wed, 5 Apr 2023 15:57:07 -0700 Subject: [PATCH] Add tools test for buildWeb compilation (#124179) Add tools test for buildWeb compilation --- .../lib/src/commands/build_web.dart | 10 +- .../lib/src/isolated/resident_web_runner.dart | 18 +- .../flutter_tools/lib/src/web/compile.dart | 176 ++++++++++-------- .../general.shard/web/compile_web_test.dart | 128 +++++++++++++ 4 files changed, 249 insertions(+), 83 deletions(-) create mode 100644 packages/flutter_tools/test/general.shard/web/compile_web_test.dart diff --git a/packages/flutter_tools/lib/src/commands/build_web.dart b/packages/flutter_tools/lib/src/commands/build_web.dart index d74c979480f..bcbc5c4d81f 100644 --- a/packages/flutter_tools/lib/src/commands/build_web.dart +++ b/packages/flutter_tools/lib/src/commands/build_web.dart @@ -7,6 +7,7 @@ import '../base/file_system.dart'; import '../build_info.dart'; import '../build_system/targets/web.dart'; import '../features.dart'; +import '../globals.dart' as globals; import '../html_utils.dart'; import '../project.dart'; import '../runner/flutter_command.dart' @@ -168,7 +169,14 @@ class BuildWebCommand extends BuildSubCommand { final String? outputDirectoryPath = stringArg('output'); displayNullSafetyMode(buildInfo); - await buildWeb( + final WebBuilder webBuilder = WebBuilder( + logger: globals.logger, + buildSystem: globals.buildSystem, + fileSystem: globals.fs, + flutterVersion: globals.flutterVersion, + usage: globals.flutterUsage, + ); + await webBuilder.buildWeb( flutterProject, target, buildInfo, 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 f7bb7910613..55eab275b6c 100644 --- a/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart +++ b/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart @@ -314,7 +314,14 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive). device!.generator!.accept(); cacheInitialDillCompilation(); } else { - await buildWeb( + final WebBuilder webBuilder = WebBuilder( + logger: _logger, + buildSystem: globals.buildSystem, + fileSystem: _fileSystem, + flutterVersion: globals.flutterVersion, + usage: globals.flutterUsage, + ); + await webBuilder.buildWeb( flutterProject, target, debuggingOptions.buildInfo, @@ -387,7 +394,14 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive). } } else { try { - await buildWeb( + final WebBuilder webBuilder = WebBuilder( + logger: _logger, + buildSystem: globals.buildSystem, + fileSystem: _fileSystem, + flutterVersion: globals.flutterVersion, + usage: globals.flutterUsage, + ); + await webBuilder.buildWeb( flutterProject, target, debuggingOptions.buildInfo, diff --git a/packages/flutter_tools/lib/src/web/compile.dart b/packages/flutter_tools/lib/src/web/compile.dart index 3e094464cff..e9509b9b80c 100644 --- a/packages/flutter_tools/lib/src/web/compile.dart +++ b/packages/flutter_tools/lib/src/web/compile.dart @@ -16,96 +16,112 @@ import '../globals.dart' as globals; import '../platform_plugins.dart'; import '../plugins.dart'; import '../project.dart'; +import '../reporting/reporting.dart'; +import '../version.dart'; import 'migrations/scrub_generated_plugin_registrant.dart'; export '../build_system/targets/web.dart' show kDart2jsDefaultOptimizationLevel; -Future buildWeb( - FlutterProject flutterProject, - String target, - BuildInfo buildInfo, - bool csp, - String serviceWorkerStrategy, - bool sourceMaps, - bool nativeNullAssertions, - bool isWasm, { - String dart2jsOptimization = kDart2jsDefaultOptimizationLevel, - String? baseHref, - bool dumpInfo = false, - bool noFrequencyBasedMinification = false, - String? outputDirectoryPath, -}) async { - final bool hasWebPlugins = (await findPlugins(flutterProject)) - .any((Plugin p) => p.platforms.containsKey(WebPlugin.kConfigKey)); - final Directory outputDirectory = outputDirectoryPath == null - ? globals.fs.directory(getWebBuildDirectory(isWasm)) - : globals.fs.directory(outputDirectoryPath); - outputDirectory.createSync(recursive: true); +class WebBuilder { + WebBuilder({ + required Logger logger, + required BuildSystem buildSystem, + required Usage usage, + required FlutterVersion flutterVersion, + required FileSystem fileSystem, + }) : _logger = logger, + _buildSystem = buildSystem, + _flutterUsage = usage, + _flutterVersion = flutterVersion, + _fileSystem = fileSystem; - // The migrators to apply to a Web project. - final List migrators = [ - ScrubGeneratedPluginRegistrant(flutterProject.web, globals.logger), - ]; + final Logger _logger; + final BuildSystem _buildSystem; + final Usage _flutterUsage; + final FlutterVersion _flutterVersion; + final FileSystem _fileSystem; - final ProjectMigration migration = ProjectMigration(migrators); - migration.run(); + Future buildWeb( + FlutterProject flutterProject, + String target, + BuildInfo buildInfo, + bool csp, + String serviceWorkerStrategy, + bool sourceMaps, + bool nativeNullAssertions, + bool isWasm, { + String dart2jsOptimization = kDart2jsDefaultOptimizationLevel, + String? baseHref, + bool dumpInfo = false, + bool noFrequencyBasedMinification = false, + String? outputDirectoryPath, + }) async { + final bool hasWebPlugins = + (await findPlugins(flutterProject)).any((Plugin p) => p.platforms.containsKey(WebPlugin.kConfigKey)); + final Directory outputDirectory = outputDirectoryPath == null + ? _fileSystem.directory(getWebBuildDirectory(isWasm)) + : _fileSystem.directory(outputDirectoryPath); + outputDirectory.createSync(recursive: true); - final Status status = globals.logger.startProgress('Compiling $target for the Web...'); - final Stopwatch sw = Stopwatch()..start(); - try { - final BuildResult result = await globals.buildSystem.build( - WebServiceWorker(globals.fs, buildInfo.webRenderer, isWasm: isWasm), - Environment( - projectDir: globals.fs.currentDirectory, - outputDir: outputDirectory, - buildDir: flutterProject.directory - .childDirectory('.dart_tool') - .childDirectory('flutter_build'), - defines: { - kTargetFile: target, - kHasWebPlugins: hasWebPlugins.toString(), - kCspMode: csp.toString(), - if (baseHref != null) - kBaseHref : baseHref, - kSourceMapsEnabled: sourceMaps.toString(), - kNativeNullAssertions: nativeNullAssertions.toString(), - kServiceWorkerStrategy: serviceWorkerStrategy, - kDart2jsOptimization: dart2jsOptimization, - kDart2jsDumpInfo: dumpInfo.toString(), - kDart2jsNoFrequencyBasedMinification: noFrequencyBasedMinification.toString(), - ...buildInfo.toBuildSystemEnvironment(), - }, - artifacts: globals.artifacts!, - fileSystem: globals.fs, - logger: globals.logger, - processManager: globals.processManager, - platform: globals.platform, - usage: globals.flutterUsage, - cacheDir: globals.cache.getRoot(), - engineVersion: globals.artifacts!.isLocalEngine - ? null - : globals.flutterVersion.engineRevision, - flutterRootDir: globals.fs.directory(Cache.flutterRoot), - // Web uses a different Dart plugin registry. - // https://github.com/flutter/flutter/issues/80406 - generateDartPluginRegistry: false, - )); - if (!result.success) { - for (final ExceptionMeasurement measurement in result.exceptions.values) { - globals.printError('Target ${measurement.target} failed: ${measurement.exception}', - stackTrace: measurement.fatal - ? measurement.stackTrace - : null, - ); + // The migrators to apply to a Web project. + final List migrators = [ + ScrubGeneratedPluginRegistrant(flutterProject.web, _logger), + ]; + + final ProjectMigration migration = ProjectMigration(migrators); + migration.run(); + + final Status status = _logger.startProgress('Compiling $target for the Web...'); + final Stopwatch sw = Stopwatch()..start(); + try { + final BuildResult result = await _buildSystem.build( + WebServiceWorker(_fileSystem, buildInfo.webRenderer, isWasm: isWasm), + Environment( + projectDir: _fileSystem.currentDirectory, + outputDir: outputDirectory, + buildDir: flutterProject.directory.childDirectory('.dart_tool').childDirectory('flutter_build'), + defines: { + kTargetFile: target, + kHasWebPlugins: hasWebPlugins.toString(), + kCspMode: csp.toString(), + if (baseHref != null) kBaseHref: baseHref, + kSourceMapsEnabled: sourceMaps.toString(), + kNativeNullAssertions: nativeNullAssertions.toString(), + kServiceWorkerStrategy: serviceWorkerStrategy, + kDart2jsOptimization: dart2jsOptimization, + kDart2jsDumpInfo: dumpInfo.toString(), + kDart2jsNoFrequencyBasedMinification: noFrequencyBasedMinification.toString(), + ...buildInfo.toBuildSystemEnvironment(), + }, + artifacts: globals.artifacts!, + fileSystem: _fileSystem, + logger: _logger, + processManager: globals.processManager, + platform: globals.platform, + usage: _flutterUsage, + cacheDir: globals.cache.getRoot(), + engineVersion: globals.artifacts!.isLocalEngine ? null : _flutterVersion.engineRevision, + flutterRootDir: _fileSystem.directory(Cache.flutterRoot), + // Web uses a different Dart plugin registry. + // https://github.com/flutter/flutter/issues/80406 + generateDartPluginRegistry: false, + )); + if (!result.success) { + for (final ExceptionMeasurement measurement in result.exceptions.values) { + _logger.printError( + 'Target ${measurement.target} failed: ${measurement.exception}', + stackTrace: measurement.fatal ? measurement.stackTrace : null, + ); + } + throwToolExit('Failed to compile application for the Web.'); } - throwToolExit('Failed to compile application for the Web.'); + } on Exception catch (err) { + throwToolExit(err.toString()); + } finally { + status.stop(); } - } on Exception catch (err) { - throwToolExit(err.toString()); - } finally { - status.stop(); + _flutterUsage.sendTiming('build', 'dart2js', Duration(milliseconds: sw.elapsedMilliseconds)); } - globals.flutterUsage.sendTiming('build', 'dart2js', Duration(milliseconds: sw.elapsedMilliseconds)); } /// Web rendering backend mode. diff --git a/packages/flutter_tools/test/general.shard/web/compile_web_test.dart b/packages/flutter_tools/test/general.shard/web/compile_web_test.dart new file mode 100644 index 00000000000..1b684870b0e --- /dev/null +++ b/packages/flutter_tools/test/general.shard/web/compile_web_test.dart @@ -0,0 +1,128 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file/memory.dart'; +import 'package:flutter_tools/src/base/logger.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/targets/web.dart'; +import 'package:flutter_tools/src/project.dart'; +import 'package:flutter_tools/src/reporting/reporting.dart'; +import 'package:flutter_tools/src/version.dart'; +import 'package:flutter_tools/src/web/compile.dart'; + +import '../../src/common.dart'; +import '../../src/context.dart'; +import '../../src/fakes.dart'; +import '../../src/test_build_system.dart'; + +void main() { + late MemoryFileSystem fileSystem; + late TestUsage testUsage; + late BufferLogger logger; + late FlutterVersion flutterVersion; + late FlutterProject flutterProject; + + setUp(() { + fileSystem = MemoryFileSystem.test(); + testUsage = TestUsage(); + logger = BufferLogger.test(); + flutterVersion = FakeFlutterVersion(frameworkVersion: '1.0.0', engineRevision: '9.8.7'); + + flutterProject = FlutterProject.fromDirectoryTest(fileSystem.currentDirectory); + fileSystem.file('.packages').createSync(); + }); + + testUsingContext('WebBuilder sets environment on success', () async { + final TestBuildSystem buildSystem = + TestBuildSystem.all(BuildResult(success: true), (Target target, Environment environment) { + final WebServiceWorker webServiceWorker = target as WebServiceWorker; + expect(webServiceWorker.isWasm, isTrue); + expect(webServiceWorker.webRenderer, WebRendererMode.autoDetect); + + expect(environment.defines, { + 'TargetFile': 'target', + 'HasWebPlugins': 'false', + 'cspMode': 'true', + 'SourceMaps': 'true', + 'NativeNullAssertions': 'true', + 'ServiceWorkerStrategy': 'serviceWorkerStrategy', + 'Dart2jsOptimization': 'O4', + 'Dart2jsDumpInfo': 'false', + 'Dart2jsNoFrequencyBasedMinification': 'false', + 'BuildMode': 'debug', + 'DartObfuscation': 'false', + 'TrackWidgetCreation': 'true', + 'TreeShakeIcons': 'false', + }); + + expect(environment.engineVersion, '9.8.7'); + expect(environment.generateDartPluginRegistry, isFalse); + }); + + final WebBuilder webBuilder = WebBuilder( + logger: logger, + buildSystem: buildSystem, + usage: testUsage, + flutterVersion: flutterVersion, + fileSystem: fileSystem, + ); + await webBuilder.buildWeb( + flutterProject, + 'target', + BuildInfo.debug, + true, + 'serviceWorkerStrategy', + true, + true, + true, + ); + + expect(logger.statusText, contains('Compiling target for the Web...')); + expect(logger.errorText, isEmpty); + // Runs ScrubGeneratedPluginRegistrant migrator. + expect(logger.traceText, contains('generated_plugin_registrant.dart not found. Skipping.')); + + // Sends timing event. + final TestTimingEvent timingEvent = testUsage.timings.single; + expect(timingEvent.category, 'build'); + expect(timingEvent.variableName, 'dart2js'); + }); + + testUsingContext('WebBuilder throws tool exit on failure', () async { + final TestBuildSystem buildSystem = TestBuildSystem.all(BuildResult( + success: false, + exceptions: { + 'hello': ExceptionMeasurement( + 'hello', + const FormatException('illegal character in input string'), + StackTrace.current, + ), + }, + )); + + final WebBuilder webBuilder = WebBuilder( + logger: logger, + buildSystem: buildSystem, + usage: testUsage, + flutterVersion: flutterVersion, + fileSystem: fileSystem, + ); + await expectLater( + () async => webBuilder.buildWeb( + flutterProject, + 'target', + BuildInfo.debug, + true, + 'serviceWorkerStrategy', + true, + true, + true, + ), + throwsToolExit(message: 'Failed to compile application for the Web.')); + + expect(logger.errorText, contains('Target hello failed: FormatException: illegal character in input string')); + expect(testUsage.timings, isEmpty); + }); +}