-
Notifications
You must be signed in to change notification settings - Fork 220
Promote dart2wasm compiler support to the stable browser platform. #2144
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
decc715
Integrate dart2wasm compiler support into the stable chrome platform.
jakemac53 a755d06
fix integration test, other cleanup
jakemac53 ead2b90
additional cleanup
jakemac53 f3f64a3
Merge branch 'master' into release-wasm
jakemac53 1751e1d
fix some tests
jakemac53 e261662
fix runner tests
jakemac53 80ef6c9
drop Runtime.isJS/isWasm, drop precompiled compiler option
jakemac53 3bd1758
update precompiled test expectations
jakemac53 99e6fd8
Maintain backwards compatibility with the wire format
jakemac53 ed13e92
remove all vestiges of --pub-serve
jakemac53 b7d36f9
update pubspecs/changelogs with proposed versions, restore 'wasm' tag
jakemac53 9eedb5a
make test_core a breaking change also since it re-exports Runtime, an…
jakemac53 1ccf2bc
override matcher version in test_api so we can get a version solve
jakemac53 e2d43ff
code review updates
jakemac53 c42ba16
style tweak
jakemac53 18523cd
make CompilerSupper an abstract interface
jakemac53 cc31cf6
declare dart2wasm support for firefox as well
jakemac53 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
platforms: [experimental-chrome-wasm] | ||
platforms: [chrome, firefox] | ||
compilers: [dart2wasm] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
37 changes: 37 additions & 0 deletions
37
pkgs/test/lib/src/runner/browser/compilers/compiler_support.dart
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file | ||
// for details. All rights reserved. Use of this source code is governed by a | ||
// BSD-style license that can be found in the LICENSE file. | ||
|
||
import 'dart:async'; | ||
|
||
import 'package:test_api/backend.dart' show StackTraceMapper, SuitePlatform; | ||
import 'package:test_core/src/runner/suite.dart'; // ignore: implementation_imports | ||
import 'package:web_socket_channel/web_socket_channel.dart'; // ignore: implementation_imports | ||
|
||
/// The shared interface for all compiler support libraries. | ||
abstract interface class CompilerSupport { | ||
/// The URL at which this compiler serves its tests. | ||
/// | ||
/// Each compiler serves its tests under a different directory. | ||
Uri get serverUrl; | ||
|
||
/// Compiles [dartPath] using [suiteConfig] for [platform]. | ||
/// | ||
/// [dartPath] is the path to the original `.dart` test suite, relative to the | ||
/// package root. | ||
Future<void> compileSuite( | ||
String dartPath, SuiteConfiguration suiteConfig, SuitePlatform platform); | ||
|
||
/// Retrieves a stack trace mapper for [dartPath] if available. | ||
jakemac53 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// | ||
/// [dartPath] is the path to the original `.dart` test suite, relative to the | ||
/// package root. | ||
StackTraceMapper? stackTraceMapperForPath(String dartPath); | ||
|
||
/// Returns the eventual URI for the web socket, as well as the channel itself | ||
/// once the connection is established. | ||
(Uri uri, Future<WebSocketChannel> socket) get webSocket; | ||
|
||
/// Closes down anything necessary for this implementation. | ||
Future<void> close(); | ||
} |
208 changes: 208 additions & 0 deletions
208
pkgs/test/lib/src/runner/browser/compilers/dart2js.dart
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,208 @@ | ||
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file | ||
// for details. All rights reserved. Use of this source code is governed by a | ||
// BSD-style license that can be found in the LICENSE file. | ||
|
||
import 'dart:async'; | ||
import 'dart:convert'; | ||
import 'dart:io'; | ||
|
||
import 'package:http_multi_server/http_multi_server.dart'; | ||
import 'package:path/path.dart' as p; | ||
import 'package:shelf/shelf.dart' as shelf; | ||
import 'package:shelf/shelf_io.dart' as shelf_io; | ||
import 'package:shelf_packages_handler/shelf_packages_handler.dart'; | ||
import 'package:shelf_static/shelf_static.dart'; | ||
import 'package:shelf_web_socket/shelf_web_socket.dart'; | ||
import 'package:test_api/backend.dart' show StackTraceMapper, SuitePlatform; | ||
import 'package:test_core/src/runner/configuration.dart'; // ignore: implementation_imports | ||
import 'package:test_core/src/runner/dart2js_compiler_pool.dart'; // ignore: implementation_imports | ||
import 'package:test_core/src/runner/package_version.dart'; // ignore: implementation_imports | ||
import 'package:test_core/src/runner/suite.dart'; // ignore: implementation_imports | ||
import 'package:test_core/src/util/io.dart'; // ignore: implementation_imports | ||
import 'package:test_core/src/util/package_config.dart'; // ignore: implementation_imports | ||
import 'package:test_core/src/util/stack_trace_mapper.dart'; // ignore: implementation_imports | ||
import 'package:web_socket_channel/web_socket_channel.dart'; | ||
|
||
import '../../../util/math.dart'; | ||
import '../../../util/one_off_handler.dart'; | ||
import '../../../util/package_map.dart'; | ||
import '../../../util/path_handler.dart'; | ||
import 'compiler_support.dart'; | ||
|
||
/// Support for Dart2Js compiled tests. | ||
class Dart2JsSupport implements CompilerSupport { | ||
/// Whether [close] has been called. | ||
bool _closed = false; | ||
|
||
/// The temporary directory in which compiled JS is emitted. | ||
final _compiledDir = createTempDir(); | ||
|
||
/// A map from test suite paths to Futures that will complete once those | ||
/// suites are finished compiling. | ||
/// | ||
/// This is used to make sure that a given test suite is only compiled once | ||
/// per run, rather than once per browser per run. | ||
final _compileFutures = <String, Future<void>>{}; | ||
|
||
/// The [Dart2JsCompilerPool] managing active instances of `dart2js`. | ||
final _compilerPool = Dart2JsCompilerPool(); | ||
|
||
/// The global test runner configuration. | ||
final Configuration _config; | ||
|
||
/// The default template path. | ||
final String _defaultTemplatePath; | ||
|
||
/// Mappers for Dartifying stack traces, indexed by test path. | ||
final _mappers = <String, StackTraceMapper>{}; | ||
|
||
/// A [PathHandler] used to serve test specific artifacts. | ||
final _pathHandler = PathHandler(); | ||
|
||
/// The root directory served statically by this server. | ||
final String _root; | ||
|
||
/// Each compiler serves its tests under a different randomly-generated | ||
/// secret URI to ensure that other users on the same system can't snoop | ||
/// on data being served through this server, as well as distinguish tests | ||
/// from different compilers from each other. | ||
final String _secret = randomUrlSecret(); | ||
|
||
/// The underlying server. | ||
final shelf.Server _server; | ||
|
||
/// A [OneOffHandler] for servicing WebSocket connections for | ||
/// [BrowserManager]s. | ||
/// | ||
/// This is one-off because each [BrowserManager] can only connect to a single | ||
/// WebSocket. | ||
final _webSocketHandler = OneOffHandler(); | ||
|
||
@override | ||
Uri get serverUrl => _server.url.resolve('$_secret/'); | ||
|
||
Dart2JsSupport._(this._config, this._defaultTemplatePath, this._server, | ||
this._root, String faviconPath) { | ||
var cascade = shelf.Cascade() | ||
.add(_webSocketHandler.handler) | ||
.add(packagesDirHandler()) | ||
.add(_pathHandler.handler) | ||
.add(createStaticHandler(_root)) | ||
.add(_wrapperHandler); | ||
|
||
var pipeline = const shelf.Pipeline() | ||
.addMiddleware(PathHandler.nestedIn(_secret)) | ||
.addHandler(cascade.handler); | ||
|
||
_server.mount(shelf.Cascade() | ||
.add(createFileHandler(faviconPath)) | ||
.add(pipeline) | ||
.handler); | ||
} | ||
|
||
static Future<Dart2JsSupport> start({ | ||
required Configuration config, | ||
required String defaultTemplatePath, | ||
required String root, | ||
required String faviconPath, | ||
}) async { | ||
var server = shelf_io.IOServer(await HttpMultiServer.loopback(0)); | ||
return Dart2JsSupport._( | ||
config, defaultTemplatePath, server, root, faviconPath); | ||
} | ||
|
||
/// A handler that serves wrapper files used to bootstrap tests. | ||
shelf.Response _wrapperHandler(shelf.Request request) { | ||
var path = p.fromUri(request.url); | ||
|
||
if (path.endsWith('.html')) { | ||
var test = p.setExtension(path, '.dart'); | ||
var scriptBase = htmlEscape.convert(p.basename(test)); | ||
var link = '<link rel="x-dart-test" href="$scriptBase">'; | ||
var testName = htmlEscape.convert(test); | ||
var template = _config.customHtmlTemplatePath ?? _defaultTemplatePath; | ||
var contents = File(template).readAsStringSync(); | ||
var processedContents = contents | ||
// Checked during loading phase that there is only one {{testScript}} placeholder. | ||
.replaceFirst('{{testScript}}', link) | ||
.replaceAll('{{testName}}', testName); | ||
return shelf.Response.ok(processedContents, | ||
headers: {'Content-Type': 'text/html'}); | ||
} | ||
|
||
return shelf.Response.notFound('Not found.'); | ||
} | ||
|
||
@override | ||
Future<void> compileSuite( | ||
String dartPath, SuiteConfiguration suiteConfig, SuitePlatform platform) { | ||
return _compileFutures.putIfAbsent(dartPath, () async { | ||
var dir = Directory(_compiledDir).createTempSync('test_').path; | ||
var jsPath = p.join(dir, '${p.basename(dartPath)}.browser_test.dart.js'); | ||
var bootstrapContent = ''' | ||
${suiteConfig.metadata.languageVersionComment ?? await rootPackageLanguageVersionComment} | ||
import "package:test/src/bootstrap/browser.dart"; | ||
|
||
import "${p.toUri(p.absolute(dartPath))}" as test; | ||
|
||
void main() { | ||
internalBootstrapBrowserTest(() => test.main); | ||
} | ||
'''; | ||
|
||
await _compilerPool.compile(bootstrapContent, jsPath, suiteConfig); | ||
if (_closed) return; | ||
|
||
var bootstrapUrl = '${p.toUri(p.relative(dartPath, from: _root)).path}' | ||
'.browser_test.dart'; | ||
_pathHandler.add(bootstrapUrl, (request) { | ||
return shelf.Response.ok(bootstrapContent, | ||
headers: {'Content-Type': 'application/dart'}); | ||
}); | ||
|
||
var jsUrl = '${p.toUri(p.relative(dartPath, from: _root)).path}' | ||
'.browser_test.dart.js'; | ||
_pathHandler.add(jsUrl, (request) { | ||
return shelf.Response.ok(File(jsPath).readAsStringSync(), | ||
headers: {'Content-Type': 'application/javascript'}); | ||
}); | ||
|
||
var mapUrl = '${p.toUri(p.relative(dartPath, from: _root)).path}' | ||
'.browser_test.dart.js.map'; | ||
_pathHandler.add(mapUrl, (request) { | ||
return shelf.Response.ok(File('$jsPath.map').readAsStringSync(), | ||
headers: {'Content-Type': 'application/json'}); | ||
}); | ||
|
||
if (suiteConfig.jsTrace) return; | ||
var mapPath = '$jsPath.map'; | ||
_mappers[dartPath] = JSStackTraceMapper(File(mapPath).readAsStringSync(), | ||
mapUrl: p.toUri(mapPath), | ||
sdkRoot: Uri.parse('org-dartlang-sdk:///sdk'), | ||
packageMap: (await currentPackageConfig).toPackageMap()); | ||
}); | ||
} | ||
|
||
@override | ||
Future<void> close() async { | ||
if (_closed) return; | ||
_closed = true; | ||
await Future.wait([ | ||
Directory(_compiledDir).deleteWithRetry(), | ||
_compilerPool.close(), | ||
_server.close(), | ||
]); | ||
} | ||
|
||
@override | ||
StackTraceMapper? stackTraceMapperForPath(String dartPath) => | ||
_mappers[dartPath]; | ||
|
||
@override | ||
(Uri, Future<WebSocketChannel>) get webSocket { | ||
var completer = Completer<WebSocketChannel>.sync(); | ||
var path = _webSocketHandler.create(webSocketHandler(completer.complete)); | ||
var webSocketUrl = serverUrl.replace(scheme: 'ws').resolve(path); | ||
return (webSocketUrl, completer.future); | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.