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

Side by Side 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 unified diff | 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 »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 import 'dart:async';
6
7 import 'package:stream_channel/stream_channel.dart';
8
9 import '../backend/declarer.dart';
10 import '../backend/group.dart';
11 import '../backend/live_test.dart';
12 import '../backend/metadata.dart';
13 import '../backend/operating_system.dart';
14 import '../backend/suite.dart';
15 import '../backend/test.dart';
16 import '../backend/test_platform.dart';
17 import '../util/remote_exception.dart';
18 import '../utils.dart';
19
20 class RemoteListener {
21 /// The test suite to run.
22 final Suite _suite;
23
24 /// The zone to forward prints to, or `null` if prints shouldn't be forwarded.
25 final Zone _printZone;
26
27 /// Extracts metadata about all the tests in the function returned by
28 /// [getMain] and returns a channel that will send information about them.
29 ///
30 /// The main function is wrapped in a closure so that we can handle it being
31 /// undefined here rather than in the generated code.
32 ///
33 /// Once that's done, this starts listening for commands about which tests to
34 /// run.
35 ///
36 /// If [hidePrints] is `true` (the default), calls to `print()` within this
37 /// suite will not be forwarded to the parent zone's print handler. However,
38 /// the caller may want them to be forwarded in (for example) a browser
39 /// context where they'll be visible in the development console.
40 static StreamChannel start(AsyncFunction getMain(), {bool hidePrints: true}) {
41 // This has to be synchronous to work around sdk#25745. Otherwise, there'll
42 // be an asynchronous pause before a syntax error notification is sent,
43 // which will cause the send to fail entirely.
44 var controller = new StreamChannelController(
45 allowForeignErrors: false, sync: true);
46 var channel = new MultiChannel(controller.local);
47
48 var printZone = hidePrints ? null : Zone.current;
49 runZoned(() async {
50 var main;
51 try {
52 main = getMain();
53 } on NoSuchMethodError catch (_) {
54 _sendLoadException(channel, "No top-level main() function defined.");
55 return;
56 } catch (error, stackTrace) {
57 _sendError(channel, error, stackTrace);
58 return;
59 }
60
61 if (main is! Function) {
62 _sendLoadException(channel, "Top-level main getter is not a function.");
63 return;
64 } else if (main is! AsyncFunction) {
65 _sendLoadException(
66 channel, "Top-level main() function takes arguments.");
67 return;
68 }
69
70 var message = await channel.stream.first;
71 var metadata = new Metadata.deserialize(message['metadata']);
72 var declarer = new Declarer(metadata);
73 await declarer.declare(main);
74
75 var os = message['os'] == null
76 ? null
77 : OperatingSystem.find(message['os']);
78 var platform = TestPlatform.find(message['platform']);
79 var suite = new Suite(declarer.build(), platform: platform, os: os);
80 new RemoteListener._(suite, printZone)._listen(channel);
81 }, onError: (error, stackTrace) {
82 _sendError(channel, error, stackTrace);
83 }, zoneSpecification: new ZoneSpecification(print: (_, __, ___, line) {
84 if (printZone != null) printZone.print(line);
85 channel.sink.add({"type": "print", "line": line});
86 }));
87
88 return controller.foreign;
89 }
90
91
92 /// Sends a message over [channel] indicating that the tests failed to load.
93 ///
94 /// [message] should describe the failure.
95 static void _sendLoadException(StreamChannel channel, String message) {
96 channel.sink.add({"type": "loadException", "message": message});
97 }
98
99 /// Sends a message over [channel] indicating an error from user code.
100 static void _sendError(StreamChannel channel, error, StackTrace stackTrace) {
101 channel.sink.add({
102 "type": "error",
103 "error": RemoteException.serialize(error, stackTrace)
104 });
105 }
106
107 RemoteListener._(this._suite, this._printZone);
108
109 /// Send information about [_suite] across [channel] and start listening for
110 /// commands to run the tests.
111 void _listen(MultiChannel channel) {
112 channel.sink.add({
113 "type": "success",
114 "root": _serializeGroup(channel, _suite.group, [])
115 });
116 }
117
118 /// Serializes [group] into a JSON-safe map.
119 ///
120 /// [parents] lists the groups that contain [group].
121 Map _serializeGroup(MultiChannel channel, Group group,
122 Iterable<Group> parents) {
123 parents = parents.toList()..add(group);
124 return {
125 "type": "group",
126 "name": group.name,
127 "metadata": group.metadata.serialize(),
128 "setUpAll": _serializeTest(channel, group.setUpAll, parents),
129 "tearDownAll": _serializeTest(channel, group.tearDownAll, parents),
130 "entries": group.entries.map((entry) {
131 return entry is Group
132 ? _serializeGroup(channel, entry, parents)
133 : _serializeTest(channel, entry, parents);
134 }).toList()
135 };
136 }
137
138 /// Serializes [test] into a JSON-safe map.
139 ///
140 /// [groups] lists the groups that contain [test]. Returns `null` if [test]
141 /// is `null`.
142 Map _serializeTest(MultiChannel channel, Test test, Iterable<Group> groups) {
143 if (test == null) return null;
144
145 var testChannel = channel.virtualChannel();
146 testChannel.stream.listen((message) {
147 assert(message['command'] == 'run');
148 _runLiveTest(
149 test.load(_suite, groups: groups),
150 channel.virtualChannel(message['channel']));
151 });
152
153 return {
154 "type": "test",
155 "name": test.name,
156 "metadata": test.metadata.serialize(),
157 "channel": testChannel.id
158 };
159 }
160
161 /// Runs [liveTest] and sends the results across [channel].
162 void _runLiveTest(LiveTest liveTest, MultiChannel channel) {
163 channel.stream.listen((message) {
164 assert(message['command'] == 'close');
165 liveTest.close();
166 });
167
168 liveTest.onStateChange.listen((state) {
169 channel.sink.add({
170 "type": "state-change",
171 "status": state.status.name,
172 "result": state.result.name
173 });
174 });
175
176 liveTest.onError.listen((asyncError) {
177 channel.sink.add({
178 "type": "error",
179 "error": RemoteException.serialize(
180 asyncError.error, asyncError.stackTrace)
181 });
182 });
183
184 liveTest.onPrint.listen((line) {
185 if (_printZone != null) _printZone.print(line);
186 channel.sink.add({"type": "print", "line": line});
187 });
188
189 liveTest.run().then((_) => channel.sink.add({"type": "complete"}));
190 }
191 }
OLDNEW
« 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