Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(283)

Unified Diff: pkg/compiler/tool/coverage_log_server.dart

Issue 1288593002: dart2js: add function coverage tracking in dart2js output, dumpinfo, and (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/compiler/pubspec.yaml ('k') | sdk/lib/_internal/js_runtime/lib/js_helper.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/compiler/tool/coverage_log_server.dart
diff --git a/pkg/compiler/tool/coverage_log_server.dart b/pkg/compiler/tool/coverage_log_server.dart
new file mode 100644
index 0000000000000000000000000000000000000000..d2e860e46ad259a112835451f74dfc1f766b0f25
--- /dev/null
+++ b/pkg/compiler/tool/coverage_log_server.dart
@@ -0,0 +1,190 @@
+// Copyright (c) 2015, 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.
+
+/// A tool to gather coverage data from an app generated with dart2js.
+/// This tool starts a server that answers to mainly 2 requests:
+/// * a GET request to retrieve the application
+/// * POST requests to record coverage data.
+///
+/// It is intended to be used as follows:
+/// * generate an app by running dart2js with the environment boolean
+/// -DinstrumentForCoverage=true provided to the vm, and the --dump-info
+/// flag provided to dart2js.
+/// * start this server, and proxy requests from your normal frontend
+/// server to this one.
+library compiler.tool.coverage_log_server;
+
+import 'dart:convert';
+import 'dart:io';
+import 'dart:async';
+import 'package:path/path.dart' as path;
+import 'package:args/args.dart';
+import 'package:shelf/shelf.dart' as shelf;
+import 'package:shelf/shelf_io.dart' as shelf;
+
+const _DEFAULT_OUT_TEMPLATE = '<dart2js-out-file>.coverage.json';
+
+main(argv) async {
+ var parser = new ArgParser()
+ ..addOption('port', abbr: 'p', help: 'port number', defaultsTo: "8080")
+ ..addOption('host', help: 'host name (use 0.0.0.0 for all interfaces)',
+ defaultsTo: 'localhost')
+ ..addFlag('help', abbr: 'h', help: 'show this help message',
+ negatable: false)
+ ..addOption('uri-prefix',
+ help: 'uri path prefix that will hit this server. This will be injected'
+ ' into the .js file',
+ defaultsTo: '')
+ ..addOption('out', abbr: 'o', help: 'output log file',
+ defaultsTo: _DEFAULT_OUT_TEMPLATE);
+ var args = parser.parse(argv);
+ if (args['help'] == true || args.rest.isEmpty) {
+ print('usage: dart coverage_logging.dart [options] '
+ '<dart2js-out-file> [<html-file>]');
+ print(parser.usage);
+ exit(1);
+ }
+
+ var jsPath = args.rest[0];
+ var htmlPath = null;
+ if (args.rest.length > 1) {
+ htmlPath = args.rest[1];
+ }
+ var outPath = args['out'];
+ if (outPath == _DEFAULT_OUT_TEMPLATE) outPath = '$jsPath.coverage.json';
+ var server = new _Server(args['host'], int.parse(args['port']), jsPath,
+ htmlPath, outPath, args['uri-prefix']);
+ await server.run();
+}
+
+class _Server {
+ /// Server hostname, typically `localhost`, but can be `0.0.0.0`.
+ final String hostname;
+
+ /// Port the server will listen to.
+ final int port;
+
+ /// JS file (previously generated by dart2js) to serve.
+ final String jsPath;
+
+ /// HTML file to serve, if any.
+ final String htmlPath;
+
+ /// Contents of jsPath, adjusted to use the appropriate server url.
+ String jsCode;
+
+ /// Location where we'll dump the coverage data.
+ final String outPath;
+
+ /// Uri prefix used on all requests to this server. This will be injected into
+ /// the .js file.
+ final String prefix;
+
+ // TODO(sigmund): add support to load also simple HTML files to test small
+ // simple apps.
+
+ /// Data received so far. The data is just an array of pairs, showing the
+ /// hashCode and name of the element used. This can be later cross-checked
+ /// against dump-info data.
+ Map data = {};
+
+ String get _serializedData => new JsonEncoder.withIndent(' ').convert(data);
+
+ _Server(this.hostname, this.port, String jsPath, this.htmlPath,
+ this.outPath, String prefix)
+ : jsPath = jsPath,
+ jsCode = _adjustRequestUrl(new File(jsPath).readAsStringSync(), prefix),
+ prefix = _normalize(prefix);
+
+ run() async {
+ await shelf.serve(_handler, hostname, port);
+ var urlBase = "http://$hostname:$port${prefix == '' ? '/' : '/$prefix/'}";
+ var htmlFilename = htmlPath == null ? '' : path.basename(htmlPath);
+ print("Server is listening\n"
+ " - html page: $urlBase$htmlFilename\n"
+ " - js code: $urlBase${path.basename(jsPath)}\n"
+ " - coverage reporting: ${urlBase}coverage\n");
+ }
+
+ _expectedPath(String tail) => prefix == '' ? tail : '$prefix/$tail';
+
+ _handler(shelf.Request request) async {
+ var urlPath = request.url.path;
+ print('received request: $urlPath');
+ var baseJsName = path.basename(jsPath);
+ var baseHtmlName = htmlPath == null ? '' : path.basename(htmlPath);
+
+ // Serve an HTML file at the default prefix, or a path matching the HTML
+ // file name
+ if (urlPath == prefix || urlPath == '$prefix/'
+ || urlPath == _expectedPath(baseHtmlName)) {
+ var contents = htmlPath == null
+ ? '<html><script src="$baseJsName"></script>'
Johnni Winther 2015/08/19 10:38:22 Is the missing </html> deliberate?
Siggi Cherem (dart-lang) 2015/08/19 15:45:19 yeah, but it is equivalent (HTML parsing spec says
+ : await new File(htmlPath).readAsString();
+ return new shelf.Response.ok(contents, headers: HTML_HEADERS);
+ }
+
+ if (urlPath == _expectedPath(baseJsName)) {
+ return new shelf.Response.ok(jsCode, headers: JS_HEADERS);
+ }
+
+ // Handle POST requests to record coverage data, and GET requests to display
+ // the currently coverage resutls.
+ if (urlPath == _expectedPath('coverage')) {
+ if (request.method == 'GET') {
+ return new shelf.Response.ok(_serializedData, headers: TEXT_HEADERS);
+ }
+
+ if (request.method == 'POST') {
+ _record(JSON.decode(await request.readAsString()));
+ return new shelf.Response.ok("Thanks!");
+ }
+ }
+
+ // Any other request is not supported.
+ return new shelf.Response.notFound('Not found: "$urlPath"');
+ }
+
+ _record(List entries) {
+ for (var entry in entries) {
+ var id = entry[0];
+ data.putIfAbsent('$id', () => {'name': entry[1], 'count': 0});
+ data['$id']['count']++;
+ }
+ _enqueueSave();
+ }
+
+ bool _savePending = false;
+ int _total = 0;
+ _enqueueSave() async {
+ if (!_savePending) {
+ _savePending = true;
+ await new Future.delayed(new Duration(seconds: 3));
+ await new File(outPath).writeAsString(_serializedData);
+ var diff = data.length - _total;
+ print(diff ? ' - no new element covered'
+ : ' - $diff new elements covered');
+ _savePending = false;
+ _total = data.length;
+ }
+ }
+}
+
+/// Removes leading and trailing slashes of [uriPath].
+_normalize(String uriPath) {
+ if (uriPath.startsWith('/')) uriPath = uriPath.substring(1);
+ if (uriPath.endsWith('/')) uriPath = uriPath.substring(0, uriPath.length - 1);
+ return uriPath;
+}
+
+_adjustRequestUrl(String code, String prefix) {
+ var newUrl = prefix == '' ? 'coverage' : '$prefix/coverage';
+ return code.replaceFirst(
+ '"/coverage_uri_to_amend_by_server"',
+ '"/$newUrl" /*url-prefix updated!*/');
+}
+
+const HTML_HEADERS = const {'content-type': 'text/html'};
+const JS_HEADERS = const {'content-type': 'text/javascript'};
+const TEXT_HEADERS = const {'content-type': 'text/plain'};
« no previous file with comments | « pkg/compiler/pubspec.yaml ('k') | sdk/lib/_internal/js_runtime/lib/js_helper.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698