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

Side by Side Diff: tools/full-coverage.dart

Issue 1846443002: - Remove --coverage_dir flag and old-style coverage service requests. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Remove obsolete tests. Created 4 years, 8 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 | « tests/standalone/full_coverage_test.dart ('k') | no next file » | 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) 2013, 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 import "dart:async";
6 import "dart:convert";
7 import "dart:io";
8 import "dart:isolate";
9
10 import "package:args/args.dart";
11 import "package:path/path.dart";
12
13 /// [Environment] stores gathered arguments information.
14 class Environment {
15 String sdkRoot;
16 String pkgRoot;
17 var input;
18 var output;
19 int workers;
20 bool prettyPrint = false;
21 bool lcov = false;
22 bool expectMarkers;
23 bool verbose;
24 }
25
26 /// [Resolver] resolves imports with respect to a given environment.
27 class Resolver {
28 static const DART_PREFIX = "dart:";
29 static const PACKAGE_PREFIX = "package:";
30 static const FILE_PREFIX = "file://";
31 static const HTTP_PREFIX = "http://";
32
33 Map _env;
34 List failed = [];
35
36 Resolver(this._env);
37
38 /// Returns the absolute path wrt. to the given environment or null, if the
39 /// import could not be resolved.
40 resolve(String import) {
41 if (import.startsWith(DART_PREFIX)) {
42 if (_env["sdkRoot"] == null) {
43 // No sdk-root given, do not resolve dart: URIs.
44 return null;
45 }
46 var slashPos = import.indexOf("/");
47 var filePath;
48 if (slashPos != -1) {
49 var path = import.substring(DART_PREFIX.length, slashPos);
50 // Drop patch files, since we don't have their source in the compiled
51 // SDK.
52 if (path.endsWith("-patch")) {
53 failed.add(import);
54 return null;
55 }
56 // Canonicalize path. For instance: _collection-dev => _collection_dev.
57 path = path.replaceAll("-", "_");
58 filePath = "${_env["sdkRoot"]}"
59 "/${path}${import.substring(slashPos, import.length)}";
60 } else {
61 // Resolve 'dart:something' to be something/something.dart in the SDK.
62 var lib = import.substring(DART_PREFIX.length, import.length);
63 filePath = "${_env["sdkRoot"]}/${lib}/${lib}.dart";
64 }
65 return filePath;
66 }
67 if (import.startsWith(PACKAGE_PREFIX)) {
68 if (_env["pkgRoot"] == null) {
69 // No package-root given, do not resolve package: URIs.
70 return null;
71 }
72 var filePath =
73 "${_env["pkgRoot"]}"
74 "/${import.substring(PACKAGE_PREFIX.length, import.length)}";
75 return filePath;
76 }
77 if (import.startsWith(FILE_PREFIX)) {
78 var filePath = fromUri(Uri.parse(import));
79 return filePath;
80 }
81 if (import.startsWith(HTTP_PREFIX)) {
82 return import;
83 }
84 // We cannot deal with anything else.
85 failed.add(import);
86 return null;
87 }
88 }
89
90 /// Converts the given hitmap to lcov format and appends the result to
91 /// env.output.
92 ///
93 /// Returns a [Future] that completes as soon as all map entries have been
94 /// emitted.
95 Future lcov(Map hitmap) {
96 var emitOne = (key) {
97 var v = hitmap[key];
98 StringBuffer entry = new StringBuffer();
99 entry.write("SF:${key}\n");
100 v.keys.toList()
101 ..sort()
102 ..forEach((k) {
103 entry.write("DA:${k},${v[k]}\n");
104 });
105 entry.write("end_of_record\n");
106 env.output.write(entry.toString());
107 return new Future.value(null);
108 };
109
110 return Future.forEach(hitmap.keys, emitOne);
111 }
112
113 /// Converts the given hitmap to a pretty-print format and appends the result
114 /// to env.output.
115 ///
116 /// Returns a [Future] that completes as soon as all map entries have been
117 /// emitted.
118 Future prettyPrint(Map hitMap, List failedLoads) {
119 var emitOne = (key) {
120 var v = hitMap[key];
121 var c = new Completer();
122 loadResource(key).then((lines) {
123 if (lines == null) {
124 failedLoads.add(key);
125 c.complete();
126 return;
127 }
128 env.output.write("${key}\n");
129 for (var line = 1; line <= lines.length; line++) {
130 String prefix = " ";
131 if (v.containsKey(line)) {
132 prefix = v[line].toString();
133 StringBuffer b = new StringBuffer();
134 for (int i = prefix.length; i < 7; i++) {
135 b.write(" ");
136 }
137 b.write(prefix);
138 prefix = b.toString();
139 }
140 env.output.write("${prefix}|${lines[line-1]}\n");
141 }
142 c.complete();
143 });
144 return c.future;
145 };
146
147 return Future.forEach(hitMap.keys, emitOne);
148 }
149
150 /// Load an import resource and return a [Future] with a [List] of its lines.
151 /// Returns [null] instead of a list if the resource could not be loaded.
152 Future<List> loadResource(String import) {
153 if (import.startsWith("http")) {
154 Completer c = new Completer();
155 HttpClient client = new HttpClient();
156 client.getUrl(Uri.parse(import))
157 .then((HttpClientRequest request) {
158 return request.close();
159 })
160 .then((HttpClientResponse response) {
161 response.transform(new StringDecoder()).toList().then((data) {
162 c.complete(data);
163 httpClient.close();
164 });
165 })
166 .catchError((e) {
167 c.complete(null);
168 });
169 return c.future;
170 } else {
171 File f = new File(import);
172 return f.readAsLines()
173 .catchError((e) {
174 return new Future.value(null);
175 });
176 }
177 }
178
179 /// Creates a single hitmap from a raw json object. Throws away all entries that
180 /// are not resolvable.
181 Map createHitmap(String rawJson, Resolver resolver) {
182 Map<String, Map<int,int>> hitMap = {};
183
184 addToMap(source, line, count) {
185 if (!hitMap[source].containsKey(line)) {
186 hitMap[source][line] = 0;
187 }
188 hitMap[source][line] += count;
189 }
190
191 JSON.decode(rawJson)['coverage'].forEach((Map e) {
192 String source = resolver.resolve(e["source"]);
193 if (source == null) {
194 // Couldnt resolve import, so skip this entry.
195 return;
196 }
197 if (!hitMap.containsKey(source)) {
198 hitMap[source] = {};
199 }
200 var hits = e["hits"];
201 // hits is a flat array of the following format:
202 // [ <line|linerange>, <hitcount>,...]
203 // line: number.
204 // linerange: "<line>-<line>".
205 for (var i = 0; i < hits.length; i += 2) {
206 var k = hits[i];
207 if (k is num) {
208 // Single line.
209 addToMap(source, k, hits[i+1]);
210 }
211 if (k is String) {
212 // Linerange. We expand line ranges to actual lines at this point.
213 var splitPos = k.indexOf("-");
214 int start = int.parse(k.substring(0, splitPos));
215 int end = int.parse(k.substring(splitPos + 1, k.length));
216 for (var j = start; j <= end; j++) {
217 addToMap(source, j, hits[i+1]);
218 }
219 }
220 }
221 });
222 return hitMap;
223 }
224
225 /// Merges [newMap] into [result].
226 mergeHitmaps(Map newMap, Map result) {
227 newMap.forEach((String file, Map v) {
228 if (result.containsKey(file)) {
229 v.forEach((int line, int cnt) {
230 if (result[file][line] == null) {
231 result[file][line] = cnt;
232 } else {
233 result[file][line] += cnt;
234 }
235 });
236 } else {
237 result[file] = v;
238 }
239 });
240 }
241
242 /// Given an absolute path absPath, this function returns a [List] of files
243 /// are contained by it if it is a directory, or a [List] containing the file if
244 /// it is a file.
245 List filesToProcess(String absPath) {
246 var filePattern = new RegExp(r"^dart-cov-\d+-\d+.json$");
247 if (FileSystemEntity.isDirectorySync(absPath)) {
248 return new Directory(absPath).listSync(recursive: true)
249 .where((entity) => entity is File &&
250 filePattern.hasMatch(basename(entity.path)))
251 .toList();
252 }
253
254 return [new File(absPath)];
255 }
256
257 worker(WorkMessage msg) {
258 final start = new DateTime.now().millisecondsSinceEpoch;
259
260 var env = msg.environment;
261 List files = msg.files;
262 Resolver resolver = new Resolver(env);
263 var workerHitmap = {};
264 files.forEach((File fileEntry) {
265 // Read file sync, as it only contains 1 object.
266 String contents = fileEntry.readAsStringSync();
267 if (contents.length > 0) {
268 mergeHitmaps(createHitmap(contents, resolver), workerHitmap);
269 }
270 });
271
272 if (env["verbose"]) {
273 final end = new DateTime.now().millisecondsSinceEpoch;
274 print("${msg.workerName}: Finished processing ${files.length} files. "
275 "Took ${end - start} ms.");
276 }
277
278 msg.replyPort.send(new ResultMessage(workerHitmap, resolver.failed));
279 }
280
281 class WorkMessage {
282 final String workerName;
283 final Map environment;
284 final List files;
285 final SendPort replyPort;
286 WorkMessage(this.workerName, this.environment, this.files, this.replyPort);
287 }
288
289 class ResultMessage {
290 final hitmap;
291 final failedResolves;
292 ResultMessage(this.hitmap, this.failedResolves);
293 }
294
295 final env = new Environment();
296
297 List<List> split(List list, int nBuckets) {
298 var buckets = new List(nBuckets);
299 var bucketSize = list.length ~/ nBuckets;
300 var leftover = list.length % nBuckets;
301 var taken = 0;
302 var start = 0;
303 for (int i = 0; i < nBuckets; i++) {
304 var end = (i < leftover) ? (start + bucketSize + 1) : (start + bucketSize);
305 buckets[i] = list.sublist(start, end);
306 taken += buckets[i].length;
307 start = end;
308 }
309 if (taken != list.length) throw "Error splitting";
310 return buckets;
311 }
312
313 Future<ResultMessage> spawnWorker(name, environment, files) {
314 RawReceivePort port = new RawReceivePort();
315 var completer = new Completer();
316 port.handler = ((ResultMessage msg) {
317 completer.complete(msg);
318 port.close();
319 });
320 var msg = new WorkMessage(name, environment, files, port.sendPort);
321 Isolate.spawn(worker, msg);
322 return completer.future;
323 }
324
325 main(List<String> arguments) {
326 parseArgs(arguments);
327
328 List files = filesToProcess(env.input);
329
330 List failedResolves = [];
331 List failedLoads = [];
332 Map globalHitmap = {};
333 int start = new DateTime.now().millisecondsSinceEpoch;
334
335 if (env.verbose) {
336 print("Environment:");
337 print(" # files: ${files.length}");
338 print(" # workers: ${env.workers}");
339 print(" sdk-root: ${env.sdkRoot}");
340 print(" package-root: ${env.pkgRoot}");
341 }
342
343 Map sharedEnv = {
344 "sdkRoot": env.sdkRoot,
345 "pkgRoot": env.pkgRoot,
346 "verbose": env.verbose,
347 };
348
349 // Create workers.
350 int workerId = 0;
351 var results = split(files, env.workers).map((workerFiles) {
352 var result = spawnWorker("Worker ${workerId++}", sharedEnv, workerFiles);
353 return result.then((ResultMessage message) {
354 mergeHitmaps(message.hitmap, globalHitmap);
355 failedResolves.addAll(message.failedResolves);
356 });
357 });
358
359 Future.wait(results).then((ignore) {
360 // All workers are done. Process the data.
361 if (env.verbose) {
362 final end = new DateTime.now().millisecondsSinceEpoch;
363 print("Done creating a global hitmap. Took ${end - start} ms.");
364 }
365
366 Future out;
367 if (env.prettyPrint) {
368 out = prettyPrint(globalHitmap, failedLoads);
369 }
370 if (env.lcov) {
371 out = lcov(globalHitmap);
372 }
373
374 out.then((_) {
375 env.output.close().then((_) {
376 if (env.verbose) {
377 final end = new DateTime.now().millisecondsSinceEpoch;
378 print("Done flushing output. Took ${end - start} ms.");
379 }
380 });
381
382 if (env.verbose) {
383 if (failedResolves.length > 0) {
384 print("Failed to resolve:");
385 failedResolves.toSet().forEach((e) {
386 print(" ${e}");
387 });
388 }
389 if (failedLoads.length > 0) {
390 print("Failed to load:");
391 failedLoads.toSet().forEach((e) {
392 print(" ${e}");
393 });
394 }
395 }
396 });
397 });
398 }
399
400 /// Checks the validity of the provided arguments. Does not initialize actual
401 /// processing.
402 parseArgs(List<String> arguments) {
403 var parser = new ArgParser();
404
405 parser.addOption("sdk-root", abbr: "s",
406 help: "path to the SDK root");
407 parser.addOption("package-root", abbr: "p",
408 help: "override path to the package root "
409 "(default: inherited from dart)");
410 parser.addOption("in", abbr: "i",
411 help: "input(s): may be file or directory");
412 parser.addOption("out", abbr: "o",
413 help: "output: may be file or stdout",
414 defaultsTo: "stdout");
415 parser.addOption("workers", abbr: "j",
416 help: "number of workers",
417 defaultsTo: "1");
418 parser.addFlag("pretty-print", abbr: "r",
419 help: "convert coverage data to pretty print format",
420 negatable: false);
421 parser.addFlag("lcov", abbr :"l",
422 help: "convert coverage data to lcov format",
423 negatable: false);
424 parser.addFlag("verbose", abbr :"v",
425 help: "verbose output",
426 negatable: false);
427 parser.addFlag("help", abbr: "h",
428 help: "show this help",
429 negatable: false);
430
431 var args = parser.parse(arguments);
432
433 printUsage() {
434 print("Usage: dart full-coverage.dart [OPTION...]\n");
435 print(parser.getUsage());
436 }
437
438 fail(String msg) {
439 print("\n$msg\n");
440 printUsage();
441 exit(1);
442 }
443
444 if (args["help"]) {
445 printUsage();
446 exit(0);
447 }
448
449 env.sdkRoot = args["sdk-root"];
450 if (env.sdkRoot == null) {
451 if (Platform.environment.containsKey("SDK_ROOT")) {
452 env.sdkRoot =
453 join(absolute(normalize(Platform.environment["SDK_ROOT"])), "lib");
454 }
455 } else {
456 env.sdkRoot = join(absolute(normalize(env.sdkRoot)), "lib");
457 }
458 if ((env.sdkRoot != null) && !FileSystemEntity.isDirectorySync(env.sdkRoot)) {
459 fail("Provided SDK root '${args["sdk-root"]}' is not a valid SDK "
460 "top-level directory");
461 }
462
463 env.pkgRoot = args["package-root"];
464 if (env.pkgRoot != null) {
465 var pkgRootUri = Uri.parse(env.pkgRoot);
466 if (pkgRootUri.scheme == "file") {
467 if (!FileSystemEntity.isDirectorySync(pkgRootUri.toFilePath())) {
468 fail("Provided package root '${args["package-root"]}' is not directory." );
469 }
470 }
471 } else {
472 env.pkgRoot = Platform.packageRoot;
473 }
474
475 if (args["in"] == null) {
476 fail("No input files given.");
477 } else {
478 env.input = absolute(normalize(args["in"]));
479 if (!FileSystemEntity.isDirectorySync(env.input) &&
480 !FileSystemEntity.isFileSync(env.input)) {
481 fail("Provided input '${args["in"]}' is neither a directory, nor a file.") ;
482 }
483 }
484
485 if (args["out"] == "stdout") {
486 env.output = stdout;
487 } else {
488 env.output = absolute(normalize(args["out"]));
489 env.output = new File(env.output).openWrite();
490 }
491
492 env.lcov = args["lcov"];
493 if (args["pretty-print"] && env.lcov) {
494 fail("Choose one of pretty-print or lcov output");
495 } else if (!env.lcov) {
496 // Use pretty-print either explicitly or by default.
497 env.prettyPrint = true;
498 }
499
500 try {
501 env.workers = int.parse("${args["workers"]}");
502 } catch (e) {
503 fail("Invalid worker count: $e");
504 }
505
506 env.verbose = args["verbose"];
507 }
OLDNEW
« no previous file with comments | « tests/standalone/full_coverage_test.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698