flutter/packages/flutter_tools/test/general.shard/web/compile_web_test.dart
Jackson Gardner ba626dc83a
Wasm/JS Dual Compile with the flutter tool (#141396)
This implements dual compile via the newly available flutter.js bootstrapping APIs for intelligent build fallback.
* Users can now use the `FlutterLoader.load` API from flutter.js
* Flutter tool injects build info into the `index.html` of the user so that the bootstrapper knows which build variants are available to bootstrap
* The semantics of the `--wasm` flag for `flutter build web` have changed:
  - Instead of producing a separate `build/web_wasm` directory, the output goes to the `build/web` directory like a normal web build
  - Produces a dual build that contains two build variants: dart2wasm+skwasm and dart2js+CanvasKit. The dart2wasm+skwasm will only work on Chrome in a cross-origin isolated context, all other environments will fall back to dart2js+CanvasKit.
  - `--wasm` and `--web-renderer` are now mutually exclusive. Since there are multiple build variants with `--wasm`, the web renderer cannot be expressed via a single command-line flag. For now, we are hard coding what build variants are produced with the `--wasm` flag, but I plan on making this more customizable in the future.
* Build targets now can optionally provide a "build key" which can uniquely identify any specific parameterization of that build target. This way, the build target can invalidate itself by changing its build key. This works a bit better than just stuffing everything into the environment defines because (a) it doesn't invalidate the entire build, just the targets which are affected and (b) settings for multiple build variants don't translate well to the flat map of environment defines.
2024-02-02 01:52:28 +00:00

176 lines
5.7 KiB
Dart

// 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/web/compile.dart';
import 'package:flutter_tools/src/web/file_generators/flutter_service_worker_js.dart';
import 'package:unified_analytics/unified_analytics.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 FakeAnalytics fakeAnalytics;
late BufferLogger logger;
late FakeFlutterVersion flutterVersion;
late FlutterProject flutterProject;
setUp(() {
fileSystem = MemoryFileSystem.test();
testUsage = TestUsage();
logger = BufferLogger.test();
flutterVersion = FakeFlutterVersion(frameworkVersion: '1.0.0', engineRevision: '9.8.7');
fakeAnalytics = getInitializedFakeAnalyticsInstance(
fs: fileSystem,
fakeFlutterVersion: flutterVersion,
);
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) {
expect(target, isA<WebServiceWorker>());
expect(environment.defines, <String, String>{
'TargetFile': 'target',
'HasWebPlugins': 'false',
'ServiceWorkerStrategy': ServiceWorkerStrategy.offlineFirst.cliName,
'BuildMode': 'debug',
'DartObfuscation': 'false',
'TrackWidgetCreation': 'true',
'TreeShakeIcons': 'false',
});
expect(environment.engineVersion, '9.8.7');
expect(environment.generateDartPluginRegistry, isFalse);
});
final WebBuilder webBuilder = WebBuilder(
logger: logger,
processManager: FakeProcessManager.any(),
buildSystem: buildSystem,
usage: testUsage,
flutterVersion: flutterVersion,
fileSystem: fileSystem,
analytics: fakeAnalytics,
);
await webBuilder.buildWeb(
flutterProject,
'target',
BuildInfo.debug,
ServiceWorkerStrategy.offlineFirst,
compilerConfigs: <WebCompilerConfig>[
const WasmCompilerConfig(
wasmOpt: WasmOptLevel.none,
renderer: WebRendererMode.skwasm,
),
const JsCompilerConfig.run(
nativeNullAssertions: true,
renderer: WebRendererMode.canvaskit,
),
],
);
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 build config event
expect(
testUsage.events,
unorderedEquals(
<TestUsageEvent>[
const TestUsageEvent(
'build',
'web',
label: 'web-compile',
parameters: CustomDimensions(
buildEventSettings:
'RunWasmOpt: none; WasmOmitTypeChecks: false; web-renderer: skwasm,canvaskit; web-target: wasm,js;',
),
),
],
),
);
expect(
fakeAnalytics.sentEvents,
containsAll(<Event>[
Event.flutterBuildInfo(
label: 'web-compile',
buildType: 'web',
settings: 'RunWasmOpt: none; WasmOmitTypeChecks: false; web-renderer: skwasm,canvaskit; web-target: wasm,js;',
),
]),
);
// Sends timing event.
final TestTimingEvent timingEvent = testUsage.timings.single;
expect(timingEvent.category, 'build');
expect(timingEvent.variableName, 'dual-compile');
expect(
analyticsTimingEventExists(
sentEvents: fakeAnalytics.sentEvents,
workflow: 'build',
variableName: 'dual-compile',
),
true,
);
});
testUsingContext('WebBuilder throws tool exit on failure', () async {
final TestBuildSystem buildSystem = TestBuildSystem.all(BuildResult(
success: false,
exceptions: <String, ExceptionMeasurement>{
'hello': ExceptionMeasurement(
'hello',
const FormatException('illegal character in input string'),
StackTrace.current,
),
},
));
final WebBuilder webBuilder = WebBuilder(
logger: logger,
processManager: FakeProcessManager.any(),
buildSystem: buildSystem,
usage: testUsage,
flutterVersion: flutterVersion,
fileSystem: fileSystem,
analytics: fakeAnalytics,
);
await expectLater(
() async => webBuilder.buildWeb(
flutterProject,
'target',
BuildInfo.debug,
ServiceWorkerStrategy.offlineFirst,
compilerConfigs: <WebCompilerConfig>[
const JsCompilerConfig.run(nativeNullAssertions: true, renderer: WebRendererMode.auto),
]
),
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);
expect(fakeAnalytics.sentEvents, isEmpty);
});
}