Wasm harness for unit tests. (flutter/engine#36255)

This commit is contained in:
Jackson Gardner 2022-11-02 09:11:12 -07:00 committed by GitHub
parent 880e443b79
commit 005dc4e774
18 changed files with 305 additions and 70 deletions

View File

@ -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.

View 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)

View File

@ -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:

View File

@ -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'));

View File

@ -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,

View File

@ -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(

View File

@ -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,
);
});

View 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);
}
}
};

View File

@ -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();

View File

@ -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,

View File

@ -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;
}

View File

@ -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

View File

@ -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
);
}

View File

@ -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(() {

View File

@ -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 =

View File

@ -10,4 +10,4 @@ dependencies:
dev_dependencies:
analyzer: 4.3.1
test: 1.21.4
test: 1.22.0

View File

@ -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:

View File

@ -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: