blob: 47ab4e01154ca6e61c3495155224adaf1ce4839c [file] [log] [blame]
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
// for details. 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 'dart:convert' show json;
import 'dart:io';
import 'package:args/args.dart';
import 'package:coverage/src/collect.dart';
import 'package:logging/logging.dart';
import 'package:stack_trace/stack_trace.dart';
Future<void> main(List<String> arguments) async {
Logger.root.level = Level.WARNING;
Logger.root.onRecord.listen((LogRecord rec) {
print('${rec.level.name}: ${rec.time}: ${rec.message}');
});
final options = _parseArgs(arguments);
await Chain.capture(() async {
final coverage = await collect(options.serviceUri, options.resume,
options.waitPaused, options.includeDart, options.scopedOutput,
timeout: options.timeout, functionCoverage: options.functionCoverage);
options.out.write(json.encode(coverage));
await options.out.close();
}, onError: (dynamic error, Chain chain) {
stderr.writeln(error);
stderr.writeln(chain.terse);
// See http://www.retro11.de/ouxr/211bsd/usr/include/sysexits.h.html
// EX_SOFTWARE
exit(70);
});
}
class Options {
Options(this.serviceUri, this.out, this.timeout, this.waitPaused, this.resume,
this.includeDart, this.functionCoverage, this.scopedOutput);
final Uri serviceUri;
final IOSink out;
final Duration? timeout;
final bool waitPaused;
final bool resume;
final bool includeDart;
final bool functionCoverage;
final Set<String> scopedOutput;
}
Options _parseArgs(List<String> arguments) {
final parser = ArgParser()
..addOption('host',
abbr: 'H',
help: 'remote VM host. DEPRECATED: use --uri',
defaultsTo: '127.0.0.1')
..addOption('port',
abbr: 'p',
help: 'remote VM port. DEPRECATED: use --uri',
defaultsTo: '8181')
..addOption('uri', abbr: 'u', help: 'VM observatory service URI')
..addOption('out',
abbr: 'o', defaultsTo: 'stdout', help: 'output: may be file or stdout')
..addOption('connect-timeout',
abbr: 't', help: 'connect timeout in seconds')
..addMultiOption('scope-output',
help: 'restrict coverage results so that only scripts that start with '
'the provided package path are considered')
..addFlag('wait-paused',
abbr: 'w',
defaultsTo: false,
help: 'wait for all isolates to be paused before collecting coverage')
..addFlag('resume-isolates',
abbr: 'r', defaultsTo: false, help: 'resume all isolates on exit')
..addFlag('include-dart',
abbr: 'd', defaultsTo: false, help: 'include "dart:" libraries')
..addFlag('function-coverage',
abbr: 'f', defaultsTo: false, help: 'Collect function coverage info')
..addFlag('help', abbr: 'h', negatable: false, help: 'show this help');
final args = parser.parse(arguments);
void printUsage() {
print('Usage: dart collect_coverage.dart --uri=http://... [OPTION...]\n');
print(parser.usage);
}
Never fail(String message) {
print('Error: $message\n');
printUsage();
exit(1);
}
if (args['help'] as bool) {
printUsage();
exit(0);
}
Uri serviceUri;
if (args['uri'] == null) {
// TODO(cbracken) eliminate --host and --port support when VM defaults to
// requiring an auth token. Estimated for Dart SDK 1.22.
serviceUri = Uri.parse('http://${args['host']}:${args['port']}/');
} else {
try {
serviceUri = Uri.parse(args['uri'] as String);
} on FormatException {
fail('Invalid service URI specified: ${args['uri']}');
}
}
final scopedOutput = args['scope-output'] as List<String>;
IOSink out;
if (args['out'] == 'stdout') {
out = stdout;
} else {
final outfile = File(args['out'] as String)..createSync(recursive: true);
out = outfile.openWrite();
}
final timeout = (args['connect-timeout'] == null)
? null
: Duration(seconds: int.parse(args['connect-timeout'] as String));
return Options(
serviceUri,
out,
timeout,
args['wait-paused'] as bool,
args['resume-isolates'] as bool,
args['include-dart'] as bool,
args['function-coverage'] as bool,
scopedOutput.toSet(),
);
}