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

Unified Diff: lib/src/runner/remote_listener.dart

Issue 1685363002: Add a platform plugin infrastructure. (Closed) Base URL: git@github.com:dart-lang/test@master
Patch Set: Code review changes Created 4 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 | « lib/src/runner/plugin/remote_platform_helpers.dart ('k') | lib/src/runner/runner_test.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/src/runner/remote_listener.dart
diff --git a/lib/src/runner/remote_listener.dart b/lib/src/runner/remote_listener.dart
new file mode 100644
index 0000000000000000000000000000000000000000..a041ccd35eb668d4c45f48eec3785d085746b496
--- /dev/null
+++ b/lib/src/runner/remote_listener.dart
@@ -0,0 +1,191 @@
+// 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.
+
+import 'dart:async';
+
+import 'package:stream_channel/stream_channel.dart';
+
+import '../backend/declarer.dart';
+import '../backend/group.dart';
+import '../backend/live_test.dart';
+import '../backend/metadata.dart';
+import '../backend/operating_system.dart';
+import '../backend/suite.dart';
+import '../backend/test.dart';
+import '../backend/test_platform.dart';
+import '../util/remote_exception.dart';
+import '../utils.dart';
+
+class RemoteListener {
+ /// The test suite to run.
+ final Suite _suite;
+
+ /// The zone to forward prints to, or `null` if prints shouldn't be forwarded.
+ final Zone _printZone;
+
+ /// Extracts metadata about all the tests in the function returned by
+ /// [getMain] and returns a channel that will send information about them.
+ ///
+ /// The main function is wrapped in a closure so that we can handle it being
+ /// undefined here rather than in the generated code.
+ ///
+ /// Once that's done, this starts listening for commands about which tests to
+ /// run.
+ ///
+ /// If [hidePrints] is `true` (the default), calls to `print()` within this
+ /// suite will not be forwarded to the parent zone's print handler. However,
+ /// the caller may want them to be forwarded in (for example) a browser
+ /// context where they'll be visible in the development console.
+ static StreamChannel start(AsyncFunction getMain(), {bool hidePrints: true}) {
+ // This has to be synchronous to work around sdk#25745. Otherwise, there'll
+ // be an asynchronous pause before a syntax error notification is sent,
+ // which will cause the send to fail entirely.
+ var controller = new StreamChannelController(
+ allowForeignErrors: false, sync: true);
+ var channel = new MultiChannel(controller.local);
+
+ var printZone = hidePrints ? null : Zone.current;
+ runZoned(() async {
+ var main;
+ try {
+ main = getMain();
+ } on NoSuchMethodError catch (_) {
+ _sendLoadException(channel, "No top-level main() function defined.");
+ return;
+ } catch (error, stackTrace) {
+ _sendError(channel, error, stackTrace);
+ return;
+ }
+
+ if (main is! Function) {
+ _sendLoadException(channel, "Top-level main getter is not a function.");
+ return;
+ } else if (main is! AsyncFunction) {
+ _sendLoadException(
+ channel, "Top-level main() function takes arguments.");
+ return;
+ }
+
+ var message = await channel.stream.first;
+ var metadata = new Metadata.deserialize(message['metadata']);
+ var declarer = new Declarer(metadata);
+ await declarer.declare(main);
+
+ var os = message['os'] == null
+ ? null
+ : OperatingSystem.find(message['os']);
+ var platform = TestPlatform.find(message['platform']);
+ var suite = new Suite(declarer.build(), platform: platform, os: os);
+ new RemoteListener._(suite, printZone)._listen(channel);
+ }, onError: (error, stackTrace) {
+ _sendError(channel, error, stackTrace);
+ }, zoneSpecification: new ZoneSpecification(print: (_, __, ___, line) {
+ if (printZone != null) printZone.print(line);
+ channel.sink.add({"type": "print", "line": line});
+ }));
+
+ return controller.foreign;
+ }
+
+
+ /// Sends a message over [channel] indicating that the tests failed to load.
+ ///
+ /// [message] should describe the failure.
+ static void _sendLoadException(StreamChannel channel, String message) {
+ channel.sink.add({"type": "loadException", "message": message});
+ }
+
+ /// Sends a message over [channel] indicating an error from user code.
+ static void _sendError(StreamChannel channel, error, StackTrace stackTrace) {
+ channel.sink.add({
+ "type": "error",
+ "error": RemoteException.serialize(error, stackTrace)
+ });
+ }
+
+ RemoteListener._(this._suite, this._printZone);
+
+ /// Send information about [_suite] across [channel] and start listening for
+ /// commands to run the tests.
+ void _listen(MultiChannel channel) {
+ channel.sink.add({
+ "type": "success",
+ "root": _serializeGroup(channel, _suite.group, [])
+ });
+ }
+
+ /// Serializes [group] into a JSON-safe map.
+ ///
+ /// [parents] lists the groups that contain [group].
+ Map _serializeGroup(MultiChannel channel, Group group,
+ Iterable<Group> parents) {
+ parents = parents.toList()..add(group);
+ return {
+ "type": "group",
+ "name": group.name,
+ "metadata": group.metadata.serialize(),
+ "setUpAll": _serializeTest(channel, group.setUpAll, parents),
+ "tearDownAll": _serializeTest(channel, group.tearDownAll, parents),
+ "entries": group.entries.map((entry) {
+ return entry is Group
+ ? _serializeGroup(channel, entry, parents)
+ : _serializeTest(channel, entry, parents);
+ }).toList()
+ };
+ }
+
+ /// Serializes [test] into a JSON-safe map.
+ ///
+ /// [groups] lists the groups that contain [test]. Returns `null` if [test]
+ /// is `null`.
+ Map _serializeTest(MultiChannel channel, Test test, Iterable<Group> groups) {
+ if (test == null) return null;
+
+ var testChannel = channel.virtualChannel();
+ testChannel.stream.listen((message) {
+ assert(message['command'] == 'run');
+ _runLiveTest(
+ test.load(_suite, groups: groups),
+ channel.virtualChannel(message['channel']));
+ });
+
+ return {
+ "type": "test",
+ "name": test.name,
+ "metadata": test.metadata.serialize(),
+ "channel": testChannel.id
+ };
+ }
+
+ /// Runs [liveTest] and sends the results across [channel].
+ void _runLiveTest(LiveTest liveTest, MultiChannel channel) {
+ channel.stream.listen((message) {
+ assert(message['command'] == 'close');
+ liveTest.close();
+ });
+
+ liveTest.onStateChange.listen((state) {
+ channel.sink.add({
+ "type": "state-change",
+ "status": state.status.name,
+ "result": state.result.name
+ });
+ });
+
+ liveTest.onError.listen((asyncError) {
+ channel.sink.add({
+ "type": "error",
+ "error": RemoteException.serialize(
+ asyncError.error, asyncError.stackTrace)
+ });
+ });
+
+ liveTest.onPrint.listen((line) {
+ if (_printZone != null) _printZone.print(line);
+ channel.sink.add({"type": "print", "line": line});
+ });
+
+ liveTest.run().then((_) => channel.sink.add({"type": "complete"}));
+ }
+}
« no previous file with comments | « lib/src/runner/plugin/remote_platform_helpers.dart ('k') | lib/src/runner/runner_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698