OLD | NEW |
(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:stack_trace/stack_trace.dart'; |
| 8 import 'package:stream_channel/stream_channel.dart'; |
| 9 |
| 10 import '../../backend/group.dart'; |
| 11 import '../../backend/metadata.dart'; |
| 12 import '../../backend/test.dart'; |
| 13 import '../../backend/test_platform.dart'; |
| 14 import '../../util/io.dart'; |
| 15 import '../../util/remote_exception.dart'; |
| 16 import '../environment.dart'; |
| 17 import '../load_exception.dart'; |
| 18 import '../runner_suite.dart'; |
| 19 import '../runner_test.dart'; |
| 20 |
| 21 typedef StackTrace _MapTrace(StackTrace trace); |
| 22 |
| 23 /// A helper method for creating a [RunnerSuiteController] containing tests |
| 24 /// that communicate over [channel]. |
| 25 /// |
| 26 /// This returns a controller so that the caller has a chance to control the |
| 27 /// runner suite's debugging state based on plugin-specific logic. |
| 28 /// |
| 29 /// If the suite is closed, this will close [channel]. |
| 30 /// |
| 31 /// If [mapTrace] is passed, it will be used to adjust stack traces for any |
| 32 /// errors emitted by tests. |
| 33 Future<RunnerSuiteController> deserializeSuite(String path, |
| 34 TestPlatform platform, Metadata metadata, Environment environment, |
| 35 StreamChannel channel, {StackTrace mapTrace(StackTrace trace)}) async { |
| 36 if (mapTrace == null) mapTrace = (trace) => trace; |
| 37 |
| 38 var disconnector = new Disconnector(); |
| 39 var suiteChannel = new MultiChannel(channel.transform(disconnector)); |
| 40 |
| 41 suiteChannel.sink.add({ |
| 42 'platform': platform.identifier, |
| 43 'metadata': metadata.serialize(), |
| 44 'os': platform == TestPlatform.vm ? currentOS.name : null |
| 45 }); |
| 46 |
| 47 var completer = new Completer(); |
| 48 |
| 49 handleError(error, stackTrace) { |
| 50 disconnector.disconnect(); |
| 51 |
| 52 if (completer.isCompleted) { |
| 53 // If we've already provided a controller, send the error to the |
| 54 // LoadSuite. This will cause the virtual load test to fail, which will |
| 55 // notify the user of the error. |
| 56 Zone.current.handleUncaughtError(error, mapTrace(stackTrace)); |
| 57 } else { |
| 58 completer.completeError(error, mapTrace(stackTrace)); |
| 59 } |
| 60 } |
| 61 |
| 62 suiteChannel.stream.listen((response) { |
| 63 switch (response["type"]) { |
| 64 case "print": |
| 65 print(response["line"]); |
| 66 break; |
| 67 |
| 68 case "loadException": |
| 69 handleError( |
| 70 new LoadException(path, response["message"]), |
| 71 new Trace.current()); |
| 72 break; |
| 73 |
| 74 case "error": |
| 75 var asyncError = RemoteException.deserialize(response["error"]); |
| 76 handleError( |
| 77 new LoadException(path, asyncError.error), |
| 78 mapTrace(asyncError.stackTrace)); |
| 79 break; |
| 80 |
| 81 case "success": |
| 82 var deserializer = new _Deserializer(suiteChannel, mapTrace); |
| 83 completer.complete(deserializer.deserializeGroup(response["root"])); |
| 84 break; |
| 85 } |
| 86 }, onError: handleError, onDone: () { |
| 87 if (completer.isCompleted) return; |
| 88 completer.completeError( |
| 89 new LoadException( |
| 90 path, "Connection closed before test suite loaded."), |
| 91 new Trace.current()); |
| 92 }); |
| 93 |
| 94 return new RunnerSuiteController( |
| 95 environment, |
| 96 await completer.future, |
| 97 path: path, |
| 98 platform: platform, |
| 99 os: currentOS, |
| 100 onClose: disconnector.disconnect); |
| 101 } |
| 102 |
| 103 /// A utility class for storing state while deserializing tests. |
| 104 class _Deserializer { |
| 105 /// The channel over which tests communicate. |
| 106 final MultiChannel _channel; |
| 107 |
| 108 /// The function used to errors' map stack traces. |
| 109 final _MapTrace _mapTrace; |
| 110 |
| 111 _Deserializer(this._channel, this._mapTrace); |
| 112 |
| 113 /// Deserializes [group] into a concrete [Group]. |
| 114 Group deserializeGroup(Map group) { |
| 115 var metadata = new Metadata.deserialize(group['metadata']); |
| 116 return new Group(group['name'], group['entries'].map((entry) { |
| 117 if (entry['type'] == 'group') return deserializeGroup(entry); |
| 118 return _deserializeTest(entry); |
| 119 }), |
| 120 metadata: metadata, |
| 121 setUpAll: _deserializeTest(group['setUpAll']), |
| 122 tearDownAll: _deserializeTest(group['tearDownAll'])); |
| 123 } |
| 124 |
| 125 /// Deserializes [test] into a concrete [Test] class. |
| 126 /// |
| 127 /// Returns `null` if [test] is `null`. |
| 128 Test _deserializeTest(Map test) { |
| 129 if (test == null) return null; |
| 130 |
| 131 var metadata = new Metadata.deserialize(test['metadata']); |
| 132 var testChannel = _channel.virtualChannel(test['channel']); |
| 133 return new RunnerTest(test['name'], metadata, testChannel, _mapTrace); |
| 134 } |
| 135 } |
OLD | NEW |