Index: lib/src/runner/debugger.dart |
diff --git a/lib/src/runner/debugger.dart b/lib/src/runner/debugger.dart |
index 31ce04123b0052c8690e0dd7a322b8a3eb8f2e01..efea0c8da61c83cd8ee514b5bf75e3a13a3f315c 100644 |
--- a/lib/src/runner/debugger.dart |
+++ b/lib/src/runner/debugger.dart |
@@ -1,39 +1,116 @@ |
-Future debug(Configuration config, Engine engine, Reporter reporter, |
- LoadSuite loadSuite) async { |
- // Make the underlying suite null so that the engine doesn't start running |
- // it immediately. |
- _engine.suiteSink.add(loadSuite.changeSuite((_) => null)); |
- |
- var suite = await loadSuite.suite; |
- if (suite == null) return; |
- |
- var debugger = new _Debugger(config, reporter, suite); |
- try { |
- await debugger._pause(); |
- if (_closed) return; |
- |
- debugger._listen(); |
- _engine.suiteSink.add(suite); |
- await _engine.onIdle.first; |
- } finally { |
- debugger._close(); |
- } |
+// Copyright (c) 2016, 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 test.runner.debugger; |
+ |
+import 'dart:async'; |
+ |
+import 'package:async/async.dart'; |
+ |
+import '../backend/live_test.dart'; |
+import '../backend/test_platform.dart'; |
+import '../util/io.dart'; |
+import '../utils.dart'; |
+import 'configuration.dart'; |
+import 'console.dart'; |
+import 'engine.dart'; |
+import 'load_suite.dart'; |
+import 'reporter.dart'; |
+import 'runner_suite.dart'; |
+ |
+/// Runs [loadSuite] in debugging mode. |
+/// |
+/// Runs the suite's tests using [engine]. The [reporter] should already be |
+/// watching [engine], and the [config] should contain the user configuration |
+/// for the test runner. |
+/// |
+/// Returns a [CancelableOperation] that will complete once the suite has |
+/// finished running. If the operation is canceled, the debugger will clean up |
+/// any resources it allocated. |
+CancelableOperation debug(Configuration config, Engine engine, |
+ Reporter reporter, LoadSuite loadSuite) { |
+ var debugger; |
+ var canceled = false; |
+ return new CancelableOperation.fromFuture(() async { |
+ // Make the underlying suite null so that the engine doesn't start running |
+ // it immediately. |
+ engine.suiteSink.add(loadSuite.changeSuite((_) => null)); |
+ |
+ var suite = await loadSuite.suite; |
+ if (canceled || suite == null) return; |
+ |
+ debugger = new _Debugger(config, engine, reporter, suite); |
+ await debugger.run(); |
+ }(), onCancel: () { |
+ canceled = true; |
+ if (debugger != null) debugger.close(); |
+ }); |
} |
+// TODO(nweiz): Test using the console and restarting a test once sdk#25369 is |
+// fixed and the VM service client is released and we can set Dartium |
+// breakpoints. |
+/// A debugger for a single test suite. |
class _Debugger { |
+ /// The user configuration for the test runner. |
final Configuration _config; |
+ /// The engine that will run the suite. |
+ final Engine _engine; |
+ |
+ /// The reporter that's reporting [_engine]'s progress. |
final Reporter _reporter; |
+ /// The suite to run. |
final RunnerSuite _suite; |
+ /// The console through which the user can control the debugger. |
+ /// |
+ /// This is only visible when the test environment is paused, so as not to |
+ /// overlap with the reporter's reporting. |
+ final Console _console; |
+ |
+ /// The subscription to [_suite.onDebugging]. |
StreamSubscription<bool> _onDebuggingSubscription; |
- bool _canDebug => |
- _suite.platform != null && _suite.platform != TestPlatform.vm; |
+ /// Whether [close] has been called. |
+ bool _closed = false; |
+ |
+ _Debugger(Configuration config, this._engine, this._reporter, this._suite) |
+ : _config = config, |
+ _console = new Console(color: config.color) { |
+ _console.registerCommand( |
+ "restart", "Restart the current test after it finishes running.", |
+ _restartTest); |
+ |
+ _onDebuggingSubscription = _suite.onDebugging.listen((debugging) { |
+ if (debugging) { |
+ _onDebugging(); |
+ } else { |
+ _onNotDebugging(); |
+ } |
+ }); |
+ } |
+ |
+ /// Runs the debugger. |
+ /// |
+ /// This prints information about the suite's debugger, then once the user has |
+ /// had a chance to set breakpoints, runs the suite's tests. |
+ Future run() async { |
+ try { |
+ await _pause(); |
+ if (_closed) return; |
- _Debugger(this._config, this._reporter, this._suite); |
+ _engine.suiteSink.add(_suite); |
+ await _engine.onIdle.first; |
+ } finally { |
+ close(); |
+ } |
+ } |
+ /// Prints URLs for the [_suite]'s debugger and waits for the user to tell the |
+ /// suite to run. |
Future _pause() async { |
if (_suite.platform == null) return; |
if (_suite.platform == TestPlatform.vm) return; |
@@ -88,21 +165,36 @@ class _Debugger { |
} |
} |
- void _listen() { |
- _suite.onDebugging.listen((debugging) { |
- if (debugging) { |
- _onDebugging(); |
- } else { |
- _onNotDebugging(); |
- } |
- }); |
+ /// Handles the environment pausing to debug. |
+ /// |
+ /// This starts the interactive console. |
+ void _onDebugging() { |
+ _reporter.pause(); |
+ |
+ print('\nEntering debugging console. Type "help" for help.'); |
+ _console.start(); |
} |
- void _onDebugging(bool debugging) { |
- _reporter.pause(); |
+ /// Handles the environment starting up again. |
+ /// |
+ /// This closes the interactive console. |
+ void _onNotDebugging() { |
+ _reporter.resume(); |
+ _console.stop(); |
+ } |
+ |
+ /// Restarts the current test. |
+ void _restartTest() { |
+ var liveTest = _engine.active.single; |
+ _engine.restartTest(liveTest); |
+ print(wordWrap( |
+ 'Will restart "${liveTest.test.name}" once it finishes running.')); |
} |
- void _close() { |
+ /// Closes the debugger and releases its resources. |
+ void close() { |
+ _closed = true; |
_onDebuggingSubscription.cancel(); |
+ _console.stop(); |
} |
} |