Skip to content

Commit 93c1ad0

Browse files
authored
Flexible Coverage API (#1151)
* Flexible Coverage API
1 parent 50b7d68 commit 93c1ad0

File tree

10 files changed

+74
-53
lines changed

10 files changed

+74
-53
lines changed

pkgs/test/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ dependencies:
3131
yaml: ^2.0.0
3232
# Use an exact version until the test_api and test_core package are stable.
3333
test_api: 0.2.14
34-
test_core: 0.2.19
34+
test_core: 0.3.0
3535

3636
dev_dependencies:
3737
fake_async: ^1.0.0

pkgs/test_core/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
## 0.2.19-dev
1+
## 0.3.0-dev
22

33
* Bump minimum SDK to `2.4.0` for safer usage of for-loop elements.
44
* Deprecate `PhantomJS` and provide warning when used. Support for `PhantomJS`
55
will be removed in version `2.0.0`.
66
* Differentiate between test-randomize-ordering-seed not set and 0 being chosen
77
as the random seed.
8+
* `deserializeSuite` now takes an optional `gatherCoverage` callback.
89

910
## 0.2.18
1011

pkgs/test_core/lib/src/runner/coverage.dart

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,20 @@
55
import 'dart:convert';
66
import 'dart:io';
77

8-
import 'package:coverage/coverage.dart';
98
import 'package:path/path.dart' as p;
109

1110
import 'live_suite_controller.dart';
1211

13-
/// Collects coverage and outputs to the [coverage] path.
14-
Future<void> gatherCoverage(
15-
String coverage, LiveSuiteController controller) async {
16-
final suite = controller.liveSuite.suite;
17-
18-
if (!suite.platform.runtime.isDartVM) return;
19-
20-
final isolateId = Uri.parse(suite.environment.observatoryUrl.fragment)
21-
.queryParameters['isolateId'];
22-
23-
final cov = await collect(
24-
suite.environment.observatoryUrl, false, false, false, {},
25-
isolateIds: {isolateId});
26-
27-
final outfile = File(p.join('$coverage', '${suite.path}.vm.json'))
12+
/// Collects coverage and outputs to the [coveragePath] path.
13+
Future<void> writeCoverage(
14+
String coveragePath, LiveSuiteController controller) async {
15+
var suite = controller.liveSuite.suite;
16+
var coverage = await controller.liveSuite.suite.gatherCoverage();
17+
final outfile = File(p.join(coveragePath,
18+
'${suite.path}.${suite.platform.runtime.name.toLowerCase()}.json'))
2819
..createSync(recursive: true);
2920
final out = outfile.openWrite();
30-
out.write(json.encode(cov));
21+
out.write(json.encode(coverage));
3122
await out.flush();
3223
await out.close();
3324
}

pkgs/test_core/lib/src/runner/coverage_stub.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import 'live_suite_controller.dart';
66

7-
Future<void> gatherCoverage(String coverage, LiveSuiteController controller) =>
7+
Future<void> writeCoverage(
8+
String coveragePath, LiveSuiteController controller) =>
89
throw UnsupportedError(
910
'Coverage is only supported through the test runner.');

pkgs/test_core/lib/src/runner/engine.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ class Engine {
285285
if (_closed) return;
286286
await _runGroup(controller, controller.liveSuite.suite.group, []);
287287
controller.noMoreLiveTests();
288-
if (_coverage != null) await gatherCoverage(_coverage, controller);
288+
if (_coverage != null) await writeCoverage(_coverage, controller);
289289
loadResource.allowRelease(() => controller.close());
290290
});
291291
}());

pkgs/test_core/lib/src/runner/load_suite.dart

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,23 @@ import 'dart:async';
66

77
import 'package:stack_trace/stack_trace.dart';
88
import 'package:stream_channel/stream_channel.dart';
9-
109
import 'package:test_api/src/backend/group.dart'; // ignore: implementation_imports
1110
import 'package:test_api/src/backend/invoker.dart'; // ignore: implementation_imports
1211
import 'package:test_api/src/backend/metadata.dart'; // ignore: implementation_imports
12+
import 'package:test_api/src/backend/runtime.dart'; // ignore: implementation_imports
1313
import 'package:test_api/src/backend/suite.dart'; // ignore: implementation_imports
1414
import 'package:test_api/src/backend/suite_platform.dart'; // ignore: implementation_imports
1515
import 'package:test_api/src/backend/test.dart'; // ignore: implementation_imports
16-
import 'package:test_api/src/backend/runtime.dart'; // ignore: implementation_imports
1716
import 'package:test_api/src/utils.dart'; // ignore: implementation_imports
1817

19-
import 'runner_suite.dart';
20-
import 'suite.dart';
21-
2218
import '../../test_core.dart';
23-
24-
// ignore: uri_does_not_exist
2519
import '../util/io_stub.dart'
2620
// ignore: uri_does_not_exist
2721
if (dart.library.io) '../util/io.dart';
2822
import 'load_exception.dart';
2923
import 'plugin/environment.dart';
24+
import 'runner_suite.dart';
25+
import 'suite.dart';
3026

3127
/// The timeout for loading a test suite.
3228
///
@@ -214,4 +210,8 @@ class LoadSuite extends Suite implements RunnerSuite {
214210

215211
@override
216212
Future close() async {}
213+
214+
@override
215+
Future<Map<String, dynamic>> gatherCoverage() =>
216+
throw UnsupportedError('Coverage is not supported for LoadSuite tests.');
217217
}

pkgs/test_core/lib/src/runner/plugin/platform_helpers.dart

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,18 @@ import 'dart:io';
77

88
import 'package:stack_trace/stack_trace.dart';
99
import 'package:stream_channel/stream_channel.dart';
10-
1110
import 'package:test_api/src/backend/group.dart'; // ignore: implementation_imports
1211
import 'package:test_api/src/backend/metadata.dart'; // ignore: implementation_imports
1312
import 'package:test_api/src/backend/suite_platform.dart'; // ignore: implementation_imports
1413
import 'package:test_api/src/backend/test.dart'; // ignore: implementation_imports
1514
import 'package:test_api/src/util/remote_exception.dart'; // ignore: implementation_imports
1615

17-
import '../runner_suite.dart';
18-
import '../environment.dart';
19-
import '../suite.dart';
2016
import '../configuration.dart';
17+
import '../environment.dart';
2118
import '../load_exception.dart';
19+
import '../runner_suite.dart';
2220
import '../runner_test.dart';
21+
import '../suite.dart';
2322

2423
/// A helper method for creating a [RunnerSuiteController] containing tests
2524
/// that communicate over [channel].
@@ -35,13 +34,17 @@ import '../runner_test.dart';
3534
///
3635
/// If [mapper] is passed, it will be used to adjust stack traces for any errors
3736
/// emitted by tests.
37+
///
38+
/// [gatherCoverage] is a callback which returns a hit-map containing merged
39+
/// coverage report suitable for use with `package:coverage`.
3840
RunnerSuiteController deserializeSuite(
3941
String path,
4042
SuitePlatform platform,
4143
SuiteConfiguration suiteConfig,
4244
Environment environment,
4345
StreamChannel channel,
44-
Object message) {
46+
Object message,
47+
{Future<Map<String, dynamic>> Function() gatherCoverage}) {
4548
var disconnector = Disconnector();
4649
var suiteChannel = MultiChannel(channel.transform(disconnector));
4750

@@ -110,7 +113,8 @@ RunnerSuiteController deserializeSuite(
110113
return RunnerSuiteController(
111114
environment, suiteConfig, suiteChannel, completer.future, platform,
112115
path: path,
113-
onClose: () => disconnector.disconnect().catchError(handleError));
116+
onClose: () => disconnector.disconnect().catchError(handleError),
117+
gatherCoverage: gatherCoverage);
114118
}
115119

116120
/// A utility class for storing state while deserializing tests.

pkgs/test_core/lib/src/runner/runner_suite.dart

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,13 @@ import 'dart:async';
66

77
import 'package:async/async.dart';
88
import 'package:stream_channel/stream_channel.dart';
9-
109
import 'package:test_api/src/backend/group.dart'; // ignore: implementation_imports
1110
import 'package:test_api/src/backend/suite.dart'; // ignore: implementation_imports
1211
import 'package:test_api/src/backend/suite_platform.dart'; // ignore: implementation_imports
1312
import 'package:test_api/src/backend/test.dart'; // ignore: implementation_imports
1413

15-
import 'suite.dart';
1614
import 'environment.dart';
15+
import 'suite.dart';
1716

1817
/// A suite produced and consumed by the test runner that has runner-specific
1918
/// logic and lifecycle management.
@@ -77,6 +76,13 @@ class RunnerSuite extends Suite {
7776

7877
/// Closes the suite and releases any resources associated with it.
7978
Future close() => _controller._close();
79+
80+
/// Collects a hit-map containing merged coverage.
81+
///
82+
/// Result is suitable for input to the coverage formatters provided by
83+
/// `package:coverage`.
84+
Future<Map<String, dynamic>> gatherCoverage() async =>
85+
(await _controller._gatherCoverage?.call()) ?? {};
8086
}
8187

8288
/// A class that exposes and controls a [RunnerSuite].
@@ -106,20 +112,28 @@ class RunnerSuiteController {
106112
/// The channel names that have already been used.
107113
final _channelNames = <String>{};
108114

115+
/// Collects a hit-map containing merged coverage.
116+
final Future<Map<String, dynamic>> Function() _gatherCoverage;
117+
109118
RunnerSuiteController(this._environment, this._config, this._suiteChannel,
110119
Future<Group> groupFuture, SuitePlatform platform,
111-
{String path, Function() onClose})
112-
: _onClose = onClose {
120+
{String path,
121+
Function() onClose,
122+
Future<Map<String, dynamic>> Function() gatherCoverage})
123+
: _onClose = onClose,
124+
_gatherCoverage = gatherCoverage {
113125
_suite =
114126
groupFuture.then((group) => RunnerSuite._(this, group, path, platform));
115127
}
116128

117129
/// Used by [new RunnerSuite] to create a runner suite that's not loaded from
118130
/// an external source.
119131
RunnerSuiteController._local(this._environment, this._config,
120-
{Function() onClose})
132+
{Function() onClose,
133+
Future<Map<String, dynamic>> Function() gatherCoverage})
121134
: _suiteChannel = null,
122-
_onClose = onClose;
135+
_onClose = onClose,
136+
_gatherCoverage = gatherCoverage;
123137

124138
/// Sets whether the suite is paused for debugging.
125139
///

pkgs/test_core/lib/src/runner/vm/platform.dart

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,23 @@ import 'dart:developer';
77
import 'dart:io';
88
import 'dart:isolate';
99

10+
import 'package:coverage/coverage.dart';
1011
import 'package:path/path.dart' as p;
1112
import 'package:stream_channel/isolate_channel.dart';
1213
import 'package:stream_channel/stream_channel.dart';
1314
import 'package:test_api/src/backend/runtime.dart'; // ignore: implementation_imports
1415
import 'package:test_api/src/backend/suite_platform.dart'; // ignore: implementation_imports
15-
import 'package:test_core/src/runner/configuration.dart'; // ignore: implementation_imports
16-
import 'package:test_core/src/runner/load_exception.dart'; // ignore: implementation_imports
17-
import 'package:test_core/src/runner/platform.dart'; // ignore: implementation_imports
18-
import 'package:test_core/src/runner/plugin/environment.dart'; // ignore: implementation_imports
19-
import 'package:test_core/src/runner/plugin/platform_helpers.dart'; // ignore: implementation_imports
20-
import 'package:test_core/src/runner/runner_suite.dart'; // ignore: implementation_imports
21-
import 'package:test_core/src/runner/suite.dart'; // ignore: implementation_imports
22-
import 'package:test_core/src/util/dart.dart' // ignore: implementation_imports
23-
as dart;
2416
import 'package:vm_service/vm_service.dart' hide Isolate;
2517
import 'package:vm_service/vm_service_io.dart';
2618

19+
import '../../runner/configuration.dart';
20+
import '../../runner/environment.dart';
21+
import '../../runner/load_exception.dart';
22+
import '../../runner/platform.dart';
23+
import '../../runner/plugin/platform_helpers.dart';
24+
import '../../runner/runner_suite.dart';
25+
import '../../runner/suite.dart';
26+
import '../../util/dart.dart' as dart;
2727
import 'environment.dart';
2828

2929
/// A platform that loads tests in isolates spawned within this Dart process.
@@ -61,7 +61,7 @@ class VMPlatform extends PlatformPlugin {
6161
sink.close();
6262
}));
6363

64-
VMEnvironment environment;
64+
Environment environment;
6565
IsolateRef isolateRef;
6666
if (_config.debug) {
6767
// Print an empty line because the VM prints an "Observatory listening on"
@@ -87,8 +87,11 @@ class VMPlatform extends PlatformPlugin {
8787
environment = VMEnvironment(url, isolateRef, client);
8888
}
8989

90-
var controller = deserializeSuite(path, platform, suiteConfig,
91-
environment ?? PluginEnvironment(), channel, message);
90+
environment ??= PluginEnvironment();
91+
92+
var controller = deserializeSuite(
93+
path, platform, suiteConfig, environment, channel, message,
94+
gatherCoverage: () => _gatherCoverage(environment));
9295

9396
if (isolateRef != null) {
9497
await client.streamListen('Debug');
@@ -152,6 +155,13 @@ Future<Isolate> _spawnPrecompiledIsolate(
152155
checked: true);
153156
}
154157

158+
Future<Map<String, dynamic>> _gatherCoverage(Environment environment) async {
159+
final isolateId = Uri.parse(environment.observatoryUrl.fragment)
160+
.queryParameters['isolateId'];
161+
return await collect(environment.observatoryUrl, false, false, false, {},
162+
isolateIds: {isolateId});
163+
}
164+
155165
Future<Isolate> _spawnPubServeIsolate(
156166
String testPath, SendPort message, Uri pubServeUrl) async {
157167
var url = pubServeUrl.resolveUri(

pkgs/test_core/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: test_core
2-
version: 0.2.19-dev
2+
version: 0.3.0-dev
33
description: A basic library for writing tests and running them on the VM.
44
homepage: https://github.com/dart-lang/test/blob/master/pkgs/test_core
55

0 commit comments

Comments
 (0)