coverage_collector.dart 3.19 KB
Newer Older
1 2 3 4 5 6
// Copyright 2016 The Chromium Authors. 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';

7
import 'package:coverage/coverage.dart' as coverage;
8

9 10
import '../base/file_system.dart';
import '../base/io.dart';
11
import '../dart/package_map.dart';
12 13
import '../globals.dart';

14
/// A class that's used to collect coverage data during tests.
15
class CoverageCollector {
16 17 18
  Map<String, dynamic> _globalHitmap;

  void _addHitmap(Map<String, dynamic> hitmap) {
19 20 21
    if (_globalHitmap == null)
      _globalHitmap = hitmap;
    else
22
      coverage.mergeHitmaps(hitmap, _globalHitmap);
23 24
  }

25
  /// Collects coverage for the given [Process] using the given `port`.
26
  ///
27 28 29 30 31 32 33 34 35 36
  /// This should be called when the code whose coverage data is being collected
  /// has been run to completion so that all coverage data has been recorded.
  ///
  /// The returned [Future] completes when the coverage is collected.
  Future<Null> collectCoverage(Process process, InternetAddress host, int port) async {
    assert(process != null);
    assert(port != null);

    int pid = process.pid;
    int exitCode;
37
    process.exitCode.then<Null>((int code) {
38 39
      exitCode = code;
    });
40 41
    if (exitCode != null)
      throw new Exception('Failed to collect coverage, process terminated before coverage could be collected.');
42 43

    printTrace('pid $pid (port $port): collecting coverage data...');
44 45 46 47
    final Map<String, dynamic> data = await coverage.collect(host.address, port, false, false).timeout(
      const Duration(seconds: 30),
      onTimeout: () { throw new Exception('Failed to collect coverage, it took more than thirty seconds.'); },
    );
48 49
    printTrace('pid $pid (port $port): ${ exitCode != null ? "process terminated prematurely with exit code $exitCode; aborting" : "collected coverage data; merging..." }');
    if (exitCode != null)
50 51
      throw new Exception('Failed to collect coverage, process terminated while coverage was being collected.');
    _addHitmap(coverage.createHitmap(data['coverage']));
52
    printTrace('pid $pid (port $port): done merging coverage data into global coverage map.');
53 54
  }

55 56 57
  /// Returns a future that will complete with the formatted coverage data
  /// (using [formatter]) once all coverage data has been collected.
  ///
58 59
  /// This will not start any collection tasks. It us up to the caller of to
  /// call [collectCoverage] for each process first.
60 61 62 63
  ///
  /// If [timeout] is specified, the future will timeout (with a
  /// [TimeoutException]) after the specified duration.
  Future<String> finalizeCoverage({
64
    coverage.Formatter formatter,
65 66
    Duration timeout,
  }) async {
67
    printTrace('formating coverage data');
68 69
    if (_globalHitmap == null)
      return null;
70
    if (formatter == null) {
71
      coverage.Resolver resolver = new coverage.Resolver(packagesPath: PackageMap.globalPackagesPath);
72
      String packagePath = fs.currentDirectory.path;
73
      List<String> reportOn = <String>[fs.path.join(packagePath, 'lib')];
74
      formatter = new coverage.LcovFormatter(resolver, reportOn: reportOn, basePath: packagePath);
75
    }
76 77 78
    String result = await formatter.format(_globalHitmap);
    _globalHitmap = null;
    return result;
79 80
  }
}