Skip to content

Commit

Permalink
fix a bug where test html files were not created in precompiled mode (#…
Browse files Browse the repository at this point in the history
…2170)

* fix a bug where test html files were not created in precompiled mode

* support wasm and js precompiled tests
  • Loading branch information
jakemac53 authored Jan 10, 2024
1 parent 0eddae4 commit c709cde
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 80 deletions.
5 changes: 4 additions & 1 deletion pkgs/test/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
## 1.25.1-wip
## 1.25.1

* Fix a bug where in precompiled mode, html files for tests were no longer
created.

* Document the silent reporter in CLI help output.

Expand Down
70 changes: 69 additions & 1 deletion pkgs/test/lib/src/runner/browser/compilers/compiler_support.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,26 @@
// BSD-style license that can be found in the LICENSE file.

import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:path/path.dart' as p;
import 'package:shelf/shelf.dart' as shelf;
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/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 {
abstract class CompilerSupport {
/// The global test runner configuration.
final Configuration config;

/// The default template path.
final String defaultTemplatePath;

CompilerSupport(this.config, this.defaultTemplatePath);

/// The URL at which this compiler serves its tests.
///
/// Each compiler serves its tests under a different directory.
Expand All @@ -34,4 +47,59 @@ abstract interface class CompilerSupport {

/// Closes down anything necessary for this implementation.
Future<void> close();

/// A handler that serves html wrapper files used to bootstrap tests.
shelf.Response htmlWrapperHandler(shelf.Request request);
}

mixin JsHtmlWrapper on CompilerSupport {
@override
shelf.Response htmlWrapperHandler(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.');
}
}

mixin WasmHtmlWrapper on CompilerSupport {
@override
shelf.Response htmlWrapperHandler(shelf.Request request) {
var path = p.fromUri(request.url);

if (path.endsWith('.html')) {
var test = '${p.withoutExtension(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 jsRuntime = p.basename('$test.browser_test.dart.mjs');
var wasmData = '<data id="WasmBootstrapInfo" '
'data-wasmurl="${p.basename('$test.browser_test.dart.wasm')}" '
'data-jsruntimeurl="$jsRuntime"></data>';
var processedContents = contents
// Checked during loading phase that there is only one {{testScript}} placeholder.
.replaceFirst('{{testScript}}', '$link\n$wasmData')
.replaceAll('{{testName}}', testName);
return shelf.Response.ok(processedContents,
headers: {'Content-Type': 'text/html'});
}

return shelf.Response.notFound('Not found.');
}
}
35 changes: 3 additions & 32 deletions pkgs/test/lib/src/runner/browser/compilers/dart2js.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
// 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';
Expand All @@ -30,7 +29,7 @@ import '../../../util/path_handler.dart';
import 'compiler_support.dart';

/// Support for Dart2Js compiled tests.
class Dart2JsSupport implements CompilerSupport {
class Dart2JsSupport extends CompilerSupport with JsHtmlWrapper {
/// Whether [close] has been called.
bool _closed = false;

Expand All @@ -47,12 +46,6 @@ class Dart2JsSupport implements CompilerSupport {
/// 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>{};

Expand Down Expand Up @@ -81,14 +74,14 @@ class Dart2JsSupport implements CompilerSupport {
@override
Uri get serverUrl => _server.url.resolve('$_secret/');

Dart2JsSupport._(this._config, this._defaultTemplatePath, this._server,
Dart2JsSupport._(super.config, super.defaultTemplatePath, this._server,
this._root, String faviconPath) {
var cascade = shelf.Cascade()
.add(_webSocketHandler.handler)
.add(packagesDirHandler())
.add(_pathHandler.handler)
.add(createStaticHandler(_root))
.add(_wrapperHandler);
.add(htmlWrapperHandler);

var pipeline = const shelf.Pipeline()
.addMiddleware(PathHandler.nestedIn(_secret))
Expand All @@ -111,28 +104,6 @@ class Dart2JsSupport implements CompilerSupport {
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) {
Expand Down
39 changes: 3 additions & 36 deletions pkgs/test/lib/src/runner/browser/compilers/dart2wasm.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
// 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';
Expand All @@ -29,7 +28,7 @@ import '../browser_manager.dart';
import 'compiler_support.dart';

/// Support for Dart2Wasm compiled tests.
class Dart2WasmSupport implements CompilerSupport {
class Dart2WasmSupport extends CompilerSupport with WasmHtmlWrapper {
/// Whether [close] has been called.
bool _closed = false;

Expand All @@ -46,12 +45,6 @@ class Dart2WasmSupport implements CompilerSupport {
/// The [WasmCompilerPool] managing active instances of `dart2wasm`.
final _compilerPool = WasmCompilerPool();

/// The global test runner configuration.
final Configuration _config;

/// The default template path.
final String _defaultTemplatePath;

/// The `package:test` side wrapper for the Dart2Wasm runtime.
final String _jsRuntimeWrapper;

Expand Down Expand Up @@ -83,14 +76,14 @@ class Dart2WasmSupport implements CompilerSupport {
@override
Uri get serverUrl => _server.url.resolve('$_secret/');

Dart2WasmSupport._(this._config, this._defaultTemplatePath,
Dart2WasmSupport._(super.config, super.defaultTemplatePath,
this._jsRuntimeWrapper, this._server, this._root, String faviconPath) {
var cascade = shelf.Cascade()
.add(_webSocketHandler.handler)
.add(packagesDirHandler())
.add(_pathHandler.handler)
.add(createStaticHandler(_root))
.add(_wrapperHandler);
.add(htmlWrapperHandler);

var pipeline = const shelf.Pipeline()
.addMiddleware(PathHandler.nestedIn(_secret))
Expand All @@ -114,32 +107,6 @@ class Dart2WasmSupport implements CompilerSupport {
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.withoutExtension(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 jsRuntime = p.basename('$test.browser_test.dart.mjs');
var wasmData = '<data id="WasmBootstrapInfo" '
'data-wasmurl="${p.basename('$test.browser_test.dart.wasm')}" '
'data-jsruntimeurl="$jsRuntime"></data>';
var processedContents = contents
// Checked during loading phase that there is only one {{testScript}} placeholder.
.replaceFirst('{{testScript}}', '$link\n$wasmData')
.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) {
Expand Down
38 changes: 29 additions & 9 deletions pkgs/test/lib/src/runner/browser/compilers/precompiled.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ 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_api/backend.dart'
show Compiler, StackTraceMapper, SuitePlatform;
import 'package:test_core/src/runner/configuration.dart'; // ignore: implementation_imports
import 'package:test_core/src/runner/suite.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
Expand All @@ -24,17 +26,17 @@ import '../../../util/package_map.dart';
import '../../../util/path_handler.dart';
import 'compiler_support.dart';

class JsPrecompiledSupport = PrecompiledSupport with JsHtmlWrapper;
class WasmPrecompiledSupport = PrecompiledSupport with WasmHtmlWrapper;

/// Support for precompiled test files.
class PrecompiledSupport implements CompilerSupport {
abstract class PrecompiledSupport extends CompilerSupport {
/// Whether [close] has been called.
bool _closed = false;

/// 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 the server.
final String _root;

Expand All @@ -60,12 +62,16 @@ class PrecompiledSupport implements CompilerSupport {
@override
Uri get serverUrl => _server.url.resolve('$_secret/');

PrecompiledSupport._(this._server, this._root, String faviconPath) {
PrecompiledSupport._(super.config, super.defaultTemplatePath, this._server,
this._root, String faviconPath) {
var cascade = shelf.Cascade()
.add(_webSocketHandler.handler)
.add(createStaticHandler(_root, serveFilesOutsidePath: true))
// TODO: This packages dir handler should not be necessary?
.add(packagesDirHandler())
.add(_pathHandler.handler)
.add(createStaticHandler(_root));
// Even for precompiled tests, we will auto-create a bootstrap html file
// if none was present.
.add(htmlWrapperHandler);

var pipeline = const shelf.Pipeline()
.addMiddleware(PathHandler.nestedIn(_secret))
Expand All @@ -78,11 +84,25 @@ class PrecompiledSupport implements CompilerSupport {
}

static Future<PrecompiledSupport> start({
required Compiler compiler,
required Configuration config,
required String defaultTemplatePath,
required String root,
required String faviconPath,
}) async {
var server = shelf_io.IOServer(await HttpMultiServer.loopback(0));
return PrecompiledSupport._(server, root, faviconPath);

return switch (compiler) {
Compiler.dart2js => JsPrecompiledSupport._(
config, defaultTemplatePath, server, root, faviconPath),
Compiler.dart2wasm => WasmPrecompiledSupport._(
config, defaultTemplatePath, server, root, faviconPath),
Compiler.exe ||
Compiler.kernel ||
Compiler.source =>
throw UnsupportedError(
'The browser platform does not support $compiler'),
};
}

/// Compiles [dartPath] using [suiteConfig] for [platform].
Expand Down
3 changes: 3 additions & 0 deletions pkgs/test/lib/src/runner/browser/platform.dart
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ class BrowserPlatform extends PlatformPlugin
_compilerSupport.putIfAbsent(compiler, () {
if (_config.suiteDefaults.precompiledPath != null) {
return PrecompiledSupport.start(
compiler: compiler,
config: _config,
defaultTemplatePath: _defaultTemplatePath,
root: _config.suiteDefaults.precompiledPath!,
faviconPath: _faviconPath);
}
Expand Down
2 changes: 1 addition & 1 deletion pkgs/test/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: test
version: 1.25.1-wip
version: 1.25.1
description: >-
A full featured library for writing and running Dart tests across platforms.
repository: https://github.com/dart-lang/test/tree/master/pkgs/test
Expand Down

0 comments on commit c709cde

Please sign in to comment.