OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2016, 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.md file. |
| 4 |
| 5 library testing.log; |
| 6 |
| 7 import 'chain.dart' show |
| 8 Result, |
| 9 Step; |
| 10 |
| 11 import 'suite.dart' show |
| 12 Suite; |
| 13 |
| 14 import 'test_description.dart' show |
| 15 TestDescription; |
| 16 |
| 17 import 'test_dart/status_file_parser.dart' show |
| 18 Expectation; |
| 19 |
| 20 /// ANSI escape code for moving cursor one line up. |
| 21 /// See [CSI codes](https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_codes). |
| 22 const String cursorUp = "\u001b[1A"; |
| 23 |
| 24 /// ANSI escape code for erasing the entire line. |
| 25 /// See [CSI codes](https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_codes). |
| 26 const String eraseLine = "\u001b[2K"; |
| 27 |
| 28 final Stopwatch wallclock = new Stopwatch()..start(); |
| 29 |
| 30 bool _isVerbose = const bool.fromEnvironment("verbose"); |
| 31 |
| 32 bool get isVerbose => _isVerbose; |
| 33 |
| 34 void enableVerboseOutput() { |
| 35 _isVerbose = true; |
| 36 } |
| 37 |
| 38 void logTestComplete(int completed, int failed, int total, |
| 39 Suite suite, TestDescription description) { |
| 40 String message = formatProgress(completed, failed, total); |
| 41 if (suite != null) { |
| 42 message += ": ${formatTestDescription(suite, description)}"; |
| 43 } |
| 44 logProgress(message); |
| 45 } |
| 46 |
| 47 void logStepStart(int completed, int failed, int total, |
| 48 Suite suite, TestDescription description, Step step) { |
| 49 String message = formatProgress(completed, failed, total); |
| 50 if (suite != null) { |
| 51 message += ": ${formatTestDescription(suite, description)} ${step.name}"; |
| 52 if (step.isAsync) { |
| 53 message += "..."; |
| 54 } |
| 55 } |
| 56 logProgress(message); |
| 57 } |
| 58 |
| 59 void logStepComplete(int completed, int failed, int total, |
| 60 Suite suite, TestDescription description, Step step) { |
| 61 if (!step.isAsync) return; |
| 62 String message = formatProgress(completed, failed, total); |
| 63 if (suite != null) { |
| 64 message += ": ${formatTestDescription(suite, description)} ${step.name}!"; |
| 65 } |
| 66 logProgress(message); |
| 67 } |
| 68 |
| 69 void logProgress(String message) { |
| 70 if (isVerbose) { |
| 71 print(message); |
| 72 } else { |
| 73 print("$eraseLine$message$cursorUp"); |
| 74 } |
| 75 } |
| 76 |
| 77 String formatProgress(int completed, int failed, int total) { |
| 78 Duration elapsed = wallclock.elapsed; |
| 79 String percent = pad((completed / total * 100.0).toStringAsFixed(1), 5); |
| 80 String good = pad(completed, 5); |
| 81 String bad = pad(failed, 5); |
| 82 String minutes = pad(elapsed.inMinutes, 2, filler: "0"); |
| 83 String seconds = pad(elapsed.inSeconds % 60, 2, filler: "0"); |
| 84 return "[ $minutes:$seconds | $percent% | +$good | -$bad ]"; |
| 85 } |
| 86 |
| 87 String formatTestDescription(Suite suite, TestDescription description) { |
| 88 return "${suite.name}/${description.shortName}"; |
| 89 } |
| 90 |
| 91 void logMessage(Object message) { |
| 92 if (isVerbose) { |
| 93 print("$message"); |
| 94 } |
| 95 } |
| 96 |
| 97 void logNumberedLines(String text) { |
| 98 if (isVerbose) { |
| 99 print(numberedLines(text)); |
| 100 } |
| 101 } |
| 102 |
| 103 void logUnexpectedResult(Suite suite, TestDescription description, |
| 104 Result result, Set<Expectation> expectedOutcomes) { |
| 105 print("${eraseLine}UNEXPECTED: ${suite.name}/${description.shortName}"); |
| 106 Uri statusFile = suite.statusFile; |
| 107 if (statusFile != null) { |
| 108 String path = statusFile.toFilePath(); |
| 109 if (result.outcome == Expectation.PASS) { |
| 110 print("The test unexpectedly passed, please update $path."); |
| 111 } else { |
| 112 print("The test had the outcome ${result.outcome}, but the status file " |
| 113 "($path) allows these outcomes: ${expectedOutcomes.join(' ')}"); |
| 114 } |
| 115 } |
| 116 String log = result.log; |
| 117 if (log.isNotEmpty) { |
| 118 print(log); |
| 119 } |
| 120 if (result.error != null) { |
| 121 print(result.error); |
| 122 if (result.trace != null) { |
| 123 print(result.trace); |
| 124 } |
| 125 } |
| 126 } |
| 127 |
| 128 void logSuiteComplete() { |
| 129 if (!isVerbose) { |
| 130 print(""); |
| 131 } |
| 132 } |
| 133 |
| 134 void logUncaughtError(error, StackTrace stackTrace) { |
| 135 logMessage(error); |
| 136 if (stackTrace != null) { |
| 137 logMessage(stackTrace); |
| 138 } |
| 139 } |
| 140 |
| 141 String pad(Object o, int pad, {String filler: " "}) { |
| 142 String result = (filler * pad) + "$o"; |
| 143 return result.substring(result.length - pad); |
| 144 } |
| 145 |
| 146 String numberedLines(String text) { |
| 147 StringBuffer result = new StringBuffer(); |
| 148 int lineNumber = 1; |
| 149 List<String> lines = splitLines(text); |
| 150 int pad = "${lines.length}".length; |
| 151 String fill = " " * pad; |
| 152 for (String line in lines) { |
| 153 String paddedLineNumber = "$fill$lineNumber"; |
| 154 paddedLineNumber = |
| 155 paddedLineNumber.substring(paddedLineNumber.length - pad); |
| 156 result.write("$paddedLineNumber: $line"); |
| 157 lineNumber++; |
| 158 } |
| 159 return '$result'; |
| 160 } |
| 161 |
| 162 List<String> splitLines(String text) { |
| 163 return text.split(new RegExp('^', multiLine: true)); |
| 164 } |
OLD | NEW |