Hixie b43722e79f Handle crashing engine.
When the engine dies unexpectedly during test execution, we have to
terminate any tests running in that engine. Previously, they would just
hang. For some reason that I was never able to satisfactorily explain,
the WebSocket doesn't die in a way I can detect in this case. So
instead, we hand in a future that we only complete when we detect the
server subprocess ends.
2015-11-02 11:19:19 -08:00

95 lines
3.1 KiB
Dart

// 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/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 'package:sky_tools/src/test/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) {
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((_) {
if (subscription != null) {
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;
}
});
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
);
}
}