LCOV - code coverage report
Current view: top level - test-0.12.24+8/lib/src/runner/reporter - expanded.dart (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 50 94 53.2 %
Date: 2017-10-10 20:17:03 Functions: 0 0 -

          Line data    Source code
       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             : import 'dart:async';
       6             : import 'dart:isolate';
       7             : 
       8             : import '../../backend/live_test.dart';
       9             : import '../../backend/message.dart';
      10             : import '../../backend/state.dart';
      11             : import '../../utils.dart';
      12             : import '../engine.dart';
      13             : import '../load_exception.dart';
      14             : import '../load_suite.dart';
      15             : import '../reporter.dart';
      16             : 
      17             : /// The maximum console line length.
      18             : ///
      19             : /// Lines longer than this will be cropped.
      20             : const _lineLength = 100;
      21             : 
      22             : /// A reporter that prints each test on its own line.
      23             : ///
      24             : /// This is currently used in place of [CompactReporter] by `lib/test.dart`,
      25             : /// which can't transitively import `dart:io` but still needs access to a runner
      26             : /// so that test files can be run directly. This means that until issue 6943 is
      27             : /// fixed, this must not import `dart:io`.
      28             : class ExpandedReporter implements Reporter {
      29             :   /// Whether the reporter should emit terminal color escapes.
      30             :   final bool _color;
      31             : 
      32             :   /// The terminal escape for green text, or the empty string if this is Windows
      33             :   /// or not outputting to a terminal.
      34             :   final String _green;
      35             : 
      36             :   /// The terminal escape for red text, or the empty string if this is Windows
      37             :   /// or not outputting to a terminal.
      38             :   final String _red;
      39             : 
      40             :   /// The terminal escape for yellow text, or the empty string if this is
      41             :   /// Windows or not outputting to a terminal.
      42             :   final String _yellow;
      43             : 
      44             :   /// The terminal escape for gray text, or the empty string if this is
      45             :   /// Windows or not outputting to a terminal.
      46             :   final String _gray;
      47             : 
      48             :   /// The terminal escape for bold text, or the empty string if this is
      49             :   /// Windows or not outputting to a terminal.
      50             :   final String _bold;
      51             : 
      52             :   /// The terminal escape for removing test coloring, or the empty string if
      53             :   /// this is Windows or not outputting to a terminal.
      54             :   final String _noColor;
      55             : 
      56             :   /// The engine used to run the tests.
      57             :   final Engine _engine;
      58             : 
      59             :   /// Whether the path to each test's suite should be printed.
      60             :   final bool _printPath;
      61             : 
      62             :   /// Whether the platform each test is running on should be printed.
      63             :   final bool _printPlatform;
      64             : 
      65             :   /// A stopwatch that tracks the duration of the full run.
      66             :   final _stopwatch = new Stopwatch();
      67             : 
      68             :   /// The size of `_engine.passed` last time a progress notification was
      69             :   /// printed.
      70             :   int _lastProgressPassed;
      71             : 
      72             :   /// The size of `_engine.skipped` last time a progress notification was
      73             :   /// printed.
      74             :   int _lastProgressSkipped;
      75             : 
      76             :   /// The size of `_engine.failed` last time a progress notification was
      77             :   /// printed.
      78             :   int _lastProgressFailed;
      79             : 
      80             :   /// The message printed for the last progress notification.
      81             :   String _lastProgressMessage;
      82             : 
      83             :   /// The suffix added to the last progress notification.
      84             :   String _lastProgressSuffix;
      85             : 
      86             :   /// Whether the reporter is paused.
      87             :   var _paused = false;
      88             : 
      89             :   /// The set of all subscriptions to various streams.
      90             :   final _subscriptions = new Set<StreamSubscription>();
      91             : 
      92             :   // TODO(nweiz): Get configuration from [Configuration.current] once we have
      93             :   // cross-platform imports.
      94             :   /// Watches the tests run by [engine] and prints their results to the
      95             :   /// terminal.
      96             :   ///
      97             :   /// If [color] is `true`, this will use terminal colors; if it's `false`, it
      98             :   /// won't. If [printPath] is `true`, this will print the path name as part of
      99             :   /// the test description. Likewise, if [printPlatform] is `true`, this will
     100             :   /// print the platform as part of the test description.
     101             :   static ExpandedReporter watch(Engine engine,
     102             :       {bool color: true, bool printPath: true, bool printPlatform: true}) {
     103           5 :     return new ExpandedReporter._(engine,
     104             :         color: color, printPath: printPath, printPlatform: printPlatform);
     105             :   }
     106             : 
     107             :   ExpandedReporter._(this._engine,
     108             :       {bool color: true, bool printPath: true, bool printPlatform: true})
     109             :       : _printPath = printPath,
     110             :         _printPlatform = printPlatform,
     111             :         _color = color,
     112             :         _green = color ? '\u001b[32m' : '',
     113             :         _red = color ? '\u001b[31m' : '',
     114             :         _yellow = color ? '\u001b[33m' : '',
     115             :         _gray = color ? '\u001b[1;30m' : '',
     116             :         _bold = color ? '\u001b[1m' : '',
     117           5 :         _noColor = color ? '\u001b[0m' : '' {
     118          30 :     _subscriptions.add(_engine.onTestStarted.listen(_onTestStarted));
     119             : 
     120             :     /// Convert the future to a stream so that the subscription can be paused or
     121             :     /// canceled.
     122          35 :     _subscriptions.add(_engine.success.asStream().listen(_onDone));
     123             :   }
     124             : 
     125             :   void pause() {
     126           0 :     if (_paused) return;
     127           0 :     _paused = true;
     128             : 
     129           0 :     _stopwatch.stop();
     130             : 
     131           0 :     for (var subscription in _subscriptions) {
     132           0 :       subscription.pause();
     133             :     }
     134             :   }
     135             : 
     136             :   void resume() {
     137           0 :     if (!_paused) return;
     138           0 :     _stopwatch.start();
     139             : 
     140           0 :     for (var subscription in _subscriptions) {
     141           0 :       subscription.resume();
     142             :     }
     143             :   }
     144             : 
     145             :   void cancel() {
     146           0 :     for (var subscription in _subscriptions) {
     147           0 :       subscription.cancel();
     148             :     }
     149           0 :     _subscriptions.clear();
     150             :   }
     151             : 
     152             :   /// A callback called when the engine begins running [liveTest].
     153             :   void _onTestStarted(LiveTest liveTest) {
     154          10 :     if (liveTest.suite is! LoadSuite) {
     155          20 :       if (!_stopwatch.isRunning) _stopwatch.start();
     156             : 
     157             :       // If this is the first non-load test to start, print a progress line so
     158             :       // the user knows what's running.
     159          30 :       if (_engine.active.length == 1) _progressLine(_description(liveTest));
     160             : 
     161             :       // The engine surfaces load tests when there are no other tests running,
     162             :       // but because the expanded reporter's output is always visible, we don't
     163             :       // emit information about them unless they fail.
     164          15 :       _subscriptions.add(liveTest.onStateChange
     165          10 :           .listen((state) => _onStateChange(liveTest, state)));
     166           0 :     } else if (_engine.active.length == 1 &&
     167           0 :         _engine.active.first == liveTest &&
     168           0 :         liveTest.test.name.startsWith("compiling ")) {
     169             :       // Print a progress line for load tests that come from compiling JS, since
     170             :       // that takes a long time.
     171           0 :       _progressLine(_description(liveTest));
     172             :     }
     173             : 
     174          15 :     _subscriptions.add(liveTest.onError
     175           5 :         .listen((error) => _onError(liveTest, error.error, error.stackTrace)));
     176             : 
     177          20 :     _subscriptions.add(liveTest.onMessage.listen((message) {
     178           2 :       _progressLine(_description(liveTest));
     179           1 :       var text = message.text;
     180           2 :       if (message.type == MessageType.skip) text = '  $_yellow$text$_noColor';
     181           1 :       print(text);
     182             :     }));
     183             :   }
     184             : 
     185             :   /// A callback called when [liveTest]'s state becomes [state].
     186             :   void _onStateChange(LiveTest liveTest, State state) {
     187          10 :     if (state.status != Status.complete) return;
     188             : 
     189             :     // If any tests are running, display the name of the oldest active
     190             :     // test.
     191          15 :     if (_engine.active.isNotEmpty) {
     192           0 :       _progressLine(_description(_engine.active.first));
     193             :     }
     194             :   }
     195             : 
     196             :   /// A callback called when [liveTest] throws [error].
     197             :   void _onError(LiveTest liveTest, error, StackTrace stackTrace) {
     198           0 :     if (liveTest.state.status != Status.complete) return;
     199             : 
     200           0 :     _progressLine(_description(liveTest), suffix: " $_bold$_red[E]$_noColor");
     201             : 
     202           0 :     if (error is! LoadException) {
     203           0 :       print(indent(error.toString()));
     204           0 :       print(indent('$stackTrace'));
     205             :       return;
     206             :     }
     207             : 
     208           0 :     print(indent(error.toString(color: _color)));
     209             : 
     210             :     // Only print stack traces for load errors that come from the user's code.
     211           0 :     if (error.innerError is! IsolateSpawnException &&
     212           0 :         error.innerError is! FormatException &&
     213           0 :         error.innerError is! String) {
     214           0 :       print(indent('$stackTrace'));
     215             :     }
     216             :   }
     217             : 
     218             :   /// A callback called when the engine is finished running tests.
     219             :   ///
     220             :   /// [success] will be `true` if all tests passed, `false` if some tests
     221             :   /// failed, and `null` if the engine was closed prematurely.
     222             :   void _onDone(bool success) {
     223             :     // A null success value indicates that the engine was closed before the
     224             :     // tests finished running, probably because of a signal from the user, in
     225             :     // which case we shouldn't print summary information.
     226             :     if (success == null) return;
     227             : 
     228          15 :     if (_engine.liveTests.isEmpty) {
     229           0 :       print("No tests ran.");
     230             :     } else if (!success) {
     231           0 :       _progressLine('Some tests failed.', color: _red);
     232          15 :     } else if (_engine.passed.isEmpty) {
     233           0 :       _progressLine("All tests skipped.");
     234             :     } else {
     235           5 :       _progressLine("All tests passed!");
     236             :     }
     237             :   }
     238             : 
     239             :   /// Prints a line representing the current state of the tests.
     240             :   ///
     241             :   /// [message] goes after the progress report, and may be truncated to fit the
     242             :   /// entire line within [_lineLength]. If [color] is passed, it's used as the
     243             :   /// color for [message]. If [suffix] is passed, it's added to the end of
     244             :   /// [message].
     245             :   void _progressLine(String message, {String color, String suffix}) {
     246             :     // Print nothing if nothing has changed since the last progress line.
     247          25 :     if (_engine.passed.length == _lastProgressPassed &&
     248           5 :         _engine.skipped.length == _lastProgressSkipped &&
     249           5 :         _engine.failed.length == _lastProgressFailed &&
     250           2 :         message == _lastProgressMessage &&
     251             :         // Don't re-print just because a suffix was removed.
     252           0 :         (suffix == null || suffix == _lastProgressSuffix)) {
     253             :       return;
     254             :     }
     255             : 
     256          20 :     _lastProgressPassed = _engine.passed.length;
     257          20 :     _lastProgressSkipped = _engine.skipped.length;
     258          20 :     _lastProgressFailed = _engine.failed.length;
     259           5 :     _lastProgressMessage = message;
     260           5 :     _lastProgressSuffix = suffix;
     261             : 
     262           0 :     if (suffix != null) message += suffix;
     263             :     if (color == null) color = '';
     264          10 :     var duration = _stopwatch.elapsed;
     265           5 :     var buffer = new StringBuffer();
     266             : 
     267             :     // \r moves back to the beginning of the current line.
     268          15 :     buffer.write('${_timeString(duration)} ');
     269          10 :     buffer.write(_green);
     270           5 :     buffer.write('+');
     271          20 :     buffer.write(_engine.passed.length);
     272          10 :     buffer.write(_noColor);
     273             : 
     274          15 :     if (_engine.skipped.isNotEmpty) {
     275           0 :       buffer.write(_yellow);
     276           0 :       buffer.write(' ~');
     277           0 :       buffer.write(_engine.skipped.length);
     278           0 :       buffer.write(_noColor);
     279             :     }
     280             : 
     281          15 :     if (_engine.failed.isNotEmpty) {
     282           0 :       buffer.write(_red);
     283           0 :       buffer.write(' -');
     284           0 :       buffer.write(_engine.failed.length);
     285           0 :       buffer.write(_noColor);
     286             :     }
     287             : 
     288           5 :     buffer.write(': ');
     289           5 :     buffer.write(color);
     290           5 :     buffer.write(message);
     291          10 :     buffer.write(_noColor);
     292             : 
     293          10 :     print(buffer.toString());
     294             :   }
     295             : 
     296             :   /// Returns a representation of [duration] as `MM:SS`.
     297             :   String _timeString(Duration duration) {
     298          15 :     return "${duration.inMinutes.toString().padLeft(2, '0')}:"
     299          25 :         "${(duration.inSeconds % 60).toString().padLeft(2, '0')}";
     300             :   }
     301             : 
     302             :   /// Returns a description of [liveTest].
     303             :   ///
     304             :   /// This differs from the test's own description in that it may also include
     305             :   /// the suite's name.
     306             :   String _description(LiveTest liveTest) {
     307          10 :     var name = liveTest.test.name;
     308             : 
     309           5 :     if (_printPath &&
     310           0 :         liveTest.suite is! LoadSuite &&
     311           0 :         liveTest.suite.path != null) {
     312           0 :       name = "${liveTest.suite.path}: $name";
     313             :     }
     314             : 
     315           5 :     if (_printPlatform && liveTest.suite.platform != null) {
     316           0 :       name = "[${liveTest.suite.platform.name}] $name";
     317             :     }
     318             : 
     319          10 :     if (liveTest.suite is LoadSuite) name = "$_bold$_gray$name$_noColor";
     320             : 
     321             :     return name;
     322             :   }
     323             : }

Generated by: LCOV version 1.13