Flexible Coverage API (#1151)
* Flexible Coverage API
diff --git a/pkgs/test/pubspec.yaml b/pkgs/test/pubspec.yaml
index 418f312..b1abea3 100644
--- a/pkgs/test/pubspec.yaml
+++ b/pkgs/test/pubspec.yaml
@@ -31,7 +31,7 @@
yaml: ^2.0.0
# Use an exact version until the test_api and test_core package are stable.
test_api: 0.2.14
- test_core: 0.2.19
+ test_core: 0.3.0
dev_dependencies:
fake_async: ^1.0.0
diff --git a/pkgs/test_core/CHANGELOG.md b/pkgs/test_core/CHANGELOG.md
index f28a372..f16d824 100644
--- a/pkgs/test_core/CHANGELOG.md
+++ b/pkgs/test_core/CHANGELOG.md
@@ -1,10 +1,11 @@
-## 0.2.19-dev
+## 0.3.0-dev
* Bump minimum SDK to `2.4.0` for safer usage of for-loop elements.
* Deprecate `PhantomJS` and provide warning when used. Support for `PhantomJS`
will be removed in version `2.0.0`.
* Differentiate between test-randomize-ordering-seed not set and 0 being chosen
as the random seed.
+* `deserializeSuite` now takes an optional `gatherCoverage` callback.
## 0.2.18
diff --git a/pkgs/test_core/lib/src/runner/coverage.dart b/pkgs/test_core/lib/src/runner/coverage.dart
index 200019b..841bdd7 100644
--- a/pkgs/test_core/lib/src/runner/coverage.dart
+++ b/pkgs/test_core/lib/src/runner/coverage.dart
@@ -5,29 +5,20 @@
import 'dart:convert';
import 'dart:io';
-import 'package:coverage/coverage.dart';
import 'package:path/path.dart' as p;
import 'live_suite_controller.dart';
-/// Collects coverage and outputs to the [coverage] path.
-Future<void> gatherCoverage(
- String coverage, LiveSuiteController controller) async {
- final suite = controller.liveSuite.suite;
-
- if (!suite.platform.runtime.isDartVM) return;
-
- final isolateId = Uri.parse(suite.environment.observatoryUrl.fragment)
- .queryParameters['isolateId'];
-
- final cov = await collect(
- suite.environment.observatoryUrl, false, false, false, {},
- isolateIds: {isolateId});
-
- final outfile = File(p.join('$coverage', '${suite.path}.vm.json'))
+/// Collects coverage and outputs to the [coveragePath] path.
+Future<void> writeCoverage(
+ String coveragePath, LiveSuiteController controller) async {
+ var suite = controller.liveSuite.suite;
+ var coverage = await controller.liveSuite.suite.gatherCoverage();
+ final outfile = File(p.join(coveragePath,
+ '${suite.path}.${suite.platform.runtime.name.toLowerCase()}.json'))
..createSync(recursive: true);
final out = outfile.openWrite();
- out.write(json.encode(cov));
+ out.write(json.encode(coverage));
await out.flush();
await out.close();
}
diff --git a/pkgs/test_core/lib/src/runner/coverage_stub.dart b/pkgs/test_core/lib/src/runner/coverage_stub.dart
index 5a2285f..64f69c7 100644
--- a/pkgs/test_core/lib/src/runner/coverage_stub.dart
+++ b/pkgs/test_core/lib/src/runner/coverage_stub.dart
@@ -4,6 +4,7 @@
import 'live_suite_controller.dart';
-Future<void> gatherCoverage(String coverage, LiveSuiteController controller) =>
+Future<void> writeCoverage(
+ String coveragePath, LiveSuiteController controller) =>
throw UnsupportedError(
'Coverage is only supported through the test runner.');
diff --git a/pkgs/test_core/lib/src/runner/engine.dart b/pkgs/test_core/lib/src/runner/engine.dart
index e4a7fa4..d944e50 100644
--- a/pkgs/test_core/lib/src/runner/engine.dart
+++ b/pkgs/test_core/lib/src/runner/engine.dart
@@ -285,7 +285,7 @@
if (_closed) return;
await _runGroup(controller, controller.liveSuite.suite.group, []);
controller.noMoreLiveTests();
- if (_coverage != null) await gatherCoverage(_coverage, controller);
+ if (_coverage != null) await writeCoverage(_coverage, controller);
loadResource.allowRelease(() => controller.close());
});
}());
diff --git a/pkgs/test_core/lib/src/runner/load_suite.dart b/pkgs/test_core/lib/src/runner/load_suite.dart
index a8d2825..fd6dfb0 100644
--- a/pkgs/test_core/lib/src/runner/load_suite.dart
+++ b/pkgs/test_core/lib/src/runner/load_suite.dart
@@ -6,27 +6,23 @@
import 'package:stack_trace/stack_trace.dart';
import 'package:stream_channel/stream_channel.dart';
-
import 'package:test_api/src/backend/group.dart'; // ignore: implementation_imports
import 'package:test_api/src/backend/invoker.dart'; // ignore: implementation_imports
import 'package:test_api/src/backend/metadata.dart'; // ignore: implementation_imports
+import 'package:test_api/src/backend/runtime.dart'; // ignore: implementation_imports
import 'package:test_api/src/backend/suite.dart'; // ignore: implementation_imports
import 'package:test_api/src/backend/suite_platform.dart'; // ignore: implementation_imports
import 'package:test_api/src/backend/test.dart'; // ignore: implementation_imports
-import 'package:test_api/src/backend/runtime.dart'; // ignore: implementation_imports
import 'package:test_api/src/utils.dart'; // ignore: implementation_imports
-import 'runner_suite.dart';
-import 'suite.dart';
-
import '../../test_core.dart';
-
-// ignore: uri_does_not_exist
import '../util/io_stub.dart'
// ignore: uri_does_not_exist
if (dart.library.io) '../util/io.dart';
import 'load_exception.dart';
import 'plugin/environment.dart';
+import 'runner_suite.dart';
+import 'suite.dart';
/// The timeout for loading a test suite.
///
@@ -214,4 +210,8 @@
@override
Future close() async {}
+
+ @override
+ Future<Map<String, dynamic>> gatherCoverage() =>
+ throw UnsupportedError('Coverage is not supported for LoadSuite tests.');
}
diff --git a/pkgs/test_core/lib/src/runner/plugin/platform_helpers.dart b/pkgs/test_core/lib/src/runner/plugin/platform_helpers.dart
index c63967c..bd11058 100644
--- a/pkgs/test_core/lib/src/runner/plugin/platform_helpers.dart
+++ b/pkgs/test_core/lib/src/runner/plugin/platform_helpers.dart
@@ -7,19 +7,18 @@
import 'package:stack_trace/stack_trace.dart';
import 'package:stream_channel/stream_channel.dart';
-
import 'package:test_api/src/backend/group.dart'; // ignore: implementation_imports
import 'package:test_api/src/backend/metadata.dart'; // ignore: implementation_imports
import 'package:test_api/src/backend/suite_platform.dart'; // ignore: implementation_imports
import 'package:test_api/src/backend/test.dart'; // ignore: implementation_imports
import 'package:test_api/src/util/remote_exception.dart'; // ignore: implementation_imports
-import '../runner_suite.dart';
-import '../environment.dart';
-import '../suite.dart';
import '../configuration.dart';
+import '../environment.dart';
import '../load_exception.dart';
+import '../runner_suite.dart';
import '../runner_test.dart';
+import '../suite.dart';
/// A helper method for creating a [RunnerSuiteController] containing tests
/// that communicate over [channel].
@@ -35,13 +34,17 @@
///
/// If [mapper] is passed, it will be used to adjust stack traces for any errors
/// emitted by tests.
+///
+/// [gatherCoverage] is a callback which returns a hit-map containing merged
+/// coverage report suitable for use with `package:coverage`.
RunnerSuiteController deserializeSuite(
String path,
SuitePlatform platform,
SuiteConfiguration suiteConfig,
Environment environment,
StreamChannel channel,
- Object message) {
+ Object message,
+ {Future<Map<String, dynamic>> Function() gatherCoverage}) {
var disconnector = Disconnector();
var suiteChannel = MultiChannel(channel.transform(disconnector));
@@ -110,7 +113,8 @@
return RunnerSuiteController(
environment, suiteConfig, suiteChannel, completer.future, platform,
path: path,
- onClose: () => disconnector.disconnect().catchError(handleError));
+ onClose: () => disconnector.disconnect().catchError(handleError),
+ gatherCoverage: gatherCoverage);
}
/// A utility class for storing state while deserializing tests.
diff --git a/pkgs/test_core/lib/src/runner/runner_suite.dart b/pkgs/test_core/lib/src/runner/runner_suite.dart
index 8470f62..1b166d3 100644
--- a/pkgs/test_core/lib/src/runner/runner_suite.dart
+++ b/pkgs/test_core/lib/src/runner/runner_suite.dart
@@ -6,14 +6,13 @@
import 'package:async/async.dart';
import 'package:stream_channel/stream_channel.dart';
-
import 'package:test_api/src/backend/group.dart'; // ignore: implementation_imports
import 'package:test_api/src/backend/suite.dart'; // ignore: implementation_imports
import 'package:test_api/src/backend/suite_platform.dart'; // ignore: implementation_imports
import 'package:test_api/src/backend/test.dart'; // ignore: implementation_imports
-import 'suite.dart';
import 'environment.dart';
+import 'suite.dart';
/// A suite produced and consumed by the test runner that has runner-specific
/// logic and lifecycle management.
@@ -77,6 +76,13 @@
/// Closes the suite and releases any resources associated with it.
Future close() => _controller._close();
+
+ /// Collects a hit-map containing merged coverage.
+ ///
+ /// Result is suitable for input to the coverage formatters provided by
+ /// `package:coverage`.
+ Future<Map<String, dynamic>> gatherCoverage() async =>
+ (await _controller._gatherCoverage?.call()) ?? {};
}
/// A class that exposes and controls a [RunnerSuite].
@@ -106,10 +112,16 @@
/// The channel names that have already been used.
final _channelNames = <String>{};
+ /// Collects a hit-map containing merged coverage.
+ final Future<Map<String, dynamic>> Function() _gatherCoverage;
+
RunnerSuiteController(this._environment, this._config, this._suiteChannel,
Future<Group> groupFuture, SuitePlatform platform,
- {String path, Function() onClose})
- : _onClose = onClose {
+ {String path,
+ Function() onClose,
+ Future<Map<String, dynamic>> Function() gatherCoverage})
+ : _onClose = onClose,
+ _gatherCoverage = gatherCoverage {
_suite =
groupFuture.then((group) => RunnerSuite._(this, group, path, platform));
}
@@ -117,9 +129,11 @@
/// Used by [new RunnerSuite] to create a runner suite that's not loaded from
/// an external source.
RunnerSuiteController._local(this._environment, this._config,
- {Function() onClose})
+ {Function() onClose,
+ Future<Map<String, dynamic>> Function() gatherCoverage})
: _suiteChannel = null,
- _onClose = onClose;
+ _onClose = onClose,
+ _gatherCoverage = gatherCoverage;
/// Sets whether the suite is paused for debugging.
///
diff --git a/pkgs/test_core/lib/src/runner/vm/platform.dart b/pkgs/test_core/lib/src/runner/vm/platform.dart
index 34853ea..12bf36c 100644
--- a/pkgs/test_core/lib/src/runner/vm/platform.dart
+++ b/pkgs/test_core/lib/src/runner/vm/platform.dart
@@ -7,23 +7,23 @@
import 'dart:io';
import 'dart:isolate';
+import 'package:coverage/coverage.dart';
import 'package:path/path.dart' as p;
import 'package:stream_channel/isolate_channel.dart';
import 'package:stream_channel/stream_channel.dart';
import 'package:test_api/src/backend/runtime.dart'; // ignore: implementation_imports
import 'package:test_api/src/backend/suite_platform.dart'; // ignore: implementation_imports
-import 'package:test_core/src/runner/configuration.dart'; // ignore: implementation_imports
-import 'package:test_core/src/runner/load_exception.dart'; // ignore: implementation_imports
-import 'package:test_core/src/runner/platform.dart'; // ignore: implementation_imports
-import 'package:test_core/src/runner/plugin/environment.dart'; // ignore: implementation_imports
-import 'package:test_core/src/runner/plugin/platform_helpers.dart'; // ignore: implementation_imports
-import 'package:test_core/src/runner/runner_suite.dart'; // ignore: implementation_imports
-import 'package:test_core/src/runner/suite.dart'; // ignore: implementation_imports
-import 'package:test_core/src/util/dart.dart' // ignore: implementation_imports
- as dart;
import 'package:vm_service/vm_service.dart' hide Isolate;
import 'package:vm_service/vm_service_io.dart';
+import '../../runner/configuration.dart';
+import '../../runner/environment.dart';
+import '../../runner/load_exception.dart';
+import '../../runner/platform.dart';
+import '../../runner/plugin/platform_helpers.dart';
+import '../../runner/runner_suite.dart';
+import '../../runner/suite.dart';
+import '../../util/dart.dart' as dart;
import 'environment.dart';
/// A platform that loads tests in isolates spawned within this Dart process.
@@ -61,7 +61,7 @@
sink.close();
}));
- VMEnvironment environment;
+ Environment environment;
IsolateRef isolateRef;
if (_config.debug) {
// Print an empty line because the VM prints an "Observatory listening on"
@@ -87,8 +87,11 @@
environment = VMEnvironment(url, isolateRef, client);
}
- var controller = deserializeSuite(path, platform, suiteConfig,
- environment ?? PluginEnvironment(), channel, message);
+ environment ??= PluginEnvironment();
+
+ var controller = deserializeSuite(
+ path, platform, suiteConfig, environment, channel, message,
+ gatherCoverage: () => _gatherCoverage(environment));
if (isolateRef != null) {
await client.streamListen('Debug');
@@ -152,6 +155,13 @@
checked: true);
}
+Future<Map<String, dynamic>> _gatherCoverage(Environment environment) async {
+ final isolateId = Uri.parse(environment.observatoryUrl.fragment)
+ .queryParameters['isolateId'];
+ return await collect(environment.observatoryUrl, false, false, false, {},
+ isolateIds: {isolateId});
+}
+
Future<Isolate> _spawnPubServeIsolate(
String testPath, SendPort message, Uri pubServeUrl) async {
var url = pubServeUrl.resolveUri(
diff --git a/pkgs/test_core/pubspec.yaml b/pkgs/test_core/pubspec.yaml
index b2455f6..73c227e 100644
--- a/pkgs/test_core/pubspec.yaml
+++ b/pkgs/test_core/pubspec.yaml
@@ -1,5 +1,5 @@
name: test_core
-version: 0.2.19-dev
+version: 0.3.0-dev
description: A basic library for writing tests and running them on the VM.
homepage: https://github.com/dart-lang/test/blob/master/pkgs/test_core