Unverified Commit 1bf6f023 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] support coverage and machine together (#51988)

parent bbf913bc
......@@ -208,24 +208,21 @@ class TestCommand extends FlutterCommand {
];
}
final bool machine = boolArg('machine');
CoverageCollector collector;
if (boolArg('coverage') || boolArg('merge-coverage')) {
final String projectName = FlutterProject.current().manifest.appName;
collector = CoverageCollector(
verbose: !machine,
libraryPredicate: (String libraryName) => libraryName.contains(projectName),
);
}
final bool machine = boolArg('machine');
if (collector != null && machine) {
throwToolExit("The test command doesn't support --machine and coverage together");
}
TestWatcher watcher;
if (collector != null) {
if (machine) {
watcher = EventPrinter(parent: collector);
} else if (collector != null) {
watcher = collector;
} else if (machine) {
watcher = EventPrinter();
}
Cache.releaseLockEarly();
......
......@@ -8,7 +8,6 @@ import 'package:coverage/coverage.dart' as coverage;
import '../base/file_system.dart';
import '../base/io.dart';
import '../base/logger.dart';
import '../base/process.dart';
import '../base/utils.dart';
import '../dart/package_map.dart';
......@@ -19,17 +18,29 @@ import 'watcher.dart';
/// A class that's used to collect coverage data during tests.
class CoverageCollector extends TestWatcher {
CoverageCollector({this.libraryPredicate});
CoverageCollector({this.libraryPredicate, this.verbose = true});
final bool verbose;
Map<String, Map<int, int>> _globalHitmap;
bool Function(String) libraryPredicate;
@override
Future<void> handleFinishedTest(ProcessEvent event) async {
globals.printTrace('test ${event.childIndex}: collecting coverage');
_logMessage('test ${event.childIndex}: collecting coverage');
await collectCoverage(event.process, event.observatoryUri);
}
void _logMessage(String line, { bool error = false }) {
if (!verbose) {
return;
}
if (error) {
globals.printError(line);
} else {
globals.printTrace(line);
}
}
void _addHitmap(Map<String, Map<int, int>> hitmap) {
if (_globalHitmap == null) {
_globalHitmap = hitmap;
......@@ -46,16 +57,16 @@ class CoverageCollector extends TestWatcher {
/// The returned [Future] completes when the coverage is collected.
Future<void> collectCoverageIsolate(Uri observatoryUri) async {
assert(observatoryUri != null);
print('collecting coverage data from $observatoryUri...');
_logMessage('collecting coverage data from $observatoryUri...');
final Map<String, dynamic> data = await collect(observatoryUri, libraryPredicate);
if (data == null) {
throw Exception('Failed to collect coverage.');
}
assert(data != null);
print('($observatoryUri): collected coverage data; merging...');
_logMessage('($observatoryUri): collected coverage data; merging...');
_addHitmap(coverage.createHitmap(data['coverage'] as List<Map<String, dynamic>>));
print('($observatoryUri): done merging coverage data into global coverage map.');
_logMessage('($observatoryUri): done merging coverage data into global coverage map.');
}
/// Collects coverage for the given [Process] using the given `port`.
......@@ -68,7 +79,7 @@ class CoverageCollector extends TestWatcher {
assert(process != null);
assert(observatoryUri != null);
final int pid = process.pid;
globals.printTrace('pid $pid: collecting coverage data from $observatoryUri...');
_logMessage('pid $pid: collecting coverage data from $observatoryUri...');
Map<String, dynamic> data;
final Future<void> processComplete = process.exitCode
......@@ -85,9 +96,9 @@ class CoverageCollector extends TestWatcher {
await Future.any<void>(<Future<void>>[ processComplete, collectionComplete ]);
assert(data != null);
globals.printTrace('pid $pid ($observatoryUri): collected coverage data; merging...');
_logMessage('pid $pid ($observatoryUri): collected coverage data; merging...');
_addHitmap(coverage.createHitmap(data['coverage'] as List<Map<String, dynamic>>));
globals.printTrace('pid $pid ($observatoryUri): done merging coverage data into global coverage map.');
_logMessage('pid $pid ($observatoryUri): done merging coverage data into global coverage map.');
}
/// Returns a future that will complete with the formatted coverage data
......@@ -116,12 +127,10 @@ class CoverageCollector extends TestWatcher {
}
Future<bool> collectCoverageData(String coveragePath, { bool mergeCoverageData = false, Directory coverageDirectory }) async {
final Status status = globals.logger.startProgress('Collecting coverage information...', timeout: timeoutConfiguration.fastOperation);
final String coverageData = await finalizeCoverage(
coverageDirectory: coverageDirectory,
);
status.stop();
globals.printTrace('coverage information collection complete');
_logMessage('coverage information collection complete');
if (coverageData == null) {
return false;
}
......@@ -129,12 +138,12 @@ class CoverageCollector extends TestWatcher {
final File coverageFile = globals.fs.file(coveragePath)
..createSync(recursive: true)
..writeAsStringSync(coverageData, flush: true);
globals.printTrace('wrote coverage data to $coveragePath (size=${coverageData.length})');
_logMessage('wrote coverage data to $coveragePath (size=${coverageData.length})');
const String baseCoverageData = 'coverage/lcov.base.info';
if (mergeCoverageData) {
if (!globals.fs.isFileSync(baseCoverageData)) {
globals.printError('Missing "$baseCoverageData". Unable to merge coverage data.');
_logMessage('Missing "$baseCoverageData". Unable to merge coverage data.', error: true);
return false;
}
......@@ -145,7 +154,7 @@ class CoverageCollector extends TestWatcher {
} else if (globals.platform.isMacOS) {
installMessage = 'Consider running "brew install lcov".';
}
globals.printError('Missing "lcov" tool. Unable to merge coverage data.\n$installMessage');
_logMessage('Missing "lcov" tool. Unable to merge coverage data.\n$installMessage', error: true);
return false;
}
......
......@@ -8,14 +8,18 @@ import 'watcher.dart';
/// Prints JSON events when running a test in --machine mode.
class EventPrinter extends TestWatcher {
EventPrinter({StringSink out}) : _out = out ?? globals.stdio.stdout;
EventPrinter({StringSink out, TestWatcher parent})
: _out = out ?? globals.stdio.stdout,
_parent = parent;
final StringSink _out;
final TestWatcher _parent;
@override
void handleStartedProcess(ProcessEvent event) {
_sendEvent('test.startedProcess',
<String, dynamic>{'observatoryUri': event.observatoryUri.toString()});
_parent?.handleStartedProcess(event);
}
void _sendEvent(String name, [ dynamic params ]) {
......
......@@ -6,6 +6,7 @@ import 'dart:async';
import 'package:args/command_runner.dart';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart';
......@@ -54,6 +55,28 @@ void main() {
Cache: () => FakeCache(),
});
testUsingContext('Supports coverage and machine', () async {
final FakePackageTest fakePackageTest = FakePackageTest();
final TestCommand testCommand = TestCommand(testWrapper: fakePackageTest);
final CommandRunner<void> commandRunner =
createTestCommandRunner(testCommand);
expect(() => commandRunner.run(const <String>[
'test',
'--no-pub',
'--machine',
'--coverage',
'--',
'test/fake_test.dart',
]), throwsA(isA<ToolExit>()
.having((ToolExit toolExit) => toolExit.message, 'message', isNull)));
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
Cache: () => FakeCache(),
});
testUsingContext('Pipes start-paused to package:test',
() async {
final FakePackageTest fakePackageTest = FakePackageTest();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment