Index: pkg/unittest/lib/compact_vm_config.dart |
diff --git a/pkg/unittest/lib/compact_vm_config.dart b/pkg/unittest/lib/compact_vm_config.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..55831564456def06989ac71b31a601c3e4326060 |
--- /dev/null |
+++ b/pkg/unittest/lib/compact_vm_config.dart |
@@ -0,0 +1,173 @@ |
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+/** |
+ * A test configuration that generates a compact 1-line progress bar. The bar is |
+ * updated in-place before and after each test is executed. If all test pass, |
+ * you should only see a couple lines in the terminal. If a test fails, the |
+ * failure is shown and the progress bar continues to be updated below it. |
+ */ |
+library compact_vm_config; |
+ |
+import 'dart:io'; |
+import 'package:unittest/unittest.dart'; |
+ |
+const String _GREEN = '\u001b[32m'; |
+const String _RED = '\u001b[31m'; |
+const String _NONE = '\u001b[0m'; |
+const int MAX_LINE = 80; |
+ |
+class CompactVMConfiguration extends Configuration { |
+ Date _start; |
+ int _pass = 0; |
+ int _fail = 0; |
+ |
+ void onInit() { |
+ super.onInit(); |
+ } |
+ |
+ void onStart() { |
+ super.onStart(); |
+ _start = new Date.now(); |
+ } |
+ |
+ void onTestStart(TestCase test) { |
+ super.onTestStart(test); |
+ _progressLine(_start, _pass, _fail, test.description); |
+ } |
+ |
+ void onTestResult(TestCase test) { |
+ super.onTestResult(test); |
+ if (test.result == PASS) { |
+ _pass++; |
+ _progressLine(_start, _pass, _fail, test.description); |
+ } else { |
+ _fail++; |
+ _progressLine(_start, _pass, _fail, test.description); |
+ print(''); |
+ if (test.message != '') { |
+ print(_indent(test.message)); |
+ } |
+ |
+ if (test.stackTrace != null && test.stackTrace != '') { |
+ print(_indent(test.stackTrace)); |
+ } |
+ } |
+ } |
+ |
+ String _indent(String str) { |
+ return str.split("\n").mappedBy((line) => " $line").join("\n"); |
+ } |
+ |
+ void onDone(int passed, int failed, int errors, List<TestCase> results, |
+ String uncaughtError) { |
+ var success = false; |
+ if (passed == 0 && failed == 0 && errors == 0) { |
+ print('\nNo tests ran.'); |
+ } else if (failed == 0 && errors == 0 && uncaughtError == null) { |
+ _progressLine(_start, _pass, _fail, 'All tests pass', _GREEN); |
+ print('\nAll $passed tests passed.'); |
+ success = true; |
+ } else { |
+ _progressLine(_start, _pass, _fail, 'Some tests fail', _RED); |
+ print(''); |
+ if (uncaughtError != null) { |
+ print('Top-level uncaught error: $uncaughtError'); |
+ } |
+ print('$passed PASSED, $failed FAILED, $errors ERRORS'); |
+ } |
+ |
+ if (!success) exit(1); |
+ } |
+ |
+ int _lastLength = 0; |
+ |
+ final int _nonVisiblePrefix = 1 + _GREEN.length + _NONE.length; |
+ |
+ void _progressLine(Date startTime, int passed, int failed, String message, |
+ [String color = _NONE]) { |
+ var duration = (new Date.now()).difference(startTime); |
+ var buffer = new StringBuffer(); |
+ // \r moves back to the beginnig of the current line. |
+ buffer.add('\r${_timeString(duration)} '); |
+ buffer.add(_GREEN); |
+ buffer.add('+'); |
+ buffer.add(passed); |
+ buffer.add(_NONE); |
+ if (failed != 0) buffer.add(_RED); |
+ buffer.add(' -'); |
+ buffer.add(failed); |
+ if (failed != 0) buffer.add(_NONE); |
+ buffer.add(': '); |
+ buffer.add(color); |
+ |
+ int nonVisible = _nonVisiblePrefix + color.length + |
+ (failed != 0 ? (_RED.length + _NONE.length) : 0); |
+ int len = buffer.length - nonVisible; |
+ var mx = MAX_LINE - len; |
+ buffer.add(_snippet(message, MAX_LINE - len)); |
+ buffer.add(_NONE); |
+ |
+ // Pad the rest of the line so that it looks erased. |
+ len = buffer.length - nonVisible - _NONE.length; |
+ if (len > _lastLength) { |
+ _lastLength = len; |
+ } else { |
+ while (len < _lastLength) { |
+ buffer.add(' '); |
+ _lastLength--; |
+ } |
+ } |
+ stdout.writeString(buffer.toString()); |
+ } |
+ |
+ String _padTime(int time) => |
+ (time == 0) ? '00' : ((time < 10) ? '0$time' : '$time'); |
+ |
+ String _timeString(Duration duration) { |
+ var min = duration.inMinutes; |
+ var sec = duration.inSeconds % 60; |
+ return '${_padTime(min)}:${_padTime(sec)}'; |
+ } |
+ |
+ String _snippet(String text, int maxLength) { |
+ // Return the full message if it fits |
+ if (text.length <= maxLength) return text; |
+ |
+ // If we can fit the first and last three words, do so. |
+ var words = text.split(' '); |
+ if (words.length > 1) { |
+ int i = words.length; |
+ var len = words.first.length + 4; |
+ do { |
+ len += 1 + words[--i].length; |
+ } while (len <= maxLength && i > 0); |
+ if (len > maxLength || i == 0) i++; |
+ if (i < words.length - 4) { |
+ // Require at least 3 words at the end. |
+ var buffer = new StringBuffer(); |
+ buffer.add(words.first); |
+ buffer.add(' ...'); |
+ for (; i < words.length; i++) { |
+ buffer.add(' '); |
+ buffer.add(words[i]); |
+ } |
+ return buffer.toString(); |
+ } |
+ } |
+ |
+ // Otherwise truncate to return the trailing text, but attempt to start at |
+ // the beginning of a word. |
+ var res = text.substring(text.length - maxLength + 4); |
+ var firstSpace = res.indexOf(' '); |
+ if (firstSpace > 0) { |
+ res = res.substring(firstSpace); |
+ } |
+ return '...$res'; |
+ } |
+} |
+ |
+void useCompactVMConfiguration() { |
+ configure(new CompactVMConfiguration()); |
+} |