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

Generated by: LCOV version 1.14