Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(479)

Unified Diff: lib/src/runner/browser/server.dart

Issue 979513002: pkg/unittest: Add a server for serving test assets to browsers. (Closed) Base URL: git@github.com:dart-lang/unittest@master
Patch Set: Fix libDir. Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | lib/src/runner/browser/static/host.dart » ('j') | lib/src/runner/loader.dart » ('J')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/src/runner/browser/server.dart
diff --git a/lib/src/runner/browser/server.dart b/lib/src/runner/browser/server.dart
new file mode 100644
index 0000000000000000000000000000000000000000..5fdae5ed35b733b50e77b14942e7dcdab5f6d1ac
--- /dev/null
+++ b/lib/src/runner/browser/server.dart
@@ -0,0 +1,160 @@
+// Copyright (c) 2015, 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.
+
+library unittest.runner.browser.server;
+
+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:shelf/shelf_io.dart' as shelf_io;
+import 'package:shelf_static/shelf_static.dart';
+import 'package:shelf_web_socket/shelf_web_socket.dart';
+
+import '../../backend/suite.dart';
+import '../../util/io.dart';
+import '../../util/one_off_handler.dart';
+import 'browser_manager.dart';
+import 'compiler_pool.dart';
+import 'chrome.dart';
+
+/// A server that serves JS-compiled tests to browsers.
+///
+/// A test suite may be loaded for a given file using [loadSuite].
+class BrowserServer {
+ /// Starts the server.
+ ///
+ /// If [packageRoot] is passed, it's used for all package imports when
+ /// compiling tests to JS. Otherwise, the package root is inferred from the
+ /// location of the source file.
+ static Future<BrowserServer> start({String packageRoot}) {
+ var server = new BrowserServer._(packageRoot);
+ return server._load().then((_) => server);
+ }
+
+ /// The underlying HTTP server.
+ HttpServer _server;
+
+ /// The URL for this server.
+ Uri get url => baseUrlForAddress(_server.address, _server.port);
+
+ /// 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 = new OneOffHandler();
+
+ /// The [CompilerPool] managing active instances of `dart2js`.
+ final _compilers = new CompilerPool();
+
+ /// The temporary directory in which compiled JS is emitted.
+ final String _compiledDir;
+
+ /// The package root which is passed to `dart2js`.
+ final String _packageRoot;
+
+ /// The browser in which test suites are loaded and run.
+ ///
+ /// This is `null` until a suite is loaded.
+ Chrome _browser;
+
+ /// A future that will complete to the [BrowserManager] for [_browser].
+ ///
+ /// The first time this is called, it will start both the browser and the
+ /// browser manager. Any further calls will return the existing manager.
+ Future<BrowserManager> get _browserManager {
+ if (_browserManagerCompleter == null) {
+ _browserManagerCompleter = new Completer();
+ var path = _webSocketHandler.create(webSocketHandler((webSocket) {
+ _browserManagerCompleter.complete(new BrowserManager(webSocket));
+ }));
+
+ var webSocketUrl = url.replace(scheme: 'ws', path: '/$path');
+ _browser = new Chrome(url.replace(queryParameters: {
+ 'managerUrl': webSocketUrl.toString()
+ }));
+
+ // TODO(nweiz): Gracefully handle the browser being killed before the
+ // tests complete.
+ _browser.onExit.catchError((error, stackTrace) {
+ if (_browserManagerCompleter.isCompleted) return;
+ _browserManagerCompleter.completeError(error, stackTrace);
+ });
+ }
+ return _browserManagerCompleter.future;
+ }
+ Completer<BrowserManager> _browserManagerCompleter;
+
+ BrowserServer._(this._packageRoot)
+ : _compiledDir = Directory.systemTemp.createTempSync('unittest_').path;
+
+ /// Starts the underlying server.
+ Future _load() {
+ var staticPath = p.join(libDir(packageRoot: _packageRoot),
+ 'src/runner/browser/static');
+ var cascade = new shelf.Cascade()
+ .add(_webSocketHandler.handler)
+ .add(createStaticHandler(staticPath, defaultDocument: 'index.html'))
+ .add(createStaticHandler(_compiledDir, defaultDocument: 'index.html'));
+
+ return shelf_io.serve(cascade.handler, 'localhost', 0).then((server) {
+ _server = server;
+ });
+ }
+
+ /// Loads the test suite at [path].
+ ///
+ /// This will start a browser to load the suite if one isn't already running.
+ Future<Suite> loadSuite(String path) {
+ return _compileSuite(path).then((dir) {
+ // TODO(nweiz): Don't start the browser until all the suites are compiled.
+ return _browserManager.then((browserManager) {
+ // Add a trailing slash because at least on Chrome, the iframe's
+ // window.location.href will do so automatically, and if that differs
+ // from the original URL communication will fail.
+ var suiteUrl = url.resolve(
+ "/" + p.toUri(p.relative(dir, from: _compiledDir)).path + "/");
+ return browserManager.loadSuite(path, suiteUrl);
+ });
+ });
+ }
+
+ /// Compile the test suite at [dartPath] to JavaScript.
+ ///
+ /// Returns a [Future] that completes to the path to the JavaScript.
+ Future<String> _compileSuite(String dartPath) {
+ var dir = new Directory(_compiledDir).createTempSync('test_').path;
+ var jsPath = p.join(dir, p.basename(dartPath) + ".js");
+ return _compilers.compile(dartPath, jsPath,
+ packageRoot: packageRootFor(dartPath, _packageRoot))
+ .then((_) {
+ // TODO(nweiz): support user-authored HTML files.
+ new File(p.join(dir, "index.html")).writeAsStringSync('''
+<!DOCTYPE html>
+<html>
+<head>
+ <title>${HTML_ESCAPE.convert(dartPath)} Test</title>
+ <script src="${HTML_ESCAPE.convert(p.basename(jsPath))}"></script>
+</head>
+</html>
+''');
+ return dir;
+ });
+ }
+
+ /// Closes the server and releases all its resources.
+ ///
+ /// Returns a [Future] that completes once the server is closed and its
+ /// resources have been fully released.
+ Future close() {
+ new Directory(_compiledDir).deleteSync(recursive: true);
+ return _server.close().then((_) {
+ if (_browserManagerCompleter == null) return null;
+ return _browserManager.then((_) => _browser.close());
+ });
+ }
+}
« no previous file with comments | « no previous file | lib/src/runner/browser/static/host.dart » ('j') | lib/src/runner/loader.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698