OLD | NEW |
(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 /** |
| 6 * A test configuration that generates a compact 1-line progress bar. The bar is |
| 7 * updated in-place before and after each test is executed. If all test pass, |
| 8 * you should only see a couple lines in the terminal. If a test fails, the |
| 9 * failure is shown and the progress bar continues to be updated below it. |
| 10 */ |
| 11 library compact_vm_config; |
| 12 |
| 13 import 'dart:io'; |
| 14 import 'package:unittest/unittest.dart'; |
| 15 |
| 16 const String _GREEN = '\u001b[32m'; |
| 17 const String _RED = '\u001b[31m'; |
| 18 const String _NONE = '\u001b[0m'; |
| 19 const int MAX_LINE = 80; |
| 20 |
| 21 class CompactVMConfiguration extends Configuration { |
| 22 Date _start; |
| 23 int _pass = 0; |
| 24 int _fail = 0; |
| 25 |
| 26 void onInit() { |
| 27 super.onInit(); |
| 28 } |
| 29 |
| 30 void onStart() { |
| 31 super.onStart(); |
| 32 _start = new Date.now(); |
| 33 } |
| 34 |
| 35 void onTestStart(TestCase test) { |
| 36 super.onTestStart(test); |
| 37 _progressLine(_start, _pass, _fail, test.description); |
| 38 } |
| 39 |
| 40 void onTestResult(TestCase test) { |
| 41 super.onTestResult(test); |
| 42 if (test.result == PASS) { |
| 43 _pass++; |
| 44 _progressLine(_start, _pass, _fail, test.description); |
| 45 } else { |
| 46 _fail++; |
| 47 _progressLine(_start, _pass, _fail, test.description); |
| 48 print(''); |
| 49 if (test.message != '') { |
| 50 print(_indent(test.message)); |
| 51 } |
| 52 |
| 53 if (test.stackTrace != null && test.stackTrace != '') { |
| 54 print(_indent(test.stackTrace)); |
| 55 } |
| 56 } |
| 57 } |
| 58 |
| 59 String _indent(String str) { |
| 60 return str.split("\n").mappedBy((line) => " $line").join("\n"); |
| 61 } |
| 62 |
| 63 void onDone(int passed, int failed, int errors, List<TestCase> results, |
| 64 String uncaughtError) { |
| 65 var success = false; |
| 66 if (passed == 0 && failed == 0 && errors == 0) { |
| 67 print('\nNo tests ran.'); |
| 68 } else if (failed == 0 && errors == 0 && uncaughtError == null) { |
| 69 _progressLine(_start, _pass, _fail, 'All tests pass', _GREEN); |
| 70 print('\nAll $passed tests passed.'); |
| 71 success = true; |
| 72 } else { |
| 73 _progressLine(_start, _pass, _fail, 'Some tests fail', _RED); |
| 74 print(''); |
| 75 if (uncaughtError != null) { |
| 76 print('Top-level uncaught error: $uncaughtError'); |
| 77 } |
| 78 print('$passed PASSED, $failed FAILED, $errors ERRORS'); |
| 79 } |
| 80 |
| 81 if (!success) exit(1); |
| 82 } |
| 83 |
| 84 int _lastLength = 0; |
| 85 |
| 86 final int _nonVisiblePrefix = 1 + _GREEN.length + _NONE.length; |
| 87 |
| 88 void _progressLine(Date startTime, int passed, int failed, String message, |
| 89 [String color = _NONE]) { |
| 90 var duration = (new Date.now()).difference(startTime); |
| 91 var buffer = new StringBuffer(); |
| 92 // \r moves back to the beginnig of the current line. |
| 93 buffer.add('\r${_timeString(duration)} '); |
| 94 buffer.add(_GREEN); |
| 95 buffer.add('+'); |
| 96 buffer.add(passed); |
| 97 buffer.add(_NONE); |
| 98 if (failed != 0) buffer.add(_RED); |
| 99 buffer.add(' -'); |
| 100 buffer.add(failed); |
| 101 if (failed != 0) buffer.add(_NONE); |
| 102 buffer.add(': '); |
| 103 buffer.add(color); |
| 104 |
| 105 int nonVisible = _nonVisiblePrefix + color.length + |
| 106 (failed != 0 ? (_RED.length + _NONE.length) : 0); |
| 107 int len = buffer.length - nonVisible; |
| 108 var mx = MAX_LINE - len; |
| 109 buffer.add(_snippet(message, MAX_LINE - len)); |
| 110 buffer.add(_NONE); |
| 111 |
| 112 // Pad the rest of the line so that it looks erased. |
| 113 len = buffer.length - nonVisible - _NONE.length; |
| 114 if (len > _lastLength) { |
| 115 _lastLength = len; |
| 116 } else { |
| 117 while (len < _lastLength) { |
| 118 buffer.add(' '); |
| 119 _lastLength--; |
| 120 } |
| 121 } |
| 122 stdout.writeString(buffer.toString()); |
| 123 } |
| 124 |
| 125 String _padTime(int time) => |
| 126 (time == 0) ? '00' : ((time < 10) ? '0$time' : '$time'); |
| 127 |
| 128 String _timeString(Duration duration) { |
| 129 var min = duration.inMinutes; |
| 130 var sec = duration.inSeconds % 60; |
| 131 return '${_padTime(min)}:${_padTime(sec)}'; |
| 132 } |
| 133 |
| 134 String _snippet(String text, int maxLength) { |
| 135 // Return the full message if it fits |
| 136 if (text.length <= maxLength) return text; |
| 137 |
| 138 // If we can fit the first and last three words, do so. |
| 139 var words = text.split(' '); |
| 140 if (words.length > 1) { |
| 141 int i = words.length; |
| 142 var len = words.first.length + 4; |
| 143 do { |
| 144 len += 1 + words[--i].length; |
| 145 } while (len <= maxLength && i > 0); |
| 146 if (len > maxLength || i == 0) i++; |
| 147 if (i < words.length - 4) { |
| 148 // Require at least 3 words at the end. |
| 149 var buffer = new StringBuffer(); |
| 150 buffer.add(words.first); |
| 151 buffer.add(' ...'); |
| 152 for (; i < words.length; i++) { |
| 153 buffer.add(' '); |
| 154 buffer.add(words[i]); |
| 155 } |
| 156 return buffer.toString(); |
| 157 } |
| 158 } |
| 159 |
| 160 // Otherwise truncate to return the trailing text, but attempt to start at |
| 161 // the beginning of a word. |
| 162 var res = text.substring(text.length - maxLength + 4); |
| 163 var firstSpace = res.indexOf(' '); |
| 164 if (firstSpace > 0) { |
| 165 res = res.substring(firstSpace); |
| 166 } |
| 167 return '...$res'; |
| 168 } |
| 169 } |
| 170 |
| 171 void useCompactVMConfiguration() { |
| 172 configure(new CompactVMConfiguration()); |
| 173 } |
OLD | NEW |