Switch from hack_load_vm_file_hook to PlatformPlugin
This patch switches us from using our previous hacking approach to integrating with package:test to using the new PlatformPlugin interface.
This commit is contained in:
parent
13e7c017e6
commit
96ec5316b8
@ -14,7 +14,7 @@ dependencies:
|
||||
# See the comment in flutter_tools' pubspec.yaml. We have to pin it
|
||||
# here also because sky_services depends on mojo_sdk which depends
|
||||
# on test.
|
||||
test: 0.12.6+1
|
||||
test: 0.12.11+1
|
||||
|
||||
# We have to pin analyzer to 0.27.1 because the flx package depends
|
||||
# on pointycastle which depends on reflectable which depends on
|
||||
|
@ -1,6 +1,6 @@
|
||||
name: flutter_test
|
||||
dependencies:
|
||||
test: 0.12.6+1
|
||||
test: 0.12.11+1
|
||||
quiver: ^0.21.4
|
||||
flutter:
|
||||
path: ../flutter
|
||||
|
@ -12,7 +12,7 @@ import '../artifacts.dart';
|
||||
import '../build_configuration.dart';
|
||||
import '../globals.dart';
|
||||
import '../runner/flutter_command.dart';
|
||||
import '../test/loader.dart' as loader;
|
||||
import '../test/flutter_platform.dart' as loader;
|
||||
|
||||
class TestCommand extends FlutterCommand {
|
||||
String get name => 'test';
|
||||
|
133
packages/flutter_tools/lib/src/test/flutter_platform.dart
Normal file
133
packages/flutter_tools/lib/src/test/flutter_platform.dart
Normal file
@ -0,0 +1,133 @@
|
||||
// Copyright 2015 The Chromium Authors. 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 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:async/async.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:stream_channel/stream_channel.dart';
|
||||
|
||||
import 'package:test/src/backend/test_platform.dart';
|
||||
import 'package:test/src/runner/plugin/platform.dart';
|
||||
import 'package:test/src/runner/plugin/hack_register_platform.dart' as hack;
|
||||
|
||||
import '../artifacts.dart';
|
||||
|
||||
final String _kSkyShell = Platform.environment['SKY_SHELL'];
|
||||
const String _kHost = '127.0.0.1';
|
||||
const String _kPath = '/runner';
|
||||
|
||||
String shellPath;
|
||||
|
||||
void installHook() {
|
||||
hack.registerPlatformPlugin([TestPlatform.vm], () => new FlutterPlatform());
|
||||
}
|
||||
|
||||
class _ServerInfo {
|
||||
final String url;
|
||||
final Future<WebSocket> socket;
|
||||
final HttpServer server;
|
||||
|
||||
_ServerInfo(this.server, this.url, this.socket);
|
||||
}
|
||||
|
||||
Future<_ServerInfo> _startServer() async {
|
||||
HttpServer server = await HttpServer.bind(_kHost, 0);
|
||||
Completer<WebSocket> socket = new Completer<WebSocket>();
|
||||
server.listen((HttpRequest request) {
|
||||
if (request.uri.path == _kPath)
|
||||
socket.complete(WebSocketTransformer.upgrade(request));
|
||||
});
|
||||
return new _ServerInfo(server, 'ws://$_kHost:${server.port}$_kPath', socket.future);
|
||||
}
|
||||
|
||||
Future<Process> _startProcess(String mainPath, { String packageRoot }) {
|
||||
assert(shellPath != null || _kSkyShell != null); // Please provide the path to the shell in the SKY_SHELL environment variable.
|
||||
return Process.start(shellPath ?? _kSkyShell, [
|
||||
'--enable-checked-mode',
|
||||
'--non-interactive',
|
||||
'--package-root=$packageRoot',
|
||||
mainPath,
|
||||
]);
|
||||
}
|
||||
|
||||
class FlutterPlatform extends PlatformPlugin {
|
||||
StreamChannel loadChannel(String mainPath, TestPlatform platform) {
|
||||
return StreamChannelCompleter.fromFuture(_startTest(mainPath));
|
||||
}
|
||||
|
||||
Future<StreamChannel> _startTest(String mainPath) async {
|
||||
_ServerInfo info = await _startServer();
|
||||
Directory tempDir = Directory.systemTemp.createTempSync(
|
||||
'dart_test_listener');
|
||||
File listenerFile = new File('${tempDir.path}/listener.dart');
|
||||
listenerFile.createSync();
|
||||
listenerFile.writeAsStringSync('''
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:stream_channel/stream_channel.dart';
|
||||
import 'package:test/src/runner/plugin/remote_platform_helpers.dart';
|
||||
import 'package:test/src/runner/vm/catch_isolate_errors.dart';
|
||||
|
||||
import '${path.toUri(path.absolute(mainPath))}' as test;
|
||||
|
||||
void main() {
|
||||
String server = Uri.decodeComponent('${Uri.encodeComponent(info.url)}');
|
||||
StreamChannel channel = serializeSuite(() {
|
||||
catchIsolateErrors();
|
||||
return test.main;
|
||||
});
|
||||
WebSocket.connect(server).then((WebSocket socket) {
|
||||
socket.map(JSON.decode).pipe(channel.sink);
|
||||
socket.addStream(channel.stream.map(JSON.encode));
|
||||
});
|
||||
}
|
||||
''');
|
||||
|
||||
Process process = await _startProcess(
|
||||
listenerFile.path,
|
||||
packageRoot: path.absolute(ArtifactStore.packageRoot)
|
||||
);
|
||||
|
||||
void finalize() {
|
||||
if (process != null) {
|
||||
Process processToKill = process;
|
||||
process = null;
|
||||
processToKill.kill();
|
||||
}
|
||||
if (tempDir != null) {
|
||||
Directory dirToDelete = tempDir;
|
||||
tempDir = null;
|
||||
dirToDelete.deleteSync(recursive: true);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
WebSocket socket = await info.socket;
|
||||
StreamChannel channel = new StreamChannel(socket.map(JSON.decode), socket);
|
||||
return channel.transformStream(
|
||||
new StreamTransformer.fromHandlers(
|
||||
handleDone: (sink) {
|
||||
finalize();
|
||||
sink.close();
|
||||
}
|
||||
)
|
||||
).transformSink(new StreamSinkTransformer.fromHandlers(
|
||||
handleData: (data, StreamSink sink) {
|
||||
sink.add(JSON.encode(data));
|
||||
},
|
||||
handleDone: (sink) {
|
||||
finalize();
|
||||
sink.close();
|
||||
}
|
||||
));
|
||||
} catch(e) {
|
||||
finalize();
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
// Copyright 2015 The Chromium Authors. 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 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
class JSONSocket {
|
||||
JSONSocket(WebSocket socket, this.unusualTermination)
|
||||
: _socket = socket, stream = socket.map(JSON.decode).asBroadcastStream();
|
||||
|
||||
final WebSocket _socket;
|
||||
final Stream stream;
|
||||
final Future<String> unusualTermination;
|
||||
|
||||
void send(dynamic data) {
|
||||
_socket.add(JSON.encode(data));
|
||||
}
|
||||
}
|
@ -1,195 +0,0 @@
|
||||
// Copyright 2015 The Chromium Authors. 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 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:stack_trace/stack_trace.dart';
|
||||
import 'package:test/src/backend/group.dart';
|
||||
import 'package:test/src/backend/metadata.dart';
|
||||
import 'package:test/src/backend/test_platform.dart';
|
||||
import 'package:test/src/runner/configuration.dart';
|
||||
import 'package:test/src/runner/hack_load_vm_file_hook.dart' as hack;
|
||||
import 'package:test/src/runner/load_exception.dart';
|
||||
import 'package:test/src/runner/runner_suite.dart';
|
||||
import 'package:test/src/runner/vm/environment.dart';
|
||||
import 'package:test/src/util/io.dart';
|
||||
import 'package:test/src/util/remote_exception.dart';
|
||||
|
||||
import 'json_socket.dart';
|
||||
import 'remote_test.dart';
|
||||
|
||||
void installHook() {
|
||||
hack.loadVMFileHook = _loadVMFile;
|
||||
}
|
||||
|
||||
final String _kSkyShell = Platform.environment['SKY_SHELL'];
|
||||
const String _kHost = '127.0.0.1';
|
||||
const String _kPath = '/runner';
|
||||
|
||||
String shellPath;
|
||||
|
||||
// Right now a bunch of our tests crash or assert after the tests have finished running.
|
||||
// Mostly this is just because the test puts the framework in an inconsistent state with
|
||||
// a scheduled microtask that verifies that state. Eventually we should fix all these
|
||||
// problems but for now we'll just paper over them.
|
||||
const bool kExpectAllTestsToCloseCleanly = false;
|
||||
|
||||
class _ServerInfo {
|
||||
final String url;
|
||||
final Future<WebSocket> socket;
|
||||
final HttpServer server;
|
||||
|
||||
_ServerInfo(this.server, this.url, this.socket);
|
||||
}
|
||||
|
||||
Future<_ServerInfo> _createServer() async {
|
||||
HttpServer server = await HttpServer.bind(_kHost, 0);
|
||||
Completer<WebSocket> socket = new Completer<WebSocket>();
|
||||
server.listen((HttpRequest request) {
|
||||
if (request.uri.path == _kPath)
|
||||
socket.complete(WebSocketTransformer.upgrade(request));
|
||||
});
|
||||
return new _ServerInfo(server, 'ws://$_kHost:${server.port}$_kPath', socket.future);
|
||||
}
|
||||
|
||||
Future<Process> _startProcess(String mainPath, { String packageRoot }) {
|
||||
assert(shellPath != null || _kSkyShell != null); // Please provide the path to the shell in the SKY_SHELL environment variable.
|
||||
return Process.start(shellPath ?? _kSkyShell, [
|
||||
'--enable-checked-mode',
|
||||
'--non-interactive',
|
||||
'--package-root=$packageRoot',
|
||||
mainPath,
|
||||
]);
|
||||
}
|
||||
|
||||
Future<RunnerSuite> _loadVMFile(String mainPath,
|
||||
Metadata metadata,
|
||||
Configuration config) async {
|
||||
String encodedMetadata = Uri.encodeComponent(JSON.encode(
|
||||
metadata.serialize()));
|
||||
_ServerInfo info = await _createServer();
|
||||
Directory tempDir = await Directory.systemTemp.createTemp(
|
||||
'dart_test_listener');
|
||||
File listenerFile = new File('${tempDir.path}/listener.dart');
|
||||
await listenerFile.create();
|
||||
await listenerFile.writeAsString('''
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:test/src/backend/metadata.dart';
|
||||
import 'package:flutter_tools/src/test/remote_listener.dart';
|
||||
|
||||
import '${path.toUri(path.absolute(mainPath))}' as test;
|
||||
|
||||
void main() {
|
||||
String server = Uri.decodeComponent('${Uri.encodeComponent(info.url)}');
|
||||
Metadata metadata = new Metadata.deserialize(
|
||||
JSON.decode(Uri.decodeComponent('$encodedMetadata')));
|
||||
RemoteListener.start(server, metadata, () => test.main);
|
||||
}
|
||||
''');
|
||||
|
||||
Completer<Iterable<RemoteTest>> completer = new Completer<Iterable<RemoteTest>>();
|
||||
Completer<String> deathCompleter = new Completer();
|
||||
|
||||
Process process = await _startProcess(
|
||||
listenerFile.path,
|
||||
packageRoot: path.absolute(config.packageRoot)
|
||||
);
|
||||
|
||||
Future cleanupTempDirectory() async {
|
||||
if (tempDir == null)
|
||||
return;
|
||||
Directory dirToDelete = tempDir;
|
||||
tempDir = null;
|
||||
await dirToDelete.delete(recursive: true);
|
||||
}
|
||||
|
||||
process.exitCode.then((int exitCode) async {
|
||||
try {
|
||||
info.server.close(force: true);
|
||||
await cleanupTempDirectory();
|
||||
String output = '';
|
||||
if (exitCode < 0) {
|
||||
// Abnormal termination (high bit of signed 8-bit exitCode is set)
|
||||
switch (exitCode) {
|
||||
case -0x0f: // ProcessSignal.SIGTERM
|
||||
break; // we probably killed it ourselves
|
||||
case -0x0b: // ProcessSignal.SIGSEGV
|
||||
output += 'Segmentation fault in subprocess for: $mainPath\n';
|
||||
break;
|
||||
case -0x06: // ProcessSignal.SIGABRT
|
||||
output += 'Aborted while running: $mainPath\n';
|
||||
break;
|
||||
default:
|
||||
output += 'Unexpected exit code $exitCode from subprocess for: $mainPath\n';
|
||||
}
|
||||
}
|
||||
String stdout = await process.stdout.transform(UTF8.decoder).join('\n');
|
||||
String stderr = await process.stderr.transform(UTF8.decoder).join('\n');
|
||||
if (stdout != '')
|
||||
output += '\nstdout:\n$stdout';
|
||||
if (stderr != '')
|
||||
output += '\nstderr:\n$stderr';
|
||||
if (!completer.isCompleted) {
|
||||
if (output == '')
|
||||
output = 'No output.';
|
||||
completer.completeError(
|
||||
new LoadException(mainPath, output),
|
||||
new Trace.current()
|
||||
);
|
||||
} else {
|
||||
if (kExpectAllTestsToCloseCleanly && output != '')
|
||||
print('Unexpected failure after test claimed to pass:\n$output');
|
||||
}
|
||||
deathCompleter.complete(output);
|
||||
} catch (e) {
|
||||
// Throwing inside this block causes all kinds of hard-to-debug issues
|
||||
// like stack overflows and hangs. So catch everything just in case.
|
||||
print("exception while handling subprocess termination: $e");
|
||||
}
|
||||
});
|
||||
|
||||
JSONSocket socket = new JSONSocket(await info.socket, deathCompleter.future);
|
||||
|
||||
await cleanupTempDirectory();
|
||||
|
||||
StreamSubscription subscription;
|
||||
subscription = socket.stream.listen((response) {
|
||||
if (response["type"] == "print") {
|
||||
print(response["line"]);
|
||||
} else if (response["type"] == "loadException") {
|
||||
process.kill(ProcessSignal.SIGTERM);
|
||||
completer.completeError(
|
||||
new LoadException(mainPath, response["message"]),
|
||||
new Trace.current());
|
||||
} else if (response["type"] == "error") {
|
||||
process.kill(ProcessSignal.SIGTERM);
|
||||
AsyncError asyncError = RemoteException.deserialize(response["error"]);
|
||||
completer.completeError(
|
||||
new LoadException(mainPath, asyncError.error),
|
||||
asyncError.stackTrace);
|
||||
} else {
|
||||
assert(response["type"] == "success");
|
||||
subscription.cancel();
|
||||
completer.complete(response["tests"].map((test) {
|
||||
var testMetadata = new Metadata.deserialize(test['metadata']);
|
||||
return new RemoteTest(test['name'], testMetadata, socket, test['index']);
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
Iterable<RemoteTest> entries = await completer.future;
|
||||
|
||||
return new RunnerSuite(
|
||||
const VMEnvironment(),
|
||||
new Group.root(entries, metadata: metadata),
|
||||
path: mainPath,
|
||||
platform: TestPlatform.vm,
|
||||
os: currentOS,
|
||||
onClose: () { process.kill(ProcessSignal.SIGTERM); }
|
||||
);
|
||||
}
|
@ -1,165 +0,0 @@
|
||||
// Copyright 2015 The Chromium Authors. 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 'dart:collection';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:stack_trace/stack_trace.dart';
|
||||
import 'package:test/src/backend/declarer.dart';
|
||||
import 'package:test/src/backend/live_test.dart';
|
||||
import 'package:test/src/backend/metadata.dart';
|
||||
import 'package:test/src/backend/operating_system.dart';
|
||||
import 'package:test/src/backend/suite.dart';
|
||||
import 'package:test/src/backend/test.dart';
|
||||
import 'package:test/src/backend/test_platform.dart';
|
||||
import 'package:test/src/util/remote_exception.dart';
|
||||
|
||||
final OperatingSystem currentOS = (() {
|
||||
var name = Platform.operatingSystem;
|
||||
var os = OperatingSystem.findByIoName(name);
|
||||
if (os != null) return os;
|
||||
|
||||
throw new UnsupportedError('Unsupported operating system "$name".');
|
||||
})();
|
||||
|
||||
typedef AsyncFunction();
|
||||
|
||||
class RemoteListener {
|
||||
RemoteListener._(this._suite, this._socket);
|
||||
|
||||
final Suite _suite;
|
||||
final WebSocket _socket;
|
||||
final Set<LiveTest> _liveTests = new HashSet<LiveTest>();
|
||||
|
||||
static Future start(String server, Metadata metadata, Function getMain()) async {
|
||||
WebSocket socket = await WebSocket.connect(server);
|
||||
// Capture any top-level errors (mostly lazy syntax errors, since other are
|
||||
// caught below) and report them to the parent isolate. We set errors
|
||||
// non-fatal because otherwise they'll be double-printed.
|
||||
var errorPort = new ReceivePort();
|
||||
Isolate.current.setErrorsFatal(false);
|
||||
Isolate.current.addErrorListener(errorPort.sendPort);
|
||||
errorPort.listen((message) {
|
||||
// Masquerade as an IsolateSpawnException because that's what this would
|
||||
// be if the error had been detected statically.
|
||||
var error = new IsolateSpawnException(message[0]);
|
||||
var stackTrace =
|
||||
message[1] == null ? new Trace([]) : new Trace.parse(message[1]);
|
||||
socket.add(JSON.encode({
|
||||
"type": "error",
|
||||
"error": RemoteException.serialize(error, stackTrace)
|
||||
}));
|
||||
});
|
||||
|
||||
var main;
|
||||
try {
|
||||
main = getMain();
|
||||
} on NoSuchMethodError catch (_) {
|
||||
_sendLoadException(socket, "No top-level main() function defined.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (main is! Function) {
|
||||
_sendLoadException(socket, "Top-level main getter is not a function.");
|
||||
return;
|
||||
} else if (main is! AsyncFunction) {
|
||||
_sendLoadException(
|
||||
socket, "Top-level main() function takes arguments.");
|
||||
return;
|
||||
}
|
||||
|
||||
Declarer declarer = new Declarer(metadata);
|
||||
try {
|
||||
await runZoned(() => new Future.sync(main), zoneValues: {
|
||||
#test.declarer: declarer
|
||||
}, zoneSpecification: new ZoneSpecification(print: (_, __, ___, line) {
|
||||
socket.add(JSON.encode({"type": "print", "line": line}));
|
||||
}));
|
||||
} catch (error, stackTrace) {
|
||||
socket.add(JSON.encode({
|
||||
"type": "error",
|
||||
"error": RemoteException.serialize(error, stackTrace)
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
Suite suite = new Suite(declarer.build(),
|
||||
platform: TestPlatform.vm, os: currentOS);
|
||||
new RemoteListener._(suite, socket)._listen();
|
||||
}
|
||||
|
||||
static void _sendLoadException(WebSocket socket, String message) {
|
||||
socket.add(JSON.encode({"type": "loadException", "message": message}));
|
||||
}
|
||||
|
||||
void _send(data) {
|
||||
_socket.add(JSON.encode(data));
|
||||
}
|
||||
|
||||
void _listen() {
|
||||
List tests = [];
|
||||
for (var i = 0; i < _suite.group.entries.length; i++) {
|
||||
// TODO(ianh): entries[] might return a Group instead of a Test. We don't
|
||||
// currently support nested groups.
|
||||
Test test = _suite.group.entries[i];
|
||||
tests.add({
|
||||
"name": test.name,
|
||||
"metadata": test.metadata.serialize(),
|
||||
"index": i,
|
||||
});
|
||||
}
|
||||
|
||||
_send({"type": "success", "tests": tests});
|
||||
_socket.listen(_handleCommand);
|
||||
}
|
||||
|
||||
void _handleCommand(String data) {
|
||||
var message = JSON.decode(data);
|
||||
if (message['command'] == 'run') {
|
||||
// TODO(ianh): entries[] might return a Group instead of a Test. We don't
|
||||
// currently support nested groups.
|
||||
Test test = _suite.group.entries[message['index']];
|
||||
LiveTest liveTest = test.load(_suite);
|
||||
_liveTests.add(liveTest);
|
||||
|
||||
liveTest.onStateChange.listen((state) {
|
||||
_send({
|
||||
"type": "state-change",
|
||||
"status": state.status.name,
|
||||
"result": state.result.name
|
||||
});
|
||||
});
|
||||
|
||||
liveTest.onError.listen((asyncError) {
|
||||
_send({
|
||||
"type": "error",
|
||||
"error": RemoteException.serialize(
|
||||
asyncError.error,
|
||||
asyncError.stackTrace
|
||||
)
|
||||
});
|
||||
});
|
||||
|
||||
liveTest.onPrint.listen((line) {
|
||||
_send({"type": "print", "line": line});
|
||||
});
|
||||
|
||||
liveTest.run().then((_) {
|
||||
_send({"type": "complete"});
|
||||
_liveTests.remove(liveTest);
|
||||
});
|
||||
} else if (message['command'] == 'close') {
|
||||
if (_liveTests.isNotEmpty)
|
||||
print('closing with ${_liveTests.length} live tests');
|
||||
for (LiveTest liveTest in _liveTests)
|
||||
liveTest.close();
|
||||
_liveTests.clear();
|
||||
} else {
|
||||
print('remote_listener.dart: ignoring command "${message["command"]}" from test harness');
|
||||
}
|
||||
}
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
// Copyright 2015 The Chromium Authors. 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:stack_trace/stack_trace.dart';
|
||||
import 'package:test/src/backend/group.dart';
|
||||
import 'package:test/src/backend/live_test.dart';
|
||||
import 'package:test/src/backend/live_test_controller.dart';
|
||||
import 'package:test/src/backend/metadata.dart';
|
||||
import 'package:test/src/backend/operating_system.dart';
|
||||
import 'package:test/src/backend/state.dart';
|
||||
import 'package:test/src/backend/suite.dart';
|
||||
import 'package:test/src/backend/test.dart';
|
||||
import 'package:test/src/backend/test_platform.dart';
|
||||
import 'package:test/src/util/remote_exception.dart';
|
||||
|
||||
import 'json_socket.dart';
|
||||
|
||||
class RemoteTest extends Test {
|
||||
RemoteTest(this.name, this.metadata, this._socket, this._index);
|
||||
|
||||
final String name;
|
||||
final Metadata metadata;
|
||||
final JSONSocket _socket;
|
||||
final int _index;
|
||||
|
||||
LiveTest load(Suite suite, { Iterable<Group> groups }) {
|
||||
LiveTestController controller;
|
||||
StreamSubscription subscription;
|
||||
|
||||
controller = new LiveTestController(suite, this, () async {
|
||||
|
||||
controller.setState(const State(Status.running, Result.success));
|
||||
_socket.send({'command': 'run', 'index': _index});
|
||||
|
||||
subscription = _socket.stream.listen((message) {
|
||||
if (message['type'] == 'error') {
|
||||
AsyncError asyncError = RemoteException.deserialize(message['error']);
|
||||
controller.addError(asyncError.error, asyncError.stackTrace);
|
||||
} else if (message['type'] == 'state-change') {
|
||||
controller.setState(
|
||||
new State(
|
||||
new Status.parse(message['status']),
|
||||
new Result.parse(message['result'])));
|
||||
} else if (message['type'] == 'print') {
|
||||
controller.print(message['line']);
|
||||
} else {
|
||||
assert(message['type'] == 'complete');
|
||||
subscription.cancel();
|
||||
subscription = null;
|
||||
controller.completer.complete();
|
||||
}
|
||||
});
|
||||
|
||||
_socket.unusualTermination.then((String message) {
|
||||
if (subscription != null) {
|
||||
controller.print('Unexpected subprocess termination: $message');
|
||||
controller.addError(new Exception('Unexpected subprocess termination.'), new Trace.current());
|
||||
controller.setState(new State(Status.complete, Result.error));
|
||||
subscription.cancel();
|
||||
subscription = null;
|
||||
controller.completer.complete();
|
||||
}
|
||||
});
|
||||
|
||||
}, () async {
|
||||
_socket.send({'command': 'close'});
|
||||
if (subscription != null) {
|
||||
subscription.cancel();
|
||||
subscription = null;
|
||||
}
|
||||
}, groups: groups);
|
||||
return controller.liveTest;
|
||||
}
|
||||
|
||||
Test change({String name, Metadata metadata}) {
|
||||
if (name == name && metadata == this.metadata) return this;
|
||||
if (name == null) name = this.name;
|
||||
if (metadata == null) metadata = this.metadata;
|
||||
return new RemoteTest(name, metadata, _socket, _index);
|
||||
}
|
||||
|
||||
// TODO(ianh): Implement this if we need it.
|
||||
Test forPlatform(TestPlatform platform, {OperatingSystem os}) {
|
||||
if (!metadata.testOn.evaluate(platform, os: os))
|
||||
return null;
|
||||
return new RemoteTest(
|
||||
name,
|
||||
metadata.forPlatform(platform, os: os),
|
||||
_socket,
|
||||
_index
|
||||
);
|
||||
}
|
||||
}
|
@ -18,7 +18,7 @@ dependencies:
|
||||
path: ^1.3.0
|
||||
pub_semver: ^1.0.0
|
||||
stack_trace: ^1.4.0
|
||||
test: 0.12.6+1 # see note below
|
||||
test: 0.12.11+1 # see note below
|
||||
yaml: ^2.1.3
|
||||
xml: ^2.4.1
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user