mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Wasm harness for unit tests. (flutter/engine#36255)
This commit is contained in:
parent
880e443b79
commit
005dc4e774
@ -13,11 +13,11 @@ chrome:
|
||||
# `self.m.platform.name.capitalize()` evaluates to. See:
|
||||
#
|
||||
# recipe_modules/web_util/api.py
|
||||
Linux: 1027016
|
||||
Mac: 1027007
|
||||
Mac_Arm: 1026994
|
||||
Win: 1026943
|
||||
version: '105.0' # CIPD tag for the above Build IDs. Normally "ChromeMajorVersion.UploadAttempt". ;)
|
||||
Linux: 1047731
|
||||
Mac: 1047732
|
||||
Mac_Arm: 1047734
|
||||
Win: 1047731
|
||||
version: '107.0' # CIPD tag for the above Build IDs. Normally "ChromeMajorVersion.UploadAttempt". ;)
|
||||
|
||||
## Firefox does not use CIPD. To update the version, simply update it in this
|
||||
## file.
|
||||
|
||||
@ -21,11 +21,15 @@ import 'environment.dart';
|
||||
|
||||
/// Provides an environment for desktop Chrome.
|
||||
class ChromeEnvironment implements BrowserEnvironment {
|
||||
ChromeEnvironment(this._enableWasmGC);
|
||||
|
||||
late final BrowserInstallation _installation;
|
||||
|
||||
final bool _enableWasmGC;
|
||||
|
||||
@override
|
||||
Future<Browser> launchBrowserInstance(Uri url, {bool debug = false}) async {
|
||||
return Chrome(url, _installation, debug: debug);
|
||||
return Chrome(url, _installation, debug: debug, enableWasmGC: _enableWasmGC);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -60,7 +64,7 @@ class ChromeEnvironment implements BrowserEnvironment {
|
||||
class Chrome extends Browser {
|
||||
/// Starts a new instance of Chrome open to the given [url], which may be a
|
||||
/// [Uri] or a [String].
|
||||
factory Chrome(Uri url, BrowserInstallation installation, {bool debug = false}) {
|
||||
factory Chrome(Uri url, BrowserInstallation installation, {required bool debug, required bool enableWasmGC}) {
|
||||
final Completer<Uri> remoteDebuggerCompleter = Completer<Uri>.sync();
|
||||
return Chrome._(BrowserProcess(() async {
|
||||
// A good source of various Chrome CLI options:
|
||||
@ -76,7 +80,15 @@ class Chrome extends Browser {
|
||||
final bool isChromeNoSandbox =
|
||||
Platform.environment['CHROME_NO_SANDBOX'] == 'true';
|
||||
final String dir = environment.webUiDartToolDir.createTempSync('test_chrome_user_data_').resolveSymbolicLinksSync();
|
||||
final String jsFlags = enableWasmGC ? <String>[
|
||||
'--experimental-wasm-gc',
|
||||
'--wasm-gc-js-interop',
|
||||
'--experimental-wasm-stack-switching',
|
||||
'--experimental-wasm-type-reflection',
|
||||
'--wasm-gc-js-interop',
|
||||
].join(' ') : '';
|
||||
final List<String> args = <String>[
|
||||
if (jsFlags.isNotEmpty) '--js-flags=$jsFlags',
|
||||
'--user-data-dir=$dir',
|
||||
url.toString(),
|
||||
if (!debug)
|
||||
|
||||
@ -261,10 +261,10 @@ const List<String> kAllBrowserNames = <String>[
|
||||
/// Creates an environment for a browser.
|
||||
///
|
||||
/// The [browserName] matches the browser name passed as the `--browser` option.
|
||||
BrowserEnvironment getBrowserEnvironment(String browserName) {
|
||||
BrowserEnvironment getBrowserEnvironment(String browserName, { required bool enableWasmGC }) {
|
||||
switch (browserName) {
|
||||
case kChrome:
|
||||
return ChromeEnvironment();
|
||||
return ChromeEnvironment(enableWasmGC);
|
||||
case kEdge:
|
||||
return EdgeEnvironment();
|
||||
case kFirefox:
|
||||
|
||||
@ -105,9 +105,26 @@ class Environment {
|
||||
/// The "dart" executable file.
|
||||
String get dartExecutable => pathlib.join(dartSdkDir.path, 'bin', 'dart');
|
||||
|
||||
/// Path to dartaotruntime for running aot snapshots
|
||||
String get dartAotRuntimePath => pathlib.join(dartSdkDir.path, 'bin', 'dartaotruntime');
|
||||
|
||||
/// The "pub" executable file.
|
||||
String get pubExecutable => pathlib.join(dartSdkDir.path, 'bin', 'pub');
|
||||
|
||||
/// The path to dart2wasm pre-compiled snapshot
|
||||
String get dart2wasmSnapshotPath => pathlib.join(dartSdkDir.path, 'bin', 'snapshots', 'dart2wasm_product.snapshot');
|
||||
|
||||
/// The path to dart2wasm.dart file
|
||||
String get dart2wasmScriptPath => pathlib.join(
|
||||
engineSrcDir.path,
|
||||
'third_party',
|
||||
'dart',
|
||||
'pkg',
|
||||
'dart2wasm',
|
||||
'bin',
|
||||
'dart2wasm.dart'
|
||||
);
|
||||
|
||||
/// Path to where github.com/flutter/engine is checked out inside the engine workspace.
|
||||
io.Directory get flutterDirectory =>
|
||||
io.Directory(pathlib.join(engineSrcDir.path, 'flutter'));
|
||||
|
||||
@ -33,11 +33,17 @@ class RunCommand extends Command<bool> with ArgUtils<bool> {
|
||||
help: 'Whether we require Skia Gold to be available or not. When this '
|
||||
'flag is true, the tests will fail if Skia Gold is not available.',
|
||||
);
|
||||
argParser.addFlag(
|
||||
'wasm',
|
||||
help: 'Whether the test we are running are compiled to webassembly.'
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String get name => 'run';
|
||||
|
||||
bool get isWasm => boolArg('wasm');
|
||||
|
||||
bool get isListSteps => boolArg('list');
|
||||
|
||||
/// When running screenshot tests, require Skia Gold to be available and
|
||||
@ -59,6 +65,7 @@ class RunCommand extends Command<bool> with ArgUtils<bool> {
|
||||
'run_tests_$browserName': RunTestsStep(
|
||||
browserName: browserName,
|
||||
isDebug: false,
|
||||
isWasm: isWasm,
|
||||
doUpdateScreenshotGoldens: false,
|
||||
requireSkiaGold: requireSkiaGold,
|
||||
overridePathToCanvasKit: null,
|
||||
|
||||
@ -23,9 +23,10 @@ import '../utils.dart';
|
||||
/// * test/ - compiled test code
|
||||
/// * test_images/ - test images copied from Skis sources.
|
||||
class CompileTestsStep implements PipelineStep {
|
||||
CompileTestsStep({this.testFiles, this.useLocalCanvasKit = false});
|
||||
CompileTestsStep({this.testFiles, this.useLocalCanvasKit = false, this.isWasm = false});
|
||||
|
||||
final List<FilePath>? testFiles;
|
||||
final bool isWasm;
|
||||
|
||||
final bool useLocalCanvasKit;
|
||||
|
||||
@ -43,11 +44,15 @@ class CompileTestsStep implements PipelineStep {
|
||||
@override
|
||||
Future<void> run() async {
|
||||
await environment.webUiBuildDir.create();
|
||||
if (isWasm) {
|
||||
await copyDart2WasmTestScript();
|
||||
await copyDart2WasmRuntime();
|
||||
}
|
||||
await copyCanvasKitFiles(useLocalCanvasKit: useLocalCanvasKit);
|
||||
await buildHostPage();
|
||||
await copyTestFonts();
|
||||
await copySkiaTestImages();
|
||||
await compileTests(testFiles ?? findAllTests());
|
||||
await compileTests(testFiles ?? findAllTests(), isWasm);
|
||||
}
|
||||
}
|
||||
|
||||
@ -124,6 +129,32 @@ Future<void> copySkiaTestImages() async {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> copyDart2WasmRuntime() async {
|
||||
final io.File sourceFile = io.File(pathlib.join(
|
||||
environment.dartSdkDir.path,
|
||||
'bin',
|
||||
'dart2wasm_runtime.mjs',
|
||||
));
|
||||
final io.Directory targetDir = io.Directory(pathlib.join(
|
||||
environment.webUiBuildDir.path,
|
||||
'dart2wasm_runtime.mjs',
|
||||
));
|
||||
|
||||
await sourceFile.copy(targetDir.path);
|
||||
}
|
||||
|
||||
Future<void> copyDart2WasmTestScript() async {
|
||||
final io.File sourceFile = io.File(pathlib.join(
|
||||
environment.webUiDevDir.path,
|
||||
'test_dart2wasm.js',
|
||||
));
|
||||
final io.Directory targetDir = io.Directory(pathlib.join(
|
||||
environment.webUiBuildDir.path,
|
||||
'test_dart2wasm.js',
|
||||
));
|
||||
await sourceFile.copy(targetDir.path);
|
||||
}
|
||||
|
||||
Future<void> copyCanvasKitFiles({bool useLocalCanvasKit = false}) async {
|
||||
// If CanvasKit has been built locally, use that instead of the CIPD version.
|
||||
final io.File localCanvasKitWasm = io.File(pathlib.join(
|
||||
@ -191,18 +222,18 @@ Future<void> copyCanvasKitFiles({bool useLocalCanvasKit = false}) async {
|
||||
}
|
||||
|
||||
/// Compiles the specified unit tests.
|
||||
Future<void> compileTests(List<FilePath> testFiles) async {
|
||||
Future<void> compileTests(List<FilePath> testFiles, bool isWasm) async {
|
||||
final Stopwatch stopwatch = Stopwatch()..start();
|
||||
|
||||
final TestsByRenderer sortedTests = sortTestsByRenderer(testFiles);
|
||||
|
||||
await Future.wait(<Future<void>>[
|
||||
if (sortedTests.htmlTests.isNotEmpty)
|
||||
_compileTestsInParallel(targets: sortedTests.htmlTests),
|
||||
_compileTestsInParallel(targets: sortedTests.htmlTests, isWasm: isWasm),
|
||||
if (sortedTests.canvasKitTests.isNotEmpty)
|
||||
_compileTestsInParallel(targets: sortedTests.canvasKitTests, renderer: Renderer.canvasKit),
|
||||
_compileTestsInParallel(targets: sortedTests.canvasKitTests, renderer: Renderer.canvasKit, isWasm: isWasm),
|
||||
if (sortedTests.skwasmTests.isNotEmpty)
|
||||
_compileTestsInParallel(targets: sortedTests.skwasmTests, renderer: Renderer.skwasm),
|
||||
_compileTestsInParallel(targets: sortedTests.skwasmTests, renderer: Renderer.skwasm, isWasm: isWasm),
|
||||
]);
|
||||
|
||||
stopwatch.stop();
|
||||
@ -210,7 +241,7 @@ Future<void> compileTests(List<FilePath> testFiles) async {
|
||||
final int targetCount = sortedTests.numTargetsToCompile;
|
||||
print(
|
||||
'Built $targetCount tests in ${stopwatch.elapsedMilliseconds ~/ 1000} '
|
||||
'seconds using $_dart2jsConcurrency concurrent dart2js processes.',
|
||||
'seconds using $_dart2jsConcurrency concurrent compile processes.',
|
||||
);
|
||||
}
|
||||
|
||||
@ -223,10 +254,11 @@ final Pool _dart2jsPool = Pool(_dart2jsConcurrency);
|
||||
Future<void> _compileTestsInParallel({
|
||||
required List<FilePath> targets,
|
||||
Renderer renderer = Renderer.html,
|
||||
bool isWasm = false,
|
||||
}) async {
|
||||
final Stream<bool> results = _dart2jsPool.forEach(
|
||||
targets,
|
||||
(FilePath file) => compileUnitTest(file, renderer: renderer),
|
||||
(FilePath file) => compileUnitTest(file, renderer: renderer, isWasm: isWasm),
|
||||
);
|
||||
await for (final bool isSuccess in results) {
|
||||
if (!isSuccess) {
|
||||
@ -235,6 +267,11 @@ Future<void> _compileTestsInParallel({
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> compileUnitTest(FilePath input, {required Renderer renderer, required bool isWasm}) async {
|
||||
return isWasm ? compileUnitTestToWasm(input, renderer: renderer)
|
||||
: compileUnitTestToJS(input, renderer: renderer);
|
||||
}
|
||||
|
||||
/// Compiles one unit test using `dart2js`.
|
||||
///
|
||||
/// When building for CanvasKit we have to use extra argument
|
||||
@ -252,7 +289,7 @@ Future<void> _compileTestsInParallel({
|
||||
/// directory before test are build. See [_copyFilesFromTestToBuild].
|
||||
///
|
||||
/// Later the extra files will be deleted in [_cleanupExtraFilesUnderTestDir].
|
||||
Future<bool> compileUnitTest(FilePath input, {required Renderer renderer}) async {
|
||||
Future<bool> compileUnitTestToJS(FilePath input, {required Renderer renderer}) async {
|
||||
// Compile to different directories for different renderers. This allows us
|
||||
// to run the same test in multiple renderers.
|
||||
final String targetFileName = pathlib.join(
|
||||
@ -305,6 +342,50 @@ Future<bool> compileUnitTest(FilePath input, {required Renderer renderer}) async
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> compileUnitTestToWasm(FilePath input, {required Renderer renderer}) async {
|
||||
final String targetFileName = pathlib.join(
|
||||
environment.webUiBuildDir.path,
|
||||
'${input.relativeToWebUi}.browser_test.dart.wasm',
|
||||
);
|
||||
|
||||
final io.Directory directoryToTarget = io.Directory(pathlib.join(
|
||||
environment.webUiBuildDir.path,
|
||||
pathlib.dirname(input.relativeToWebUi)));
|
||||
|
||||
if (!directoryToTarget.existsSync()) {
|
||||
directoryToTarget.createSync(recursive: true);
|
||||
}
|
||||
|
||||
final List<String> arguments = <String>[
|
||||
environment.dart2wasmSnapshotPath,
|
||||
|
||||
'--dart-sdk=${environment.dartSdkDir.path}',
|
||||
|
||||
// We do not want to auto-select a renderer in tests. As of today, tests
|
||||
// are designed to run in one specific mode. So instead, we specify the
|
||||
// renderer explicitly.
|
||||
'-DFLUTTER_WEB_AUTO_DETECT=false',
|
||||
'-DFLUTTER_WEB_USE_SKIA=${renderer == Renderer.canvasKit}',
|
||||
'-DFLUTTER_WEB_USE_SKWASM=${renderer == Renderer.skwasm}',
|
||||
input.relativeToWebUi, // current path.
|
||||
targetFileName, // target path.
|
||||
];
|
||||
|
||||
final int exitCode = await runProcess(
|
||||
environment.dartAotRuntimePath,
|
||||
arguments,
|
||||
workingDirectory: environment.webUiRootDir.path,
|
||||
);
|
||||
|
||||
if (exitCode != 0) {
|
||||
io.stderr.writeln('ERROR: Failed to compile test $input. '
|
||||
'dart2wasm exited with exit code $exitCode');
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> buildHostPage() async {
|
||||
final String hostDartPath = pathlib.join('lib', 'static', 'host.dart');
|
||||
final io.File hostDartFile = io.File(pathlib.join(
|
||||
|
||||
@ -38,11 +38,13 @@ class RunTestsStep implements PipelineStep {
|
||||
required this.requireSkiaGold,
|
||||
this.testFiles,
|
||||
required this.overridePathToCanvasKit,
|
||||
required this.isWasm
|
||||
});
|
||||
|
||||
final String browserName;
|
||||
final List<FilePath>? testFiles;
|
||||
final bool isDebug;
|
||||
final bool isWasm;
|
||||
final bool doUpdateScreenshotGoldens;
|
||||
final String? overridePathToCanvasKit;
|
||||
|
||||
@ -62,7 +64,7 @@ class RunTestsStep implements PipelineStep {
|
||||
Future<void> run() async {
|
||||
await _prepareTestResultsDirectory();
|
||||
|
||||
final BrowserEnvironment browserEnvironment = getBrowserEnvironment(browserName);
|
||||
final BrowserEnvironment browserEnvironment = getBrowserEnvironment(browserName, enableWasmGC: isWasm);
|
||||
await browserEnvironment.prepare();
|
||||
|
||||
final SkiaGoldClient? skiaClient = await _createSkiaClient();
|
||||
@ -77,6 +79,7 @@ class RunTestsStep implements PipelineStep {
|
||||
browserEnvironment: browserEnvironment,
|
||||
expectFailure: false,
|
||||
isDebug: isDebug,
|
||||
isWasm: isWasm,
|
||||
doUpdateScreenshotGoldens: doUpdateScreenshotGoldens,
|
||||
skiaClient: skiaClient,
|
||||
overridePathToCanvasKit: overridePathToCanvasKit,
|
||||
@ -90,6 +93,7 @@ class RunTestsStep implements PipelineStep {
|
||||
browserEnvironment: browserEnvironment,
|
||||
expectFailure: false,
|
||||
isDebug: isDebug,
|
||||
isWasm: isWasm,
|
||||
doUpdateScreenshotGoldens: doUpdateScreenshotGoldens,
|
||||
skiaClient: skiaClient,
|
||||
overridePathToCanvasKit: overridePathToCanvasKit,
|
||||
@ -103,6 +107,7 @@ class RunTestsStep implements PipelineStep {
|
||||
browserEnvironment: browserEnvironment,
|
||||
expectFailure: false,
|
||||
isDebug: isDebug,
|
||||
isWasm: isWasm,
|
||||
doUpdateScreenshotGoldens: doUpdateScreenshotGoldens,
|
||||
skiaClient: skiaClient,
|
||||
overridePathToCanvasKit: overridePathToCanvasKit,
|
||||
@ -176,6 +181,7 @@ Future<void> _runTestBatch({
|
||||
required List<FilePath> testFiles,
|
||||
required Renderer renderer,
|
||||
required bool isDebug,
|
||||
required bool isWasm,
|
||||
required BrowserEnvironment browserEnvironment,
|
||||
required bool doUpdateScreenshotGoldens,
|
||||
required bool expectFailure,
|
||||
@ -225,6 +231,7 @@ Future<void> _runTestBatch({
|
||||
doUpdateScreenshotGoldens: !expectFailure && doUpdateScreenshotGoldens,
|
||||
skiaClient: skiaClient,
|
||||
overridePathToCanvasKit: overridePathToCanvasKit,
|
||||
isWasm: isWasm,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
73
engine/src/flutter/lib/web_ui/dev/test_dart2wasm.js
Normal file
73
engine/src/flutter/lib/web_ui/dev/test_dart2wasm.js
Normal file
@ -0,0 +1,73 @@
|
||||
// Copyright 2013 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.
|
||||
|
||||
// This script runs in HTML files and loads and instantiates dart unit tests
|
||||
// that are compiled to WebAssembly. It is based off of the `test/dart.js`
|
||||
// script from the `test` dart package.
|
||||
|
||||
window.onload = async function () {
|
||||
// Sends an error message to the server indicating that the script failed to
|
||||
// load.
|
||||
//
|
||||
// This mimics a MultiChannel-formatted message.
|
||||
var sendLoadException = function (message) {
|
||||
window.parent.postMessage({
|
||||
"href": window.location.href,
|
||||
"data": [0, { "type": "loadException", "message": message }],
|
||||
"exception": true,
|
||||
}, window.location.origin);
|
||||
}
|
||||
|
||||
// Listen for dartLoadException events and forward to the server.
|
||||
window.addEventListener('dartLoadException', function (e) {
|
||||
sendLoadException(e.detail);
|
||||
});
|
||||
|
||||
// The basename of the current page.
|
||||
var name = window.location.href.replace(/.*\//, '').replace(/#.*/, '');
|
||||
|
||||
// Find <link rel="x-dart-test">.
|
||||
var links = document.getElementsByTagName("link");
|
||||
var testLinks = [];
|
||||
var length = links.length;
|
||||
for (var i = 0; i < length; ++i) {
|
||||
if (links[i].rel == "x-dart-test") testLinks.push(links[i]);
|
||||
}
|
||||
|
||||
if (testLinks.length != 1) {
|
||||
sendLoadException(
|
||||
'Expected exactly 1 <link rel="x-dart-test"> in ' + name + ', found ' +
|
||||
testLinks.length + '.');
|
||||
return;
|
||||
}
|
||||
|
||||
var link = testLinks[0];
|
||||
|
||||
if (link.href == '') {
|
||||
sendLoadException(
|
||||
'Expected <link rel="x-dart-test"> in ' + name + ' to have an "href" ' +
|
||||
'attribute.');
|
||||
return;
|
||||
}
|
||||
|
||||
let dart2wasm_runtime;
|
||||
let moduleInstance;
|
||||
try {
|
||||
dart2wasm_runtime = await import('./dart2wasm_runtime.mjs');
|
||||
const dartModulePromise = WebAssembly.compileStreaming(fetch(link.href + ".browser_test.dart.wasm"));
|
||||
moduleInstance = await dart2wasm_runtime.instantiate(dartModulePromise, {});
|
||||
} catch (exception) {
|
||||
const message = `Failed to fetch and instantiate wasm module: ${exception}`;
|
||||
sendLoadException(message);
|
||||
}
|
||||
|
||||
if (moduleInstance) {
|
||||
try {
|
||||
await dart2wasm_runtime.invoke(moduleInstance);
|
||||
} catch (exception) {
|
||||
const message = `Exception while invoking test: ${exception}`;
|
||||
sendLoadException(message);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -47,6 +47,7 @@ class BrowserPlatform extends PlatformPlugin {
|
||||
required this.server,
|
||||
required this.renderer,
|
||||
required this.isDebug,
|
||||
required this.isWasm,
|
||||
required this.doUpdateScreenshotGoldens,
|
||||
required this.packageConfig,
|
||||
required this.skiaClient,
|
||||
@ -106,6 +107,7 @@ class BrowserPlatform extends PlatformPlugin {
|
||||
required bool doUpdateScreenshotGoldens,
|
||||
required SkiaGoldClient? skiaClient,
|
||||
required String? overridePathToCanvasKit,
|
||||
required bool isWasm,
|
||||
}) async {
|
||||
final shelf_io.IOServer server =
|
||||
shelf_io.IOServer(await HttpMultiServer.loopback(0));
|
||||
@ -114,6 +116,7 @@ class BrowserPlatform extends PlatformPlugin {
|
||||
renderer: renderer,
|
||||
server: server,
|
||||
isDebug: Configuration.current.pauseAfterLoad,
|
||||
isWasm: isWasm,
|
||||
doUpdateScreenshotGoldens: doUpdateScreenshotGoldens,
|
||||
packageConfig: await loadPackageConfigUri((await Isolate.packageConfig)!),
|
||||
skiaClient: skiaClient,
|
||||
@ -126,6 +129,8 @@ class BrowserPlatform extends PlatformPlugin {
|
||||
/// breakpoints in the code.
|
||||
final bool isDebug;
|
||||
|
||||
final bool isWasm;
|
||||
|
||||
/// The underlying server.
|
||||
final shelf.Server server;
|
||||
|
||||
@ -368,6 +373,7 @@ class BrowserPlatform extends PlatformPlugin {
|
||||
|
||||
static const Map<String, String> contentTypes = <String, String>{
|
||||
'.js': 'text/javascript',
|
||||
'.mjs': 'text/javascript',
|
||||
'.wasm': 'application/wasm',
|
||||
'.html': 'text/html',
|
||||
'.htm': 'text/html',
|
||||
@ -439,6 +445,8 @@ class BrowserPlatform extends PlatformPlugin {
|
||||
final String scriptBase = htmlEscape.convert(p.basename(test));
|
||||
final String link = '<link rel="x-dart-test" href="$scriptBase">';
|
||||
|
||||
final String testRunner = isWasm ? '/test_dart2wasm.js' : 'packages/test/dart.js';
|
||||
|
||||
return shelf.Response.ok('''
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
@ -451,7 +459,7 @@ class BrowserPlatform extends PlatformPlugin {
|
||||
};
|
||||
</script>
|
||||
$link
|
||||
<script src="packages/test/dart.js"></script>
|
||||
<script src="$testRunner"></script>
|
||||
</head>
|
||||
</html>
|
||||
''', headers: <String, String>{'Content-Type': 'text/html'});
|
||||
@ -502,10 +510,6 @@ class BrowserPlatform extends PlatformPlugin {
|
||||
return suite;
|
||||
}
|
||||
|
||||
@override
|
||||
StreamChannel<dynamic> loadChannel(String path, SuitePlatform platform) =>
|
||||
throw UnimplementedError();
|
||||
|
||||
Future<BrowserManager?>? _browserManager;
|
||||
Future<BrowserManager> get browserManager async => (await _browserManager!)!;
|
||||
|
||||
@ -533,6 +537,7 @@ class BrowserPlatform extends PlatformPlugin {
|
||||
url: hostUrl,
|
||||
future: completer.future,
|
||||
packageConfig: packageConfig,
|
||||
isWasm: isWasm,
|
||||
debug: isDebug,
|
||||
renderer: renderer,
|
||||
);
|
||||
@ -630,7 +635,7 @@ class BrowserManager {
|
||||
/// Creates a new BrowserManager that communicates with the browser over
|
||||
/// [webSocket].
|
||||
BrowserManager._(this.packageConfig, this._browser, this._browserEnvironment,
|
||||
this._renderer, WebSocketChannel webSocket) {
|
||||
this._renderer, this._isWasm, WebSocketChannel webSocket) {
|
||||
// The duration should be short enough that the debugging console is open as
|
||||
// soon as the user is done setting breakpoints, but long enough that a test
|
||||
// doing a lot of synchronous work doesn't trigger a false positive.
|
||||
@ -702,6 +707,9 @@ class BrowserManager {
|
||||
/// Whether the channel to the browser has closed.
|
||||
bool _closed = false;
|
||||
|
||||
/// Whether we are running tests that have been compiled to WebAssembly.
|
||||
final bool _isWasm;
|
||||
|
||||
/// The completer for [_BrowserEnvironment.displayPause].
|
||||
///
|
||||
/// This will be `null` as long as the browser isn't displaying a pause
|
||||
@ -744,6 +752,7 @@ class BrowserManager {
|
||||
required Future<WebSocketChannel> future,
|
||||
required PackageConfig packageConfig,
|
||||
required Renderer renderer,
|
||||
required bool isWasm,
|
||||
bool debug = false,
|
||||
}) async {
|
||||
final Browser browser =
|
||||
@ -755,6 +764,7 @@ class BrowserManager {
|
||||
packageConfig: packageConfig,
|
||||
browser: browser,
|
||||
renderer: renderer,
|
||||
isWasm: isWasm,
|
||||
debug: debug);
|
||||
}
|
||||
|
||||
@ -765,6 +775,7 @@ class BrowserManager {
|
||||
required PackageConfig packageConfig,
|
||||
required Browser browser,
|
||||
required Renderer renderer,
|
||||
required bool isWasm,
|
||||
bool debug = false,
|
||||
}) {
|
||||
final Completer<BrowserManager> completer = Completer<BrowserManager>();
|
||||
@ -786,7 +797,7 @@ class BrowserManager {
|
||||
return;
|
||||
}
|
||||
completer.complete(BrowserManager._(
|
||||
packageConfig, browser, browserEnvironment, renderer, webSocket));
|
||||
packageConfig, browser, browserEnvironment, renderer, isWasm, webSocket));
|
||||
}).catchError((Object error, StackTrace stackTrace) {
|
||||
browser.close();
|
||||
if (completer.isCompleted) {
|
||||
@ -848,6 +859,11 @@ class BrowserManager {
|
||||
sink.close();
|
||||
}));
|
||||
|
||||
if (Configuration.current.pauseAfterLoad) {
|
||||
print('Browser loaded. Press enter to start tests...');
|
||||
stdin.readLineSync();
|
||||
}
|
||||
|
||||
return _pool.withResource<RunnerSuite>(() async {
|
||||
_channel.sink.add(<String, dynamic>{
|
||||
'command': 'loadSuite',
|
||||
@ -865,24 +881,30 @@ class BrowserManager {
|
||||
suiteChannel,
|
||||
message);
|
||||
|
||||
final String sourceMapFileName =
|
||||
'${p.basename(path)}.browser_test.dart.js.map';
|
||||
final String pathToTest = p.dirname(path);
|
||||
if (_isWasm) {
|
||||
// We don't have mapping for wasm yet. But we should send a message
|
||||
// to let the host page move forward.
|
||||
controller!.channel('test.browser.mapper').sink.add(null);
|
||||
} else {
|
||||
final String sourceMapFileName =
|
||||
'${p.basename(path)}.browser_test.dart.js.map';
|
||||
final String pathToTest = p.dirname(path);
|
||||
|
||||
final String mapPath = p.join(env.environment.webUiRootDir.path,
|
||||
'build', getBuildDirForRenderer(_renderer), pathToTest, sourceMapFileName);
|
||||
final String mapPath = p.join(env.environment.webUiRootDir.path,
|
||||
'build', getBuildDirForRenderer(_renderer), pathToTest, sourceMapFileName);
|
||||
|
||||
final Map<String, Uri> packageMap = <String, Uri>{
|
||||
for (Package p in packageConfig.packages) p.name: p.packageUriRoot
|
||||
};
|
||||
final JSStackTraceMapper mapper = JSStackTraceMapper(
|
||||
await File(mapPath).readAsString(),
|
||||
mapUrl: p.toUri(mapPath),
|
||||
packageMap: packageMap,
|
||||
sdkRoot: p.toUri(sdkDir),
|
||||
);
|
||||
final Map<String, Uri> packageMap = <String, Uri>{
|
||||
for (Package p in packageConfig.packages) p.name: p.packageUriRoot
|
||||
};
|
||||
final JSStackTraceMapper mapper = JSStackTraceMapper(
|
||||
await File(mapPath).readAsString(),
|
||||
mapUrl: p.toUri(mapPath),
|
||||
packageMap: packageMap,
|
||||
sdkRoot: p.toUri(sdkDir),
|
||||
);
|
||||
|
||||
controller!.channel('test.browser.mapper').sink.add(mapper.serialize());
|
||||
controller!.channel('test.browser.mapper').sink.add(mapper.serialize());
|
||||
}
|
||||
|
||||
_controllers.add(controller!);
|
||||
return await controller!.suite;
|
||||
@ -944,6 +966,10 @@ class BrowserManager {
|
||||
/// Closes the manager and releases any resources it owns, including closing
|
||||
/// the browser.
|
||||
Future<void> close() => _closeMemoizer.runOnce(() {
|
||||
if (Configuration.current.pauseAfterLoad) {
|
||||
print('Test run finished. Press enter to close browser...');
|
||||
stdin.readLineSync();
|
||||
}
|
||||
_closed = true;
|
||||
_timer.cancel();
|
||||
_pauseCompleter?.complete();
|
||||
|
||||
@ -31,17 +31,17 @@ class TestCommand extends Command<bool> with ArgUtils<bool> {
|
||||
help: 'Run in watch mode so the tests re-run whenever a change is '
|
||||
'made.',
|
||||
)
|
||||
..addFlag('use-system-flutter',
|
||||
help:
|
||||
'integration tests are using flutter repository for various tasks'
|
||||
', such as flutter drive, flutter pub get. If this flag is set, felt '
|
||||
'will use flutter command without cloning the repository. This flag '
|
||||
'can save internet bandwidth. However use with caution. Note that '
|
||||
'since flutter repo is always synced to youngest commit older than '
|
||||
'the engine commit for the tests running in CI, the tests results '
|
||||
"won't be consistent with CIs when this flag is set. flutter "
|
||||
'command should be set in the PATH for this flag to be useful.'
|
||||
'This flag can also be used to test local Flutter changes.')
|
||||
..addFlag(
|
||||
'use-system-flutter',
|
||||
help: 'integration tests are using flutter repository for various tasks'
|
||||
', such as flutter drive, flutter pub get. If this flag is set, felt '
|
||||
'will use flutter command without cloning the repository. This flag '
|
||||
'can save internet bandwidth. However use with caution. Note that '
|
||||
'since flutter repo is always synced to youngest commit older than '
|
||||
'the engine commit for the tests running in CI, the tests results '
|
||||
"won't be consistent with CIs when this flag is set. flutter "
|
||||
'command should be set in the PATH for this flag to be useful.'
|
||||
'This flag can also be used to test local Flutter changes.')
|
||||
..addFlag(
|
||||
'require-skia-gold',
|
||||
help:
|
||||
@ -74,6 +74,10 @@ class TestCommand extends Command<bool> with ArgUtils<bool> {
|
||||
'tests. If omitted, the test runner uses the default CanvasKit '
|
||||
'build.',
|
||||
)
|
||||
..addFlag(
|
||||
'wasm',
|
||||
help: 'Whether the test we are running are compiled to webassembly.'
|
||||
)
|
||||
..addFlag(
|
||||
'use-local-canvaskit',
|
||||
help: 'Optional. Whether or not to use the locally built version of '
|
||||
@ -91,6 +95,8 @@ class TestCommand extends Command<bool> with ArgUtils<bool> {
|
||||
|
||||
bool get failEarly => boolArg('fail-early');
|
||||
|
||||
bool get isWasm => boolArg('wasm');
|
||||
|
||||
/// Whether to start the browser in debug mode.
|
||||
///
|
||||
/// In this mode the browser pauses before running the test to allow
|
||||
@ -131,11 +137,12 @@ class TestCommand extends Command<bool> with ArgUtils<bool> {
|
||||
|
||||
final Pipeline testPipeline = Pipeline(steps: <PipelineStep>[
|
||||
if (isWatchMode) ClearTerminalScreenStep(),
|
||||
CompileTestsStep(testFiles: testFiles, useLocalCanvasKit: useLocalCanvasKit),
|
||||
CompileTestsStep(testFiles: testFiles, useLocalCanvasKit: useLocalCanvasKit, isWasm: isWasm),
|
||||
RunTestsStep(
|
||||
browserName: browserName,
|
||||
testFiles: testFiles,
|
||||
isDebug: isDebug,
|
||||
isWasm: isWasm,
|
||||
doUpdateScreenshotGoldens: doUpdateScreenshotGoldens,
|
||||
requireSkiaGold: requireSkiaGold,
|
||||
overridePathToCanvasKit: overridePathToCanvasKit,
|
||||
|
||||
@ -700,6 +700,14 @@ extension DomCanvasRenderingContext2DExtension on DomCanvasRenderingContext2D {
|
||||
external set globalAlpha(num? value);
|
||||
}
|
||||
|
||||
@JS()
|
||||
@staticInterop
|
||||
class DomCanvasRenderingContextWebGl {}
|
||||
|
||||
extension DomCanvasRenderingContextWebGlExtension on DomCanvasRenderingContextWebGl {
|
||||
external bool isContextLost();
|
||||
}
|
||||
|
||||
@JS()
|
||||
@staticInterop
|
||||
class DomImageData {}
|
||||
@ -771,7 +779,7 @@ Future<DomXMLHttpRequest> domHttpRequest(String url,
|
||||
}
|
||||
}));
|
||||
|
||||
xhr.addEventListener('error', allowInterop(completer.completeError));
|
||||
xhr.addEventListener('error', allowInterop((DomEvent event) => completer.completeError(event)));
|
||||
xhr.send(sendData);
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ environment:
|
||||
|
||||
dependencies:
|
||||
js: 0.6.4
|
||||
meta: 1.3.0
|
||||
meta: ^1.7.0
|
||||
|
||||
dev_dependencies:
|
||||
archive: 3.1.2
|
||||
@ -27,7 +27,7 @@ dev_dependencies:
|
||||
shelf_web_socket: any
|
||||
stack_trace: any
|
||||
stream_channel: any
|
||||
test: 1.17.7
|
||||
test: 1.22.0
|
||||
test_api: any
|
||||
test_core: any
|
||||
typed_data: any
|
||||
|
||||
@ -41,9 +41,9 @@ void _profilerTests() {
|
||||
|
||||
test('can listen to benchmarks', () {
|
||||
final List<BenchmarkDatapoint> data = <BenchmarkDatapoint>[];
|
||||
jsOnBenchmark((String name, num value) {
|
||||
jsOnBenchmark(allowInterop((String name, num value) {
|
||||
data.add(BenchmarkDatapoint(name, value));
|
||||
});
|
||||
}));
|
||||
|
||||
Profiler.instance.benchmark('foo', 123);
|
||||
expect(data, <BenchmarkDatapoint>[BenchmarkDatapoint('foo', 123)]);
|
||||
@ -64,9 +64,9 @@ void _profilerTests() {
|
||||
final List<BenchmarkDatapoint> data = <BenchmarkDatapoint>[];
|
||||
|
||||
// Wrong callback signature.
|
||||
jsOnBenchmark((num value) {
|
||||
jsOnBenchmark(allowInterop((num value) {
|
||||
data.add(BenchmarkDatapoint('bad', value));
|
||||
});
|
||||
}));
|
||||
expect(
|
||||
() => Profiler.instance.benchmark('foo', 123),
|
||||
throwsA(isA<NoSuchMethodError>()),
|
||||
@ -160,8 +160,6 @@ void jsOnBenchmark(dynamic listener) {
|
||||
js_util.setProperty(
|
||||
domWindow,
|
||||
'_flutter_internal_on_benchmark',
|
||||
listener is Function
|
||||
? allowInterop(listener)
|
||||
: listener,
|
||||
listener
|
||||
);
|
||||
}
|
||||
|
||||
@ -437,7 +437,7 @@ Future<void> testMain() async {
|
||||
test('dispatches browser event on flutter/service_worker channel', () async {
|
||||
final Completer<void> completer = Completer<void>();
|
||||
domWindow.addEventListener('flutter-first-frame',
|
||||
allowInterop(completer.complete));
|
||||
allowInterop((DomEvent e) => completer.complete()));
|
||||
final Zone innerZone = Zone.current.fork();
|
||||
|
||||
innerZone.runGuarded(() {
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
|
||||
import 'dart:math' as math;
|
||||
import 'dart:typed_data';
|
||||
import 'dart:web_gl';
|
||||
|
||||
import 'package:test/bootstrap/browser.dart';
|
||||
import 'package:test/test.dart';
|
||||
@ -362,8 +361,8 @@ Future<void> testMain() async {
|
||||
() async {
|
||||
final DomCanvasElement sideCanvas =
|
||||
createDomCanvasElement(width: 5, height: 5);
|
||||
final RenderingContext? context =
|
||||
sideCanvas.getContext('webgl') as RenderingContext?;
|
||||
final DomCanvasRenderingContextWebGl? context =
|
||||
sideCanvas.getContext('webgl') as DomCanvasRenderingContextWebGl?;
|
||||
expect(context, isNotNull);
|
||||
|
||||
final EngineCanvas engineCanvas =
|
||||
|
||||
@ -10,4 +10,4 @@ dependencies:
|
||||
|
||||
dev_dependencies:
|
||||
analyzer: 4.3.1
|
||||
test: 1.21.4
|
||||
test: 1.22.0
|
||||
|
||||
@ -7,7 +7,7 @@ environment:
|
||||
dependencies:
|
||||
js: 0.6.4
|
||||
stream_channel: 2.1.0
|
||||
test: 1.17.7
|
||||
test: 1.22.0
|
||||
webkit_inspection_protocol: 1.0.0
|
||||
stack_trace: 1.10.0
|
||||
ui:
|
||||
|
||||
@ -9,7 +9,7 @@ dependencies:
|
||||
crypto: 3.0.1
|
||||
image: 3.0.1
|
||||
js: 0.6.4
|
||||
meta: 1.3.0
|
||||
meta: ^1.7.0
|
||||
path: 1.8.0
|
||||
process: 4.2.3
|
||||
skia_gold_client:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user