Default to eagerly closing hybrid isolates. (#513)
Users can pass stayAlive: true to keep them open, but this helps ensure
that they won't accidentally have a bunch of isolates hanging around
between tests.
Closes #109
diff --git a/lib/src/frontend/spawn_hybrid.dart b/lib/src/frontend/spawn_hybrid.dart
index 8e25b4d..8c9b85d 100644
--- a/lib/src/frontend/spawn_hybrid.dart
+++ b/lib/src/frontend/spawn_hybrid.dart
@@ -9,6 +9,7 @@
import 'package:path/path.dart' as p;
import 'package:stream_channel/stream_channel.dart';
+import '../../test.dart';
import '../backend/invoker.dart';
import '../util/remote_exception.dart';
import '../utils.dart';
@@ -79,9 +80,13 @@
/// context, so it can't access test functions like `expect()` and
/// `expectAsync()`.
///
+/// By default, the hybrid isolate is automatically killed when the test
+/// finishes running. If [stayAlive] is `true`, it won't be killed until the
+/// entire test suite finishes running.
+///
/// **Note**: If you use this API, be sure to add a dependency on the
/// **`stream_channel` package, since you're using its API as well!
-StreamChannel spawnHybridUri(uri, {Object message}) {
+StreamChannel spawnHybridUri(uri, {Object message, bool stayAlive: false}) {
Uri parsedUrl;
if (uri is Uri) {
parsedUrl = uri;
@@ -101,7 +106,7 @@
absoluteUri = uri.toString();
}
- return _spawn(absoluteUri, message);
+ return _spawn(absoluteUri, message, stayAlive: stayAlive);
}
/// Spawns a VM isolate that runs the given [dartCode], which is loaded as the
@@ -138,17 +143,22 @@
/// context, so it can't access test functions like `expect()` and
/// `expectAsync()`.
///
+/// By default, the hybrid isolate is automatically killed when the test
+/// finishes running. If [stayAlive] is `true`, it won't be killed until the
+/// entire test suite finishes running.
+///
/// **Note**: If you use this API, be sure to add a dependency on the
/// **`stream_channel` package, since you're using its API as well!
-StreamChannel spawnHybridCode(String dartCode, {Object message}) {
+StreamChannel spawnHybridCode(String dartCode, {Object message,
+ bool stayAlive: false}) {
var uri = new Uri.dataFromString(dartCode,
encoding: UTF8, mimeType: 'application/dart');
- return _spawn(uri.toString(), message);
+ return _spawn(uri.toString(), message, stayAlive: stayAlive);
}
/// Like [spawnHybridUri], but doesn't take [Uri] objects and doesn't handle
/// relative URLs.
-StreamChannel _spawn(String uri, Object message) {
+StreamChannel _spawn(String uri, Object message, {bool stayAlive: false}) {
var channel = Zone.current[#test.runner.test_channel] as MultiChannel;
if (channel == null) {
// TODO(nweiz): Link to an issue tracking support when running the test file
@@ -168,5 +178,11 @@
"channel": isolateChannel.id
});
+ if (!stayAlive) {
+ var disconnector = new Disconnector();
+ addTearDown(() => disconnector.disconnect());
+ isolateChannel = isolateChannel.transform(disconnector);
+ }
+
return isolateChannel.transform(_transformer);
}
diff --git a/pubspec.yaml b/pubspec.yaml
index 0f8b41e..927a8d3 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
name: test
-version: 0.12.18-dev
+version: 0.12.18
author: Dart Team <misc@dartlang.org>
description: A library for writing dart unit tests.
homepage: https://github.com/dart-lang/test
diff --git a/test/runner/hybrid_test.dart b/test/runner/hybrid_test.dart
index e455116..a118564 100644
--- a/test/runner/hybrid_test.dart
+++ b/test/runner/hybrid_test.dart
@@ -11,10 +11,15 @@
import 'package:scheduled_test/descriptor.dart' as d;
import 'package:scheduled_test/scheduled_test.dart';
-
import '../io.dart';
void main() {
+ String packageRoot;
+ setUpAll(() async {
+ packageRoot = p.absolute(p.dirname(p.fromUri(
+ await Isolate.resolvePackageUri(Uri.parse("package:test/")))));
+ });
+
useSandbox();
group("spawnHybridUri():", () {
@@ -250,53 +255,6 @@
completion(equals({"a": "b"})));
});
- test("persists across multiple tests", () {
- d.file("test.dart", """
- import "dart:async";
-
- import "package:async/async.dart";
- import "package:stream_channel/stream_channel.dart";
-
- import "package:test/test.dart";
-
- void main() {
- StreamQueue queue;
- StreamSink sink;
- setUpAll(() {
- var channel = spawnHybridCode('''
- import "package:stream_channel/stream_channel.dart";
-
- void hybridMain(StreamChannel channel) {
- channel.stream.listen((message) {
- channel.sink.add(message);
- });
- }
- ''');
- queue = new StreamQueue(channel.stream);
- sink = channel.sink;
- });
-
- test("echoes a number", () {
- expect(queue.next, completion(equals(123)));
- sink.add(123);
- });
-
- test("echoes a string", () {
- expect(queue.next, completion(equals("wow")));
- sink.add("wow");
- });
- }
- """).create();
-
- var test = runTest(["-p", "content-shell", "test.dart"]);
- test.stdout.expect(containsInOrder([
- "+0: echoes a number",
- "+1: echoes a string",
- "+2: All tests passed!"
- ]));
- test.shouldExit(0);
- }, tags: ['content-shell']);
-
test("allows the hybrid isolate to send errors across the stream channel",
() {
var channel = spawnHybridCode("""
@@ -500,5 +458,87 @@
expect(channel.stream.toList(), completion(isEmpty));
});
+
+ test("closes the channel when the test finishes by default", () {
+ d.file("test.dart", """
+ import "package:stream_channel/stream_channel.dart";
+ import "package:test/test.dart";
+
+ import "${p.toUri(packageRoot)}/test/utils.dart";
+
+ void main() {
+ StreamChannel channel;
+ test("test 1", () {
+ channel = spawnHybridCode('''
+ import "package:stream_channel/stream_channel.dart";
+
+ void hybridMain(StreamChannel channel) {}
+ ''');
+ });
+
+ test("test 2", () async {
+ var isDone = false;
+ channel.stream.listen(null, onDone: () => isDone = true);
+ await pumpEventQueue();
+ expect(isDone, isTrue);
+ });
+ }
+ """).create();
+
+ var test = runTest(["test.dart"]);
+ test.stdout.expect(containsInOrder([
+ "+0: test 1",
+ "+1: test 2",
+ "+2: All tests passed!"
+ ]));
+ test.shouldExit(0);
+ });
+
+ test("persists across multiple tests with stayAlive: true", () {
+ d.file("test.dart", """
+ import "dart:async";
+
+ import "package:async/async.dart";
+ import "package:stream_channel/stream_channel.dart";
+
+ import "package:test/test.dart";
+
+ void main() {
+ StreamQueue queue;
+ StreamSink sink;
+ setUpAll(() {
+ var channel = spawnHybridCode('''
+ import "package:stream_channel/stream_channel.dart";
+
+ void hybridMain(StreamChannel channel) {
+ channel.stream.listen((message) {
+ channel.sink.add(message);
+ });
+ }
+ ''', stayAlive: true);
+ queue = new StreamQueue(channel.stream);
+ sink = channel.sink;
+ });
+
+ test("echoes a number", () {
+ expect(queue.next, completion(equals(123)));
+ sink.add(123);
+ });
+
+ test("echoes a string", () {
+ expect(queue.next, completion(equals("wow")));
+ sink.add("wow");
+ });
+ }
+ """).create();
+
+ var test = runTest(["test.dart"]);
+ test.stdout.expect(containsInOrder([
+ "+0: echoes a number",
+ "+1: echoes a string",
+ "+2: All tests passed!"
+ ]));
+ test.shouldExit(0);
+ });
});
}