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

Side by Side 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 unified diff | 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 »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 /// A tool to gather coverage data from an app generated with dart2js.
6 /// This tool starts a server that answers to mainly 2 requests:
7 /// * a GET request to retrieve the application
8 /// * POST requests to record coverage data.
9 ///
10 /// It is intended to be used as follows:
11 /// * generate an app by running dart2js with the environment boolean
12 /// -DinstrumentForCoverage=true provided to the vm, and the --dump-info
13 /// flag provided to dart2js.
14 /// * start this server, and proxy requests from your normal frontend
15 /// server to this one.
16 library compiler.tool.coverage_log_server;
17
18 import 'dart:convert';
19 import 'dart:io';
20 import 'dart:async';
21 import 'package:path/path.dart' as path;
22 import 'package:args/args.dart';
23 import 'package:shelf/shelf.dart' as shelf;
24 import 'package:shelf/shelf_io.dart' as shelf;
25
26 const _DEFAULT_OUT_TEMPLATE = '<dart2js-out-file>.coverage.json';
27
28 main(argv) async {
29 var parser = new ArgParser()
30 ..addOption('port', abbr: 'p', help: 'port number', defaultsTo: "8080")
31 ..addOption('host', help: 'host name (use 0.0.0.0 for all interfaces)',
32 defaultsTo: 'localhost')
33 ..addFlag('help', abbr: 'h', help: 'show this help message',
34 negatable: false)
35 ..addOption('uri-prefix',
36 help: 'uri path prefix that will hit this server. This will be injected'
37 ' into the .js file',
38 defaultsTo: '')
39 ..addOption('out', abbr: 'o', help: 'output log file',
40 defaultsTo: _DEFAULT_OUT_TEMPLATE);
41 var args = parser.parse(argv);
42 if (args['help'] == true || args.rest.isEmpty) {
43 print('usage: dart coverage_logging.dart [options] '
44 '<dart2js-out-file> [<html-file>]');
45 print(parser.usage);
46 exit(1);
47 }
48
49 var jsPath = args.rest[0];
50 var htmlPath = null;
51 if (args.rest.length > 1) {
52 htmlPath = args.rest[1];
53 }
54 var outPath = args['out'];
55 if (outPath == _DEFAULT_OUT_TEMPLATE) outPath = '$jsPath.coverage.json';
56 var server = new _Server(args['host'], int.parse(args['port']), jsPath,
57 htmlPath, outPath, args['uri-prefix']);
58 await server.run();
59 }
60
61 class _Server {
62 /// Server hostname, typically `localhost`, but can be `0.0.0.0`.
63 final String hostname;
64
65 /// Port the server will listen to.
66 final int port;
67
68 /// JS file (previously generated by dart2js) to serve.
69 final String jsPath;
70
71 /// HTML file to serve, if any.
72 final String htmlPath;
73
74 /// Contents of jsPath, adjusted to use the appropriate server url.
75 String jsCode;
76
77 /// Location where we'll dump the coverage data.
78 final String outPath;
79
80 /// Uri prefix used on all requests to this server. This will be injected into
81 /// the .js file.
82 final String prefix;
83
84 // TODO(sigmund): add support to load also simple HTML files to test small
85 // simple apps.
86
87 /// Data received so far. The data is just an array of pairs, showing the
88 /// hashCode and name of the element used. This can be later cross-checked
89 /// against dump-info data.
90 Map data = {};
91
92 String get _serializedData => new JsonEncoder.withIndent(' ').convert(data);
93
94 _Server(this.hostname, this.port, String jsPath, this.htmlPath,
95 this.outPath, String prefix)
96 : jsPath = jsPath,
97 jsCode = _adjustRequestUrl(new File(jsPath).readAsStringSync(), prefix),
98 prefix = _normalize(prefix);
99
100 run() async {
101 await shelf.serve(_handler, hostname, port);
102 var urlBase = "http://$hostname:$port${prefix == '' ? '/' : '/$prefix/'}";
103 var htmlFilename = htmlPath == null ? '' : path.basename(htmlPath);
104 print("Server is listening\n"
105 " - html page: $urlBase$htmlFilename\n"
106 " - js code: $urlBase${path.basename(jsPath)}\n"
107 " - coverage reporting: ${urlBase}coverage\n");
108 }
109
110 _expectedPath(String tail) => prefix == '' ? tail : '$prefix/$tail';
111
112 _handler(shelf.Request request) async {
113 var urlPath = request.url.path;
114 print('received request: $urlPath');
115 var baseJsName = path.basename(jsPath);
116 var baseHtmlName = htmlPath == null ? '' : path.basename(htmlPath);
117
118 // Serve an HTML file at the default prefix, or a path matching the HTML
119 // file name
120 if (urlPath == prefix || urlPath == '$prefix/'
121 || urlPath == _expectedPath(baseHtmlName)) {
122 var contents = htmlPath == null
123 ? '<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
124 : await new File(htmlPath).readAsString();
125 return new shelf.Response.ok(contents, headers: HTML_HEADERS);
126 }
127
128 if (urlPath == _expectedPath(baseJsName)) {
129 return new shelf.Response.ok(jsCode, headers: JS_HEADERS);
130 }
131
132 // Handle POST requests to record coverage data, and GET requests to display
133 // the currently coverage resutls.
134 if (urlPath == _expectedPath('coverage')) {
135 if (request.method == 'GET') {
136 return new shelf.Response.ok(_serializedData, headers: TEXT_HEADERS);
137 }
138
139 if (request.method == 'POST') {
140 _record(JSON.decode(await request.readAsString()));
141 return new shelf.Response.ok("Thanks!");
142 }
143 }
144
145 // Any other request is not supported.
146 return new shelf.Response.notFound('Not found: "$urlPath"');
147 }
148
149 _record(List entries) {
150 for (var entry in entries) {
151 var id = entry[0];
152 data.putIfAbsent('$id', () => {'name': entry[1], 'count': 0});
153 data['$id']['count']++;
154 }
155 _enqueueSave();
156 }
157
158 bool _savePending = false;
159 int _total = 0;
160 _enqueueSave() async {
161 if (!_savePending) {
162 _savePending = true;
163 await new Future.delayed(new Duration(seconds: 3));
164 await new File(outPath).writeAsString(_serializedData);
165 var diff = data.length - _total;
166 print(diff ? ' - no new element covered'
167 : ' - $diff new elements covered');
168 _savePending = false;
169 _total = data.length;
170 }
171 }
172 }
173
174 /// Removes leading and trailing slashes of [uriPath].
175 _normalize(String uriPath) {
176 if (uriPath.startsWith('/')) uriPath = uriPath.substring(1);
177 if (uriPath.endsWith('/')) uriPath = uriPath.substring(0, uriPath.length - 1);
178 return uriPath;
179 }
180
181 _adjustRequestUrl(String code, String prefix) {
182 var newUrl = prefix == '' ? 'coverage' : '$prefix/coverage';
183 return code.replaceFirst(
184 '"/coverage_uri_to_amend_by_server"',
185 '"/$newUrl" /*url-prefix updated!*/');
186 }
187
188 const HTML_HEADERS = const {'content-type': 'text/html'};
189 const JS_HEADERS = const {'content-type': 'text/javascript'};
190 const TEXT_HEADERS = const {'content-type': 'text/plain'};
OLDNEW
« 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