From 6719207be7861beb42b781bfc8011aa4a2fd115a Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum <nweiz@google.com> Date: Thu, 6 Oct 2016 14:33:16 -0700 Subject: [PATCH] Add a JS API to restart the current test. --- lib/src/runner/browser/browser_manager.dart | 28 ++++++++++++++++----- lib/src/runner/browser/static/host.dart | 8 +++++- lib/src/runner/browser/static/host.dart.js | 9 ++++++- lib/src/runner/debugger.dart | 9 +++++++ lib/src/runner/environment.dart | 8 ++++++ lib/src/runner/plugin/environment.dart | 3 +++ 6 files changed, 57 insertions(+), 8 deletions(-) diff --git a/lib/src/runner/browser/browser_manager.dart b/lib/src/runner/browser/browser_manager.dart index e8172bfa..66acacc9 100644 --- a/lib/src/runner/browser/browser_manager.dart +++ b/lib/src/runner/browser/browser_manager.dart @@ -68,6 +68,9 @@ class BrowserManager { /// screen. CancelableCompleter _pauseCompleter; + /// The controller for [_BrowserEnvironment.onRestart]. + final _onRestartController = new StreamController(); + /// The environment to attach to each suite. Future<_BrowserEnvironment> _environment; @@ -179,7 +182,7 @@ class BrowserManager { /// Loads [_BrowserEnvironment]. Future<_BrowserEnvironment> _loadBrowserEnvironment() async { return new _BrowserEnvironment(this, await _browser.observatoryUrl, - await _browser.remoteDebuggerUrl); + await _browser.remoteDebuggerUrl, _onRestartController.stream); } /// Tells the browser the load a test suite from the URL [url]. @@ -259,11 +262,22 @@ class BrowserManager { /// The callback for handling messages received from the host page. void _onMessage(Map message) { - if (message["command"] == "ping") return; + switch (message["command"]) { + case "ping": break; + + case "restart": + _onRestartController.add(null); + break; + + case "resume": + if (_pauseCompleter != null) _pauseCompleter.complete(); + break; - assert(message["command"] == "resume"); - if (_pauseCompleter == null) return; - _pauseCompleter.complete(); + default: + // Unreachable. + assert(false); + break; + } } /// Closes the manager and releases any resources it owns, including closing @@ -291,8 +305,10 @@ class _BrowserEnvironment implements Environment { final Uri remoteDebuggerUrl; + final Stream onRestart; + _BrowserEnvironment(this._manager, this.observatoryUrl, - this.remoteDebuggerUrl); + this.remoteDebuggerUrl, this.onRestart); CancelableOperation displayPause() => _manager._displayPause(); } diff --git a/lib/src/runner/browser/static/host.dart b/lib/src/runner/browser/static/host.dart index c802b04a..675c70dd 100644 --- a/lib/src/runner/browser/static/host.dart +++ b/lib/src/runner/browser/static/host.dart @@ -31,7 +31,11 @@ class _JSApi { /// the "play" button. external Function get resume; - external factory _JSApi({void resume()}); + /// Causes the test runner to restart the current test once it finishes + /// running. + external Function get restartCurrent; + + external factory _JSApi({void resume(), void restartCurrent()}); } /// Sets the top-level `dartTest` object so that it's visible to JS. @@ -135,6 +139,8 @@ void main() { _jsApi = new _JSApi(resume: allowInterop(() { if (!document.body.classes.remove('paused')) return; serverChannel.sink.add({"command": "resume"}); + }), restartCurrent: allowInterop(() { + serverChannel.sink.add({"command": "restart"}); })); }, onError: (error, stackTrace) { print("$error\n${new Trace.from(stackTrace).terse}"); diff --git a/lib/src/runner/browser/static/host.dart.js b/lib/src/runner/browser/static/host.dart.js index ab52b3ca..7aafafa3 100644 --- a/lib/src/runner/browser/static/host.dart.js +++ b/lib/src/runner/browser/static/host.dart.js @@ -15448,7 +15448,8 @@ P.Timer_Timer$periodic(P.Duration$(0, 0, 0, 0, 0, 1), new M.main__closure0(serverChannel)); t1 = J.get$onClick$x(document.querySelector("#play")); new W._EventStreamSubscription(0, t1._html$_target, t1._eventType, W._wrapZone(new M.main__closure1(serverChannel)), false, [H.getTypeArgumentByIndex(t1, 0)])._tryResume$0(); - t1 = {resume: P.allowInterop(new M.main__closure2(serverChannel))}; + t1 = P.allowInterop(new M.main__closure2(serverChannel)); + t1 = {restartCurrent: P.allowInterop(new M.main__closure3(serverChannel)), resume: t1}; self.dartTest = t1; }, null, null, 0, 0, null, "call"] }, @@ -15502,6 +15503,12 @@ this.serverChannel._mainController._foreign._sink.add$1(0, P.LinkedHashMap__makeLiteral(["command", "resume"])); }, null, null, 0, 0, null, "call"] }, + main__closure3: { + "^": "Closure:1;serverChannel", + call$0: [function() { + this.serverChannel._mainController._foreign._sink.add$1(0, P.LinkedHashMap__makeLiteral(["command", "restart"])); + }, null, null, 0, 0, null, "call"] + }, main_closure0: { "^": "Closure:3;", call$2: [function(error, stackTrace) { diff --git a/lib/src/runner/debugger.dart b/lib/src/runner/debugger.dart index 305ff7fc..1b9fb634 100644 --- a/lib/src/runner/debugger.dart +++ b/lib/src/runner/debugger.dart @@ -73,6 +73,9 @@ class _Debugger { /// The subscription to [_suite.onDebugging]. StreamSubscription<bool> _onDebuggingSubscription; + /// The subscription to [_suite.environment.onRestart]. + StreamSubscription _onRestartSubscription; + /// Whether [close] has been called. bool _closed = false; @@ -91,6 +94,10 @@ class _Debugger { _onNotDebugging(); } }); + + _onRestartSubscription = _suite.environment.onRestart.listen((_) { + _restartTest(); + }); } /// Runs the debugger. @@ -189,6 +196,7 @@ class _Debugger { /// Restarts the current test. void _restartTest() { + if (_engine.active.isEmpty) return; var liveTest = _engine.active.single; _engine.restartTest(liveTest); if (!_json) { @@ -201,6 +209,7 @@ class _Debugger { void close() { _closed = true; _onDebuggingSubscription.cancel(); + _onRestartSubscription.cancel(); _console.stop(); } } diff --git a/lib/src/runner/environment.dart b/lib/src/runner/environment.dart index 694f9382..bd8afd15 100644 --- a/lib/src/runner/environment.dart +++ b/lib/src/runner/environment.dart @@ -2,6 +2,8 @@ // 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:async/async.dart'; /// The abstract class of environments in which test suites are @@ -18,6 +20,12 @@ abstract class Environment { /// enabled. Uri get remoteDebuggerUrl; + /// Emits a `null` event whenever the user tells the environment to restart + /// the current test once it's finished. + /// + /// Never emits an error, and never closes. + Stream get onRestart; + /// Displays information indicating that the test runner is paused. /// /// The returned operation will complete when the user takes action within the diff --git a/lib/src/runner/plugin/environment.dart b/lib/src/runner/plugin/environment.dart index 90d5b3e8..417f8e5d 100644 --- a/lib/src/runner/plugin/environment.dart +++ b/lib/src/runner/plugin/environment.dart @@ -2,6 +2,8 @@ // 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:async/async.dart'; import '../environment.dart'; @@ -9,6 +11,7 @@ import '../environment.dart'; /// The default environment for platform plugins. class PluginEnvironment implements Environment { final supportsDebugging = false; + Stream get onRestart => new StreamController().stream; const PluginEnvironment(); -- GitLab